如何使用xUnit测试带命令行参数的C#控制台应用
问题描述
我有一个C#控制台应用,功能为打开文件、查找特定行并编辑。该程序支持三种运行方式:
- 无参数运行时修改本地
version.h文件; - 通过
--path参数指定待修改文件路径; - 通过
help参数获取帮助。
程序代码如下:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using static System.Net.Mime.MediaTypeNames; namespace BuildIncrement { public class Program { static void Main(string[] args) { Console.WriteLine(args.Length); string filePath = "version.h"; if (args.Length > 0) { if (args[0].Contains("path")) { var argsArray = args[0].Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries); if (argsArray.Length > 1) { filePath = argsArray[1]; } } else if (args[0].Contains("help")) { Console.WriteLine("To run the Program please type --path"); return; } } if (File.Exists(filePath) == false) { Console.WriteLine("Fehler: Die Datei existiert nicht!"); return; } List<string> lines = File.ReadAllLines(filePath).ToList(); string line = lines.Where(i => i.Contains("BUILD_NUM")).FirstOrDefault(); int buildNr = 0; if (line != null) { string[] lineparts = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (lineparts.Length > 2) { string Nr = lineparts[2]; if (int.TryParse(Nr, out buildNr)) { } buildNr++; int index = lines.IndexOf(line); lines.RemoveAt(index); lines.Insert(index, line.Replace(Nr, buildNr.ToString())); } } File.WriteAllLines(filePath, lines); Console.WriteLine($"Build-Nr wurde auf {buildNr} aktualisiert!"); } } }
请问如何使用xUnit测试这款带有命令行参数的C#控制台应用?
使用xUnit测试该控制台应用的步骤
1. 重构原代码,提升可测试性
原代码逻辑全部耦合在Main方法中,无法单独测试模块功能。先拆分核心逻辑到独立方法:
namespace BuildIncrement { public class Program { // 暴露给测试的入口方法 public static void Run(string[] args) { Main(args); } static void Main(string[] args) { var (filePath, shouldShowHelp) = ParseArgs(args); if (shouldShowHelp) { Console.WriteLine("To run the Program please type --path"); return; } if (!File.Exists(filePath)) { Console.WriteLine("Fehler: Die Datei existiert nicht!"); return; } var lines = File.ReadAllLines(filePath).ToList(); var (updatedLines, newBuildNumber) = UpdateBuildNumber(lines); File.WriteAllLines(filePath, updatedLines); Console.WriteLine($"Build-Nr wurde auf {newBuildNumber} aktualisiert!"); } // 独立的参数解析逻辑 public static (string FilePath, bool ShowHelp) ParseArgs(string[] args) { string filePath = "version.h"; bool showHelp = false; if (args.Length > 0) { if (args[0].Contains("path")) { var argsArray = args[0].Split('=', StringSplitOptions.RemoveEmptyEntries); if (argsArray.Length > 1) { filePath = argsArray[1]; } } else if (args[0].Contains("help")) { showHelp = true; } } return (filePath, showHelp); } // 独立的版本号更新逻辑 public static (List<string> UpdatedLines, int NewBuildNumber) UpdateBuildNumber(List<string> lines) { int buildNr = 0; string buildLine = lines.FirstOrDefault(l => l.Contains("BUILD_NUM")); if (buildLine != null) { var lineParts = buildLine.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (lineParts.Length > 2 && int.TryParse(lineParts[2], out buildNr)) { buildNr++; int index = lines.IndexOf(buildLine); lines.RemoveAt(index); lines.Insert(index, buildLine.Replace(lineParts[2], buildNr.ToString())); } } return (lines, buildNr); } } }
2. 创建xUnit测试项目
在解决方案中添加xUnit测试项目(比如命名为BuildIncrement.Tests),并引用原控制台应用项目。
3. 编写测试用例
测试时使用临时文件避免影响真实文件,同时捕获控制台输出验证结果。
控制台输出捕获工具类
using System; using System.IO; namespace BuildIncrement.Tests { public class ConsoleOutputHelper : IDisposable { private readonly StringWriter _stringWriter; private readonly TextWriter _originalOutput; public ConsoleOutputHelper() { _stringWriter = new StringWriter(); _originalOutput = Console.Out; Console.SetOut(_stringWriter); } public string GetOutput() => _stringWriter.ToString().Trim(); public void Dispose() { Console.SetOut(_originalOutput); _stringWriter.Dispose(); } } }
具体测试用例
using System; using System.Collections.Generic; using System.IO; using Xunit; namespace BuildIncrement.Tests { public class ProgramTests { // 测试无参数时的默认路径解析 [Fact] public void ParseArgs_WithoutArgs_ReturnsDefaultPath() { var result = Program.ParseArgs(Array.Empty<string>()); Assert.Equal("version.h", result.FilePath); Assert.False(result.ShowHelp); } // 测试--path参数的路径解析 [Fact] public void ParseArgs_WithPathArg_ReturnsSpecifiedPath() { var result = Program.ParseArgs(new[] { "--path=C:\\test\\version.h" }); Assert.Equal("C:\\test\\version.h", result.FilePath); Assert.False(result.ShowHelp); } // 测试help参数的解析 [Fact] public void ParseArgs_WithHelpArg_ReturnsShowHelpTrue() { var result = Program.ParseArgs(new[] { "help" }); Assert.Equal("version.h", result.FilePath); Assert.True(result.ShowHelp); } // 测试版本号正常递增逻辑 [Fact] public void UpdateBuildNumber_WithValidBuildLine_IncrementsNumber() { var lines = new List<string> { "#define MAJOR_VERSION 1", "#define BUILD_NUM 5", "#define MINOR_VERSION 0" }; var (updatedLines, newBuildNr) = Program.UpdateBuildNumber(lines); var updatedBuildLine = updatedLines.Find(l => l.Contains("BUILD_NUM")); Assert.Equal(6, newBuildNr); Assert.Equal("#define BUILD_NUM 6", updatedBuildLine); } // 测试文件不存在的错误输出 [Fact] public void Run_WithNonExistentFile_OutputsError() { using var consoleHelper = new ConsoleOutputHelper(); Program.Run(new[] { "--path=non_existent_file.h" }); var output = consoleHelper.GetOutput(); Assert.Contains("Fehler: Die Datei existiert nicht!", output); } // 测试help参数的帮助输出 [Fact] public void Run_WithHelpArg_OutputsHelpMessage() { using var consoleHelper = new ConsoleOutputHelper(); Program.Run(new[] { "help" }); var output = consoleHelper.GetOutput(); Assert.Contains("To run the Program please type --path", output); } // 测试无参数完整流程(修改临时文件) [Fact] public void Run_WithoutArgs_UpdatesDefaultTempFile() { // 创建临时文件 string tempDir = Path.GetTempPath(); string tempFile = Path.Combine(tempDir, "version.h"); File.WriteAllLines(tempFile, new[] { "#define BUILD_NUM 10" }); // 切换到临时目录模拟默认路径 string originalDir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(tempDir); try { using var consoleHelper = new ConsoleOutputHelper(); Program.Run(Array.Empty<string>()); // 验证结果 var lines = File.ReadAllLines(tempFile); Assert.Equal("#define BUILD_NUM 11", lines[0]); Assert.Contains("Build-Nr wurde auf 11 aktualisiert!", consoleHelper.GetOutput()); } finally { Directory.SetCurrentDirectory(originalDir); File.Delete(tempFile); } } } }
4. 运行测试
在Visual Studio或通过命令行执行dotnet test命令,验证所有测试用例是否通过。
内容的提问来源于stack exchange,提问作者HussamOthman
相关产品推荐
相关产品推荐

