问题描述
我的 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;
}
}
}