使用 Specflow

问题描述

我在运行 Specflow 测试时得到这个:

[xUnit.net 00:00:06.30]       System.AggregateException : One or more errors occurred. (Cannot mix synchronous and asynchronous operation on process stream.)
[xUnit.net 00:00:06.30]       ---- System.InvalidOperationException : Cannot mix synchronous and asynchronous operation on process stream.
[xUnit.net 00:00:06.30]       Stack Trace:
[xUnit.net 00:00:06.31]            at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
[xUnit.net 00:00:06.31]            at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
[xUnit.net 00:00:06.31]            at System.Threading.Tasks.Task`1.get_Result()
[xUnit.net 00:00:06.31]         c:\Users\Me\Source\Repos\test_tool\Steps\IntegrationTestStepDefinitions.cs(51,0): at test_tool.Steps.IntegrationTestStepDefinitions.GivenMessagesAreStreamingFromToTopicOnPartition(String binaryFile,String topic,Int32 partition)
[xUnit.net 00:00:06.31]            at TechTalk.SpecFlow.Bindings.BindingInvoker.InvokeBinding(IBinding binding,IContextManager contextManager,Object[] arguments,ITestTracer testTracer,TimeSpan& duration)
[xUnit.net 00:00:06.31]            at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match,TimeSpan& duration)
[xUnit.net 00:00:06.31]            at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(IContextManager contextManager,StepInstance stepInstance)
[xUnit.net 00:00:06.31]            at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep()
[xUnit.net 00:00:06.31]            at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors()
[xUnit.net 00:00:06.31]            at test_tool.Features.IntegrationTestFeature.ScenarioCleanup()

这是步骤(第 43-53 行):

        [Given(@"messages are streaming from ""(.*)"" to topic ""(.*)"" on partition (.*)")]
        public void GivenMessagesAreStreamingFromToTopicOnPartition(string binaryFile,string topic,int partition)
        {
            var MessageInjectorOptions = new MessageInjectorOptions();
            _configuration.GetSection("JavaMessageInjector").Bind(MessageInjectorOptions);
            _logger.LogInformation("MessageInjector Command: " + MessageInjectorOptions.MessageInjectorFilename + " " + MessageInjectorOptions.MessageInjectorParameters + " " + binaryFile + " " + topic + " " + partition);
            _output.WriteLine("Console MessageInjector Command: " + MessageInjectorOptions.MessageInjectorFilename + " " + MessageInjectorOptions.MessageInjectorParameters + " " + binaryFile + " " + topic + " " + partition);
            var result = TestHelper.ExecuteShellCommand(MessageInjectorOptions.MessageInjectorFilename,MessageInjectorOptions.MessageInjectorParameters + " " + binaryFile + " " + topic + " " + partition,300000,true);
            _logger.LogInformation("Output: " + result.Result.Output);
            _logger.LogInformation("MessageInjector Exit Code: " + result.Result.ExitCode + ",Completed: " + result.Result.Completed + " Output: " + result.Result.Output);
        }

这是我的ExecuteShellCommand

public static async Task<ProcessResult> ExecuteShellCommand(string command,string arguments="",int timeout=1000,bool insertWait=false)
{
    var result = new ProcessResult();

    using (var process = new Process())
    {
        process.StartInfo.FileName = command;
        process.StartInfo.Arguments = arguments;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardInput = true;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.CreateNoWindow = true;

        var outputBuilder = new StringBuilder();
        var outputCloseEvent = new TaskCompletionSource<bool>();

        process.OutputDataReceived += (s,e) =>
        {
            // The output stream has been closed i.e. the process has terminated
            if (e.Data == null)
            {
                outputCloseEvent.SetResult(true);
            }
            else
            {
                outputBuilder.AppendLine(e.Data);
            }
        };

        var errorBuilder = new StringBuilder();
        var errorCloseEvent = new TaskCompletionSource<bool>();

        process.ErrorDataReceived += (s,e) =>
        {
            // The error stream has been closed i.e. the process has terminated
            if (e.Data == null)
            {
                errorCloseEvent.SetResult(true);
            }
            else
            {
                errorBuilder.AppendLine(e.Data);
            }
        };

        bool isStarted;

        try
        {
            process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
            process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
            isStarted = process.Start(); 
            StreamReader reader = process.StandardOutput;
            string output = reader.ReadToEnd();
            result.Output = output;
        }
        catch (Exception error)
        {
            // Usually it occurs when an executable file is not found or is not executable

            result.Completed = true;
            result.ExitCode = -1;
            result.Output = error.Message;

            isStarted = false;
        }

        if (isStarted)
        {
            // Reads the output stream first and then waits because deadlocks are possible
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            if (insertWait)
            {
                await Task.Delay(150000);
            }

            // Creates task to wait for process exit using timeout
            var waitForExit = WaitForExitAsync(process,timeout);

            // Create task to wait for process exit and closing all output streams
            var processTask = Task.WhenAll(waitForExit,outputCloseEvent.Task,errorCloseEvent.Task);

            // Waits process completion and then checks it was not completed by timeout
            if (await Task.WhenAny(Task.Delay(timeout),processTask) == processTask && waitForExit.Result)
            {
                result.Completed = true;
                result.ExitCode = process.ExitCode;

                // Adds process output if it was completed with error
                if (process.ExitCode != 0)
                {
                    result.Output = $"{outputBuilder}{errorBuilder}";
                }
            }
            else
            {
                try
                {
                    // Kill hung process
                    process.Kill();
                }
                catch
                {
                }
            }
        }
    }

    return result;
}

我不明白错误是关于什么的。我认为它来自 TestHelper 中的错误。是我必须等待它返回还是我应该如何解决这个问题?

解决方法

您不能从同步步骤定义中调用 async 方法。您需要改用 Asynchronous Bindings

  1. 将步骤定义方法签名更改为异步并返回一个任务:

    [Given(@"messages are streaming from ""(.*)"" to topic ""(.*)"" on partition (.*)")]
    public async Task GivenMessagesAreStreamingFromToTopicOnPartition(string binaryFile,string topic,int partition)
    {
        // ...
    }
    
  2. 调用 await 时使用 TestHelper.ExecuteShellCommand(...)

    var result = await TestHelper.ExecuteShellCommand(MessageInjectorOptions.MessageInjectorFilename,MessageInjectorOptions.MessageInjectorParameters + " " + binaryFile + " " + topic + " " + partition,300000,true);
    
    _logger.LogInformation("Output: " + result.Output);
    _logger.LogInformation("MessageInjector Exit Code: " + result.ExitCode + ",Completed: " + result.Completed + " Output: " + result.Output);
    

    务必将所有出现的 result.Result.X 替换为仅 result.X,因为 result 变量现在将是 ProcessResult 对象而不是 Task<ProcessResult>

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...