You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:20:30