如何在.NET Core 3.1的C#控制台程序中输入stop立即终止NUnit测试?
解决NUnit测试即时终止的问题
你的问题在于之前的代码是在测试执行完成后才会触发Console.ReadLine()——主线程被测试运行完全阻塞了,根本没机会实时监听输入。要实现输入"stop"就立即终止测试,需要单独开一个线程监听控制台输入,同时还要能控制NUnit的测试执行流程,而不是等测试跑完再杀进程。下面给你两种可行的方案:
方案一:使用NUnit Engine API(推荐,优雅终止)
这种方法直接通过NUnit的编程式API运行测试,让你可以主动调用停止方法,不需要粗暴杀死进程。
步骤:
先安装NUnit Engine的NuGet包:
Install-Package NUnit.Engine Install-Package NUnit.Engine.Api注意选择和.NET Core 3.1兼容的版本(比如3.13.x系列)。
编写代码,后台线程监听输入,收到stop时终止测试:
using System; using System.Threading; using NUnit.Engine; namespace TestRunnerApp { class Program { static ITestRunner _testRunner; static void Main(string[] args) { // 启动后台线程监听控制台输入 var inputListenerThread = new Thread(ListenForStopCommand); inputListenerThread.IsBackground = true; inputListenerThread.Start(); // 初始化NUnit测试引擎 var testEngine = TestEngineActivator.CreateInstance(); var testPackage = new TestPackage(@"你的测试项目.dll路径"); // 启动测试 _testRunner = testEngine.GetRunner(testPackage); var testResult = _testRunner.Run(null); // null使用默认的测试结果输出 // 测试结束后清理资源 _testRunner.Dispose(); testEngine.Dispose(); } static void ListenForStopCommand() { while (true) { var input = Console.ReadLine(); if (input?.Trim().Equals("stop", StringComparison.OrdinalIgnoreCase) == true) { Console.WriteLine("收到停止命令,正在终止测试..."); // 立即终止测试,true表示不等待当前测试完成 _testRunner?.StopRun(true); break; } } } } }
说明:
- 后台线程会一直监听控制台输入,不会阻塞主线程的测试运行。
StopRun(true)方法会立即终止所有正在运行的测试,而不是等它们完成,完美满足你的即时终止需求。- 这种方式属于优雅终止,不会像
Kill()那样直接杀死整个进程,能保证测试引擎做必要的资源清理。
方案二:外部进程运行dotnet test(适合必须用命令行的场景)
如果你坚持要通过dotnet test命令启动测试,那需要找到测试对应的子进程并杀死它(因为dotnet test会启动testhost子进程来实际运行测试)。
步骤:
安装
System.ManagementNuGet包,用来获取进程的子进程:Install-Package System.Management编写代码,启动
dotnet test进程并监听输入:using System; using System.Diagnostics; using System.Management; using System.Threading; namespace TestRunnerApp { class Program { static Process _dotnetTestProcess; static void Main(string[] args) { // 启动输入监听线程 var inputThread = new Thread(ListenForStopCommand); inputThread.IsBackground = true; inputThread.Start(); // 启动dotnet test进程 _dotnetTestProcess = new Process { StartInfo = new ProcessStartInfo { FileName = "dotnet", Arguments = "test", WorkingDirectory = @"你的测试项目根目录", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true } }; _dotnetTestProcess.Start(); // 实时输出测试日志到控制台 _dotnetTestProcess.OutputDataReceived += (sender, e) => Console.WriteLine(e.Data); _dotnetTestProcess.ErrorDataReceived += (sender, e) => Console.WriteLine(e.Data); _dotnetTestProcess.BeginOutputReadLine(); _dotnetTestProcess.BeginErrorReadLine(); // 等待测试进程结束 _dotnetTestProcess.WaitForExit(); } static void ListenForStopCommand() { while (true) { var input = Console.ReadLine(); if (input?.Trim().Equals("stop", StringComparison.OrdinalIgnoreCase) == true) { Console.WriteLine("收到停止命令,正在终止测试进程..."); KillProcessAndChildren(_dotnetTestProcess.Id); break; } } } // 递归杀死进程及其所有子进程 static void KillProcessAndChildren(int pid) { // 获取所有子进程 using (var searcher = new ManagementObjectSearcher($"SELECT * FROM Win32_Process WHERE ParentProcessID={pid}")) { foreach (var obj in searcher.Get()) { var childPid = Convert.ToInt32(obj["ProcessID"]); KillProcessAndChildren(childPid); } } try { var process = Process.GetProcessById(pid); process.Kill(); } catch (ArgumentException) { // 进程已经结束,忽略异常 } } } }
说明:
- 这个方法会启动
dotnet test作为子进程,同时实时输出测试日志。 - 收到stop命令时,会递归杀死
dotnet test进程及其子进程(也就是实际运行测试的testhost进程),确保测试完全终止。 - 这种方式是强制杀死进程,不如方案一优雅,但适合必须使用
dotnet test命令的场景。
总结
优先选择方案一,因为它能通过NUnit的原生API控制测试流程,实现优雅的即时终止,避免进程被强制杀死带来的潜在问题。如果因为某些原因必须用dotnet test命令,再考虑方案二。
内容的提问来源于stack exchange,提问作者Ayan
相关产品推荐
相关产品推荐

