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); }
用例结果,断言通过
限定值
使用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容器实战指南