NSubstitute单元测试Mock框架,开启你单元测试的学习!

发布于:2025-08-13 ⋅ 阅读:(23) ⋅ 点赞:(0)

我们在这里使用NSubstitute+FluentAssertions(断言库)进行代码的演示和讲解,FluentAssertions的功能较为简单,我们在这里直接使用,在别的文章进行专门的讲解

在本文中我将以以下两个类为背景,进行单元测试的讲解,测试框架我们使用NUnit
TestService主要包含Login功能,在其中调用IApiService进行登陆验证

 public class TestService : ITestService
 {
     private readonly IApiService apiService;
     public TestService(IApiService apiService)
     {
         this.apiService = apiService;
     }

     public bool Login(User user)
     {
         return apiService.Login(user);
     }
 }

IApiService仅包含登陆接口,一会我们将对IApiService进行Mock

 public interface IApiService
 {
     bool Login(User user);
 }

创建Mock对象

IApiService apiService = apiService = Substitute.For<IApiService>();

通过以上的语句我就可以Mock出相应的对象来进行测试
带到之前的项目背景中我们将这样进行对象的Mock,我们可以放在[SetUp]中进行对象的Mock

public class TestServiceUnitTest
{
    IApiService apiService;

    [SetUp]
    public void Setup()
    {
        // 创建模拟对象
        apiService = Substitute.For<IApiService>();
    }
}

参数匹配器

任意值

使用Arg.Any<T>()来表示,Mock输入任意值,设置Mock的结果,如下设置任意值都登录成功

public class TestServiceUnitTest
{
    IApiService apiService;

    [SetUp]
    public void Setup()
    {
        apiService = Substitute.For<IApiService>();
        testService = new TestService(apiService);
    }

    [Test]
    public void ShouldLoginCorrectly()
    {
        // mock数据 任何登录值都返回true 
        apiService.Login(Arg.Any<User>()).Returns(true);
        var result = testService.Login(new User() {UserName = "123", Password = "456"});
        result.Should().Be(true);
    }

用例结果,断言通过
在这里插入图片描述

限定值

使用Arg.Is()来表示,Mock输入UserName等于1,Password等于1时,设置Mock的结果为True

[Test]
public void ShouldLoginSuccessCorrectly()
{
    apiService.Login(Arg.Is<User>(x => x.UserName == "1" && x.Password == "1")).Returns(true);
    var result = testService.Login(new User() { UserName = "1", Password = "1" });
    result.Should().Be(true);
}      

用例结果,断言通过`在这里插入代码片
在这里插入图片描述

设置Mock对象返回值

我们在单元测试中可能面临这样一个场景,在登陆时我们需要判断,当输入的用户名和密码为1时,通过登陆,其余情况登陆失败。
前面在参数匹配器中已经有了示例,这里我们将示例完整

public class TestServiceUnitTest
{
    TestService testService;
    IApiService apiService;
    
    [SetUp]
    public void Setup()
    {
        apiService = Substitute.For<IApiService>();
        testService = new TestService(apiService);
    }
    
    [Test]
    public void ShouldLoginSuccessCorrectly()
    {
        // mock数据
        apiService.Login(Arg.Is<User>(x => x.UserName == "1" && x.Password == "1")).Returns(true);
        var result = testService.Login(new User() { UserName = "1", Password = "1" });
        result.Should().Be(true);
    }
    
    [Test]
    public void ShouldLoginFailedCorrectly()
    {
        // mock数据
        apiService.Login(Arg.Is<User>(x => x.UserName == "1" && x.Password == "1")).Returns(true);
        var result = testService.Login(new User() { UserName = "123", Password = "789" });
        result.Should().Be(false);
    }
}

用例结果,断言通过
在这里插入图片描述

设置Mock抛出异常

在我们调用接口进行登录的时候,也会出现没有调用成功,返回异常的时候,这时我们也来模拟下这种场景
我们首先修改之前的登录方法,进行异常的处理

public class TestService : ITestService
{
    private readonly IApiService apiService;
    public TestService(IApiService apiService)
    {
        this.apiService = apiService;
    }

    public bool Login(User user)
    {
        try
        {
            return apiService.Login(user);
        }
        catch(Exception ex)
        {
            return false;
        }
    }
}

我们使用xx.Throws进行Mock异常的抛出

[Test]
public void ShouldLoginExceptionCorrectly()
{
    apiService.Login(Arg.Any<User>()).Throws(new Exception("Login failed"));
    var result = testService.Login(new User() { UserName = "1", Password = "1" });
    result.Should().Be(false);
}

断言方法被调用次数

判断方法被调用的次数,我们需要用到Received(),下面我们通过示例看看

 [Test]
 public void ShouldLoginExceptionCorrectly()
 {
     // mock数据
     apiService.Login(Arg.Any<User>()).Throws(new Exception("Login failed"));
     var result = testService.Login(new User() { UserName = "1", Password = "1" });
     result.Should().Be(false);
     apiService.Received(1).Login(Arg.Is<User>(x => x.UserName == "1" && x.Password == "1"));
 }

我们在登录之后,判断登录方法被调用的次数

相关的Mock内容还有很多,我这里主要列举常用的形式,其余内容大家可以通过官方文档进行学习。

往期推荐

.NET-键控服务依赖注入

掌握Autofac:IOC容器实战指南


网站公告

今日签到

点亮在社区的每一天
去签到