> 技术文档 > NSubstitute单元测试Mock框架,开启你单元测试的学习!

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


我们在这里使用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()来表示,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); }

用例结果,断言通过
NSubstitute单元测试Mock框架,开启你单元测试的学习!

限定值

使用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);} 

用例结果,断言通过`在这里插入代码片
NSubstitute单元测试Mock框架,开启你单元测试的学习!

设置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); }}

用例结果,断言通过
NSubstitute单元测试Mock框架,开启你单元测试的学习!

设置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容器实战指南