我们在这里使用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内容还有很多,我这里主要列举常用的形式,其余内容大家可以通过官方文档进行学习。