当前位置:首页 > 技能相关 > C#与C++ > 正文内容

C#进阶 一、NUnit 单元测试详解

admin1个月前 (01-22)C#与C++870 修订时间:2026-01-22 11:30:29

1. 引用和安装

通过 NuGet 包管理器安装:

# 安装 NUnit 框架
Install-Package NUnit
# 安装 NUnit SDK 框架
Microsoft.NET.Test.Sdk
# 安装 NUnit 测试运行器
Install-Package NUnit3TestAdapter
# 安装断言扩展(可选)
Install-Package NUnit.Analyzers

2. 测试类定义

using NUnit.Framework;
[TestFixture]  // 标识测试类,版本2.5以后已省略
public class CalculatorTests
{
    private Calculator _calculator;
    
    [SetUp]  // 每个测试方法前执行
    public void SetUp()
    {
        _calculator = new Calculator();
    }
    
    [TearDown]  // 每个测试方法后执行
    public void TearDown()
    {
        _calculator.Dispose();
    }
}

3. 常用测试属性

测试方法属性:

[Test]  // 基本测试方法
public void Add_TwoNumbers_ReturnsSum()
{
    // 测试代码
}

参数化测试

[TestCase(2, 3, 5)]  // 参数化测试
[TestCase(0, 0, 0)]
[TestCase(-1, 1, 0)]
public void Add_TwoNumbers_ReturnsCorrectSum(int a, int b, int expected)
{
    var result = _calculator.Add(a, b);
    Assert.AreEqual(expected, result);
}

测试控制属性:

[Ignore("暂时跳过,等待修复")]  // 跳过测试
[Test]
public void DeprecatedTest() { }

设置超时时间(毫秒)

[MaxTime(100)]  // 设置超时时间(毫秒)
[Test]
public void PerformanceTest() { }

失败时重试次数

[Retry(3)]  // 失败时重试次数
[Test]
public void FlakyTest() { }

4. 常用断言方法

基本断言:

// 相等性断言
Assert.AreEqual(expected, actual);
Assert.AreNotEqual(notExpected, actual);
// 布尔断言
Assert.IsTrue(condition);
Assert.IsFalse(condition);
// 空值断言
Assert.IsNull(obj);
Assert.IsNotNull(obj);
集合断言:
var collection = new List<int> { 1, 2, 3 };
Assert.That(collection, Is.Not.Empty);
Assert.That(collection, Has.Count.EqualTo(3));
Assert.That(collection, Contains.Item(2));
Assert.That(collection, Is.Ordered);  // 检查是否有序
约束模型(Constraint Model):
// 现代 NUnit 推荐的语法
Assert.That(actual, Is.EqualTo(expected));
Assert.That(actual, Is.Not.Null);
Assert.That(actual, Is.GreaterThan(0));
Assert.That(actual, Is.InRange(1, 10));
Assert.That(actual, Is.TypeOf<string>());

异常断言:

方法一:使用 Assert.Throws

var ex = Assert.Throws<ArgumentNullException>(() => 
    _calculator.Divide(5, 0));
Assert.That(ex.ParamName, Is.EqualTo("divisor"));

方法二:使用 Assert.That

Assert.That(() => _calculator.Divide(5, 0),
    Throws.TypeOf<DivideByZeroException>());

5. 数据驱动测试

[TestFixture]
public class DataDrivenTests
{
    // 使用 TestCaseSource
    private static readonly object[] AddCases =
    {
        new object[] { 1, 2, 3 },
        new object[] { -1, -1, -2 },
        new object[] { 0, 0, 0 }
    };
    [TestCaseSource(nameof(AddCases))]
    public void Add_TestCases(int a, int b, int expected)
    {
        var result = a + b;
        Assert.AreEqual(expected, result);
    }
    // 使用 ValueSource
    private static int[] Values = { 1, 2, 3, 4, 5 };
    [Test]
    public void IsPositive_ValueSource([ValueSource(nameof(Values))] int value)
    {
        Assert.That(value, Is.GreaterThan(0));
    }
    // 使用 Random 数据
    [Test]
    public void RandomTest([Random(1, 100, 5)] int value)
    {
        Assert.That(value, Is.InRange(1, 100));
    }
}

6. 测试生命周期

[TestFixture]
public class LifecycleTests
{
    // 一次性设置(整个测试类开始时)
    [OneTimeSetUp]
    public void OneTimeSetUp()
    {
        Console.WriteLine("整个测试类只执行一次");
    }
    // 每个测试方法前的设置
    [SetUp]
    public void SetUp()
    {
        Console.WriteLine("每个测试方法前执行");
    }
    [Test]
    public void Test1()
    {
        Console.WriteLine("Test1 执行");
    }
    [Test]
    public void Test2()
    {
        Console.WriteLine("Test2 执行");
    }
    // 每个测试方法后的清理
    [TearDown]
    public void TearDown()
    {
        Console.WriteLine("每个测试方法后执行");
    }
    // 一次性清理(整个测试类结束时)
    [OneTimeTearDown]
    public void OneTimeTearDown()
    {
        Console.WriteLine("整个测试类结束时执行一次");
    }
}

7. 异步测试

[Test]
public async Task GetDataAsync_ReturnsValidData()
{
    // 异步方法测试
    var result = await _service.GetDataAsync();
    
    Assert.That(result, Is.Not.Null);
    Assert.That(result.Count, Is.GreaterThan(0));
}
[Test]
public async Task SaveAsync_ThrowsException_WhenInvalid()
{
    var invalidData = new Data();
    
    // 异步异常测试
    Assert.That(async () => await _service.SaveAsync(invalidData),
        Throws.TypeOf<ValidationException>());
}

8. 常用特性测试

[TestFixture]
public class AdvancedTests
{
    [Test]
    [Category("Integration")]  // 分类测试
    [Category("Slow")]
    public void IntegrationTest()
    {
        // 集成测试代码
    }
    [Test]
    [Order(1)]  // 指定执行顺序
    public void FirstTest() { }
    [Test]
    [Order(2)]
    public void SecondTest() { }
    [Test]
    [Platform("Win")]  // 平台特定测试
    public void WindowsOnlyTest() { }
    [Test]
    [Repeat(5)]  // 重复执行测试
    public void RepeatableTest()
    {
        // 会执行5次
    }
}

9. 测试组合和理论

[TestFixture]
public class CombinatorialTests
{
    // 组合测试:所有参数组合
    [Test]
    [Combinatorial]
    public void TestAllCombinations(
        [Values(true, false)] bool flag1,
        [Values(1, 2, 3)] int number)
    {
        // 会运行 2 * 3 = 6 次测试
    }
    // 成对测试:减少组合数
    [Test]
    [Pairwise]
    public void TestPairwise(
        [Values("a", "b", "c")] string str,
        [Values(1, 2)] int num,
        [Values(true, false)] bool flag)
    {
        // 使用成对算法减少测试用例
    }
}


 您阅读本篇文章共花了: 

免责声明
本站内容均为博客主本人日常使用记录的存档,如侵犯你的权益请联系:lifei@zaiheze.com 546262132@qq.com 沟通删除事宜。本站仅带访问端口形式使用,已杜绝搜索引擎爬取。

扫描二维码推送至手机访问。

版权声明:本文由LIFEI - blog发布,如需转载请注明出处。

本文链接:http://lifeiai.com/index.php?id=462

分享给朋友:

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。