这个实战项目展示了如何在一个典型的WPF应用程序中实施全面的单元测试。项目采用NUnit 4.1.0作为测试框架,通过Calculator和StringProcessor两个核心业务类的测试案例,系统性地演示了NUnit的各项核心功能。
项目结构采用了标准的解决方案布局,将主应用程序(NUnitDemo)与测试项目(NUnitDemo.Tests)分离。这种分离式结构是.NET生态中的最佳实践,既保持了生产代码的纯净性,又确保了测试代码的独立性。测试项目中包含了四组测试类,分别演示基础测试、参数化测试、高级特性以及集合测试等不同维度的NUnit功能。
提示:虽然项目使用的是NUnit 4.1.0版本,但大部分概念和用法同样适用于NUnit 3.x系列,差异部分我会在相应章节特别说明。
要运行这个项目,你需要准备以下环境:
项目已经配置了必要的NuGet包引用:
让我们深入看一下项目结构的设计思路:
code复制NUnitDemo.sln
├── NUnitDemo/ (主应用程序)
│ ├── Calculator.cs
│ ├── StringProcessor.cs
│ ├── MainWindow.xaml
│ └── (其他WPF标准文件)
└── NUnitDemo.Tests/ (测试项目)
├── CalculatorTests.cs
├── StringProcessorTests.cs
├── AdvancedFeaturesTests.cs
└── CollectionTests.cs
这种结构有以下几个优势:
Calculator.cs实现了一个简单的四则运算器:
csharp复制public class Calculator
{
public double Add(double a, double b) => a + b;
public double Subtract(double a, double b) => a - b;
public double Multiply(double a, double b) => a * b;
public double Divide(double a, double b)
{
if (b == 0) throw new DivideByZeroException();
return a / b;
}
}
对应的CalculatorTests.cs展示了基础测试模式:
csharp复制[TestFixture]
public class CalculatorTests
{
private Calculator _calculator;
[SetUp]
public void Setup()
{
_calculator = new Calculator();
}
[Test]
public void Add_TwoNumbers_ReturnsCorrectSum()
{
var result = _calculator.Add(2.5, 3.5);
Assert.That(result, Is.EqualTo(6));
}
[Test]
public void Divide_ByZero_ThrowsException()
{
Assert.Throws<DivideByZeroException>(() => _calculator.Divide(10, 0));
}
}
这里有几个值得注意的测试模式:
StringProcessor.cs提供了字符串处理功能:
csharp复制public class StringProcessor
{
public string Reverse(string input)
{
if (input == null) return null;
return new string(input.Reverse().ToArray());
}
public bool IsPalindrome(string input)
{
if (string.IsNullOrEmpty(input)) return false;
return input.SequenceEqual(input.Reverse());
}
}
StringProcessorTests.cs展示了参数化测试的强大功能:
csharp复制[TestFixture]
public class StringProcessorTests
{
[TestCase("hello", "olleh")]
[TestCase("", "")]
[TestCase(null, null)]
public void Reverse_WithVariousInputs_ReturnsExpected(string input, string expected)
{
var processor = new StringProcessor();
var result = processor.Reverse(input);
Assert.That(result, Is.EqualTo(expected));
}
[TestCaseSource(nameof(PalindromeTestCases))]
public void IsPalindrome_WithTestCases_ReturnsCorrect(string input, bool expected)
{
var processor = new StringProcessor();
var result = processor.IsPalindrome(input);
Assert.That(result, Is.EqualTo(expected));
}
private static readonly object[] PalindromeTestCases =
{
new object[] { "madam", true },
new object[] { "racecar", true },
new object[] { "hello", false },
new object[] { "", false }
};
}
参数化测试的关键优势:
AdvancedFeaturesTests.cs展示了NUnit的执行控制功能:
csharp复制[TestFixture]
[Category("Advanced")]
public class AdvancedFeaturesTests
{
[Test]
[MaxTime(100)] // 毫秒
public void PerformanceTest_ShouldCompleteInTime()
{
// 模拟耗时操作
Thread.Sleep(50);
}
[Test]
[Ignore("暂时跳过,等待BUG修复")]
public void BrokenTest_CurrentlyIgnored()
{
Assert.Fail("这个测试已知有问题");
}
[Test]
[Repeat(5)]
public void RepeatTest_ShouldPassEveryTime()
{
var randomValue = new Random().Next(1, 10);
Assert.That(randomValue, Is.InRange(1, 10));
}
}
这些特性在实际项目中的应用场景:
CollectionTests.cs展示了集合测试的高级技巧:
csharp复制[TestFixture]
public class CollectionTests
{
[Test]
public void CollectionAssert_UsageExamples()
{
var numbers = new[] { 1, 3, 5, 7, 9 };
// 传统集合断言
CollectionAssert.AllItemsAreUnique(numbers);
CollectionAssert.Contains(numbers, 5);
// 约束模型断言
Assert.That(numbers, Is.All.InstanceOf<int>());
Assert.That(numbers, Is.Ordered.Ascending);
}
[Test]
public void Dictionary_ConstraintExamples()
{
var dict = new Dictionary<string, int>
{
["one"] = 1,
["two"] = 2
};
Assert.That(dict, Contains.Key("one"));
Assert.That(dict, Contains.Value(2));
Assert.That(dict.Count, Is.EqualTo(2));
}
}
NUnit的约束模型优势:
在VS2022中,测试资源管理器提供了丰富的功能:
注意:确保已安装"NUnit3TestAdapter"包,否则测试可能无法被发现。
项目也支持通过命令行执行测试:
bash复制dotnet test NUnitDemo.sln
常用参数:
--filter:按条件筛选测试--logger:控制输出格式--results-directory:指定结果输出位置可能原因及解决方案:
提高覆盖率的策略:
优化建议:
基于这个基础项目,你可以进一步探索:
在实际项目中应用NUnit时,建议建立以下规范:
这个项目虽然不大,但完整展示了NUnit的核心功能。我在实际工作中发现,良好的单元测试实践可以显著提高代码质量和开发效率。特别是在重构时,完善的测试套件能给你足够的信心进行大胆修改。