Specflow 应用程序在当前步骤的处理完成之前移至下一步

问题描述

我的 Specflow 应用中有这一步:

[Given(@"Messages are streaming to MF application")]
public void GivenMessagesAreStreamingToMFApplication()
{
    var MessageInjectorOptions = new MessageInjectorOptions();
    _configuration.GetSection("JavaMessageInjector").Bind(MessageInjectorOptions);
    _logger.Loginformation("MessageInjector Command: " + MessageInjectorOptions.MessageInjectorFilename + " " + MessageInjectorOptions.MessageInjectorParameters);
    int applicationStatus = TestHelper.ExecuteApplication(MessageInjectorOptions.MessageInjectorFilename,MessageInjectorOptions.MessageInjectorParameters);
    _logger.Loginformation("MessageInjector Exit Code: " + applicationStatus);
}

这是我的 TestHelper 课:

public class TestHelper
{
    public static int ExecuteApplication(string filename,string arguments="")
    {
        var processInfo = new processstartinfo()
        {
            CreateNowindow = true,UseShellExecute = false,FileName = filename,WindowStyle = ProcessWindowStyle.Hidden,Arguments = arguments
        };
        Process proc;

        if ((proc = Process.Start(processInfo)) == null)
        {
            throw new InvalidOperationException("Message injector Failed to run");
        }


        proc.WaitForExit();
        int exitCode = proc.ExitCode;
        proc.Close();
        return exitCode;
    }
}

Java 应用程序在独立运行时通常需要几分钟才能执行,但是当我使用 dotnet test --logger:"console;verbosity=detailed;" 运行我的 Specflow 应用程序时,该步骤在 0.3 秒内完成,似乎没有问题。下一步失败,因为它依赖于成功完成的上一步。

标准输出消息: 给定的 MD 消息正在流式传输到 MF 应用程序 test_tool.Steps.IntegrationTestStepDeFinitions [0] MessageInjector 命令:C:\ProgramData\ME\Java_SymLinks\JDK8_x64\jre\bin\java.exe -jar .\Jar\Injector\Injector-6.0.jar MY.TOPIC -> 完成:IntegrationTestStepDeFinitions.GivenMessagesAreStreamingToMFApplication() (0.3s)

我认为 proc.WaitForExit() 会在继续之前确保应用程序完成,但事实并非如此。在继续之前,我如何确保应用程序已完成运行?

解决方法

我将我的 TestHelper 更改为:

public class TestHelper
    {
        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
                {
                    isStarted = process.Start();
                }
                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;
        }


        private static Task<bool> WaitForExitAsync(Process process,int timeout)
        {
            return Task.Run(() => process.WaitForExit(timeout));
        }


        public struct ProcessResult
        {
            public bool Completed;
            public int? ExitCode;
            public string Output;
        }
    }
}