【问题标题】:Pass parameter to Specflow BeforeScenario method将参数传递给 Specflow BeforeScenario 方法
【发布时间】:2021-08-13 14:19:55
【问题描述】:

我有一个 Specflow BDD 测试,我在其中检查进程是否正在运行,如果是,则终止它。

这目前发生在以下步骤之一中:

[Given(@"Application ""(.*)"" application runs version ""(.*)""")]
public void WhenApplicationRunsVersion(string publisher, string version)
{
    var ApplicationOptions = new ApplicationOptions();
    _configuration.GetSection("Application").Bind(ApplicationOptions);
    var processRunning = TestHelper.ProcessRunning(publisher);
    if (processRunning)
    {
        TestHelper.KillProcess(publisher);
    }
    var result = TestHelper.ExecuteShellCommand(ApplicationOptions.FilePath.ToString() + version + "\\",
                                                ApplicationOptions.Filename.ToString(),
                                                timeout: 150000);
    processRunning = TestHelper.ProcessRunning(publisher);
    processRunning.Should().BeTrue();
}

我想把它拿出来并在BeforeScenario 方法中运行。我在我的 Hooks.cs 文件中创建了这个

[Binding]
public class Hooks
{
    [BeforeScenario]
    public static void BeforeScenario(ScenarioContext scenarioContext)
    {
        TestHelper.KillProcess(scenarioContext);
    }
}

这是我的测试助手:

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

        using (var process = new Process())
        {
            process.StartInfo.FileName = path + 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;
            process.StartInfo.WorkingDirectory = Path.GetDirectoryName(path);

            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)
                {

                    StreamReader reader = process.StandardOutput;
                    string output = reader.ReadToEnd();
                    result.Output = output;

                    // 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;
    }

    public static void KillProcess(string processName)
    {
        foreach(Process process in Process.GetProcessesByName(processName))
        {
            process.Kill();
        }
    }

    public static bool ProcessRunning(string processName)
    {
        Process[] pname = Process.GetProcessesByName(processName);
        if (pname.Length == 0)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
}

我的问题是我需要进程名称才能运行TestHelper.KillProcess()。如何将它传递给我的 BeforeScenario() 方法?

【问题讨论】:

  • 为什么要将其移至BeforeScenario 方法?您希望获得什么?
  • 您也许可以使用标签来实现这一点。见stackoverflow.com/q/59876532/261050
  • 我先到了 BeforeScenario。这不是必需的。接下来,我要添加类似于 AfterScenario 的内容。我要添加的内容当前有一个场景步骤,因此如果在它失败之前的步骤将不会执行,所以我想移动它以确保它始终运行,所以我仍然会有这个查询。

标签: c# .net specflow


【解决方案1】:

这个逻辑在TestHelper 类中可能更容易处理。 ExecuteShellCommand 方法最适合杀死当前正在运行的进程并启动一个新进程。

此方法可以将进程名称或 ID 分配给静态字段,因此当您尝试再次运行相同的命令时,它会终止当前正在运行的进程。此外,抛出进程异常的最佳位置没有正确启动。在我给你一个代码示例之前,你需要在你的 TestHelper 类中发布代码。

感觉好像 SpecFlow 钩子是处理这个问题的错误地方。 TestHelper 似乎是更好的地方。

【讨论】:

  • 感谢@Greg Burghardt。我将 TestHelper 类添加到我的问题中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 2017-01-05
  • 2013-10-04
  • 2013-05-19
  • 2013-02-08
相关资源
最近更新 更多