开放的编程资料库

当前位置:我爱分享网 > C#教程 > 正文

C# 单元

C#NUnit教程展示了如何使用NUnit框架在C#中进行单元测试。

单元测试是一种软件测试,其中测试软件的各个单元(组件)。单元测试的目的是验证软件的每个单元是否按设计执行。单元是任何软件中最小的可测试部分。

NUnit是适用于所有.NET语言的单元测试库。它的灵感来自Java的JUnit。其他单元测试库包括XUnit和MSTest。

可以将测试放在同一个项目目录或不同的目录中。我们从一个更简单的选项开始,并将测试放在同一个项目目录中。最后,我们展示了后一种选择。

$ dotnet add Microsoft.NET.Test.Sdk
$ dotnet new nunit
$ dotnet add NUnit3TestAdapter

为了使用NUnit,我们需要添加这三个库。

C#NUNit简单示例

我们从一个简单的例子开始。

namespace Arithmetic;

class Basic
{
    public static Func<int, int, int> add = (a, b) => a + b;
    public static Func<int, int, int> mul = (a, b) => a * b;
    public static Func<int, int, int> sub = (a, b) => a - b;
    public static Func<int, int, int> div = (a, b) => a / b;
}

我们测试简单的算术函数。

我们将测试放入测试目录。NUnit自动发现我们的测试。

namespace Testing;

using NUnit.Framework;
using Arithmetic;

class ArithTest
{
    [Test]
    public void SimpleArithmetic()
    {
        int r1 = Basic.add(3, 3);
        Assert.AreEqual(r1, 6);

        int r2 = Basic.sub(3, 3);
        Assert.AreEqual(r2, 0);

        int r3 = Basic.mul(3, 3);
        Assert.AreEqual(r3, 9);

        int r4 = Basic.div(3, 3);
        Assert.AreEqual(r4, 1);
    }
}

测试方法用[Test]属性注释。我们使用断言来确保正确的输出。

$ dotnet test 
... 
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, ...

C#NUnit跳过测试

可以使用[Ignore]属性跳过测试方法。

namespace Testing;

using NUnit.Framework;
using Arithmetic;

class ArithTest
{
    [Test]
    public void AddSub()
    {
        int r1 = Basic.add(3, 3);
        Assert.AreEqual(r1, 6);

        int r2 = Basic.sub(3, 3);
        Assert.AreEqual(r2, 0);
    }

    [Test]
    [Ignore("Ignoring")]
    public void MulDiv()
    {
        int r3 = Basic.mul(3, 3);
        Assert.AreEqual(r3, 9);

        int r4 = Basic.div(3, 3);
        Assert.AreEqual(r4, 1);
    }
}

我们有两种测试方法。使用[Ignore]属性跳过其中之一。

C#NUnit测试用例

通过TestCase属性,我们可以拥有参数化的测试方法。

namespace Testing;

using NUnit.Framework;
using Arithmetic;

class ArithTest
{
    [TestCase(1, 2, 3)]
    [TestCase(2, 2, 4)]
    [TestCase(-1, 4, 3)]
    public void Add(int x, int y, int z)
    {
        int r = Basic.add(x, y);
        Assert.AreEqual(r, z);
    }

    [TestCase(1, 2, -1)]
    [TestCase(2, 2, 0)]
    [TestCase(3, 2, 1)]
    public void Sub(int x, int y, int z)
    {
        int r = Basic.sub(x, y);
        Assert.AreEqual(r, z);
    }

    [TestCase(9, 3, 27)]
    [TestCase(3, 3, 9)]
    [TestCase(-3, -3, 9)]
    public void Mul(int x, int y, int z)
    {
        int r = Basic.mul(x, y);
        Assert.AreEqual(r, z);
    }

    [TestCase(9, 3, 3)]
    [TestCase(3, 3, 1)]
    [TestCase(8, 2, 4)]
    public void Div(int x, int y, int z)
    {
        int r = Basic.div(x, y);
        Assert.AreEqual(r, z);
    }
}

在此示例中,我们使用三组值测试每个方法。

C#NUnitTestCaseSource

[TestCaseSource]属性允许我们从不同来源读取参数化测试方法的数据。

namespace Testing;

using NUnit.Framework;
using Arithmetic;

public class ArithTest
{
    [TestCaseSource(nameof(AddCases))]
    public void Add(int x, int y, int z)
    {
        int r = Basic.add(x, y);
        Assert.AreEqual(r, z);
    }

    [TestCaseSource(nameof(SubCases))]
    public void Sub(int x, int y, int z)
    {
        int r = Basic.sub(x, y);
        Assert.AreEqual(r, z);
    }

    [TestCaseSource(nameof(MulCases))]
    public void Mul(int x, int y, int z)
    {
        int r = Basic.mul(x, y);
        Assert.AreEqual(r, z);
    }


    [TestCaseSource(nameof(DivCases))]
    public void Div(int x, int y, int z)
    {
        int r = Basic.div(x, y);
        Assert.AreEqual(r, z);
    }

    static object[] AddCases =
    {
        new object[] { 1, 2, 3 },
        new object[] { 2, 2, 4 },
        new object[] { -1, 4, 3 }
    };
    
    static object[] SubCases =
    {
        new object[] { 1, 2, -1 },
        new object[] { 2, 2, 0 },
        new object[] { 3, 2, 1 }
    };

    static object[] MulCases =
    {
        new object[] { 9, 3, 27 },
        new object[] { 3, 3, 9 },
        new object[] { -3, -3, 9 }
    };

    static object[] DivCases =
    {
        new object[] { 9, 3, 3 },
        new object[] { 3, 3, 1 },
        new object[] { 8, 2, 4 }
    };
}

在这个例子中,值被放置在数组中。

C#NUnit预期结果

使用ExpectedResult,我们可以简化测试设置。

namespace Testing;

using NUnit.Framework;
using Arithmetic;

class ArithTest
{
    [TestCase(1, 2, ExpectedResult = 3)]
    [TestCase(2, 2, ExpectedResult = 4)]
    [TestCase(-1, 4, ExpectedResult = 3)]
    public int Add(int x, int y)
    {
        return Basic.add(x, y);
    }

    [TestCase(1, 2, ExpectedResult = -1)]
    [TestCase(2, 2, ExpectedResult = 0)]
    [TestCase(3, 2, ExpectedResult = 1)]
    public int Sub(int x, int y)
    {
        return Basic.sub(x, y);
    }

    [TestCase(9, 3, ExpectedResult = 27)]
    [TestCase(3, 3, ExpectedResult = 9)]
    [TestCase(-3, -3, ExpectedResult = 9)]
    public int Mul(int x, int y)
    {
        return Basic.mul(x, y);
    }

    [TestCase(9, 3, ExpectedResult = 3)]
    [TestCase(3, 3, ExpectedResult = 1)]
    [TestCase(8, 2, ExpectedResult = 4)]
    public int Div(int x, int y)
    {
        return Basic.div(x, y);
    }
}

使用ExpectedResult,我们的代码缩短了一点。

将测试放在单独的目录中

在下面的示例中,我们展示了如何将测试放在单独的目录中。

$ mkdir Separate
$ cd Separate

我们创建一个新目录。

$ dotnet new sln

我们创建一个新的空解决方案。

$ mkdir PalindromeService PalindromeService.Tests

创建了两个目录。

$ cd PalindromeService
$ dotnet new classlib

我们创建一个新库。

namespace Palindrome.Services;
  
using System.Globalization;

public class PalindromeService
{
    public bool IsPalindrome(string word)
    {
        IEnumerable<string> GraphemeClusters(string s)
        {
            var enumerator = StringInfo.GetTextElementEnumerator(s);
            while (enumerator.MoveNext())
            {
                yield return (string)enumerator.Current;
            }
        }

        var reversed = string.Join("", GraphemeClusters(word).Reverse().ToArray());

        return reversed == word;
    }
}

PalindromeService包含IsPalindrome方法,该方法确定一个单词是否为回文。

$ cd .. 
$ dotnet sln add PalindromeService\PalindromeService.csproj

我们将PalindromeService添加到解决方案中。

$ cd PalindromeService.Tests
$ dotnet add nunit
$ dotnet add reference ..\PalindromeService\PalindromeService.csproj

我们转到PalindromeService.Tests目录并添加unit库,添加对PalindromeService的引用。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
    <PackageReference Include="nunit" Version="3.13.3" />
    <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
    <PackageReference Include="coverlet.collector" Version="3.1.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\PalindromeService\PalindromeService.csproj" />
  </ItemGroup>

</Project>

这是项目文件的样子。

namespace Palindrome.Services.Tests;

using NUnit.Framework;

public class Tests
{
    private PalindromeService? _palindromeService;

    [SetUp]
    public void SetUp()
    {
        _palindromeService = new PalindromeService();
    }

    [TestCase("racecar")]
    [TestCase("nun")]
    [TestCase("level")]
    public void IsPalindrome(string word)
    {
        var r = _palindromeService!.IsPalindrome(word);
        Assert.AreEqual(r, true);
    }
}

我们用三个单词测试IsPalindrome方法。

[SetUp]
public void SetUp()
{
    _palindromeService = new PalindromeService();
}

[Setup]属性用于提供一组在调用每个测试方法之前执行的通用函数。在我们的例子中,我们创建了PalindromeService

$ cd ..
$ dotnet sln add PalindromeService.Tests\PalindromeService.Tests.csproj

我们将测试项目添加到解决方案中。

$ dotnet test

最后,我们可以运行测试了。

在本文中,我们使用NUnit库在C#中完成了单元测试。

列出所有C#教程。

未经允许不得转载:我爱分享网 » C# 单元

感觉很棒!可以赞赏支持我哟~

赞(0) 打赏