C# 用mock进行单元测试。测试行为而不是实现
当单元测试调用其他类的类时,我总是遇到一个问题,例如,我有一个类,它从一个电话号码创建一个新用户,然后将其保存到数据库,并向提供的号码发送SMS 就像下面提供的代码一样C# 用mock进行单元测试。测试行为而不是实现,c#,unit-testing,mocking,C#,Unit Testing,Mocking,当单元测试调用其他类的类时,我总是遇到一个问题,例如,我有一个类,它从一个电话号码创建一个新用户,然后将其保存到数据库,并向提供的号码发送SMS 就像下面提供的代码一样 public class UserRegistrationProcess : IUserRegistration { private readonly IRepository _repository; private readonly ISmsService _smsService; public Us
public class UserRegistrationProcess : IUserRegistration
{
private readonly IRepository _repository;
private readonly ISmsService _smsService;
public UserRegistrationProcess(IRepository repository, ISmsService smsService)
{
_repository = repository;
_smsService = smsService;
}
public void Register(string phone)
{
var user = new User(phone);
_repository.Save(user);
_smsService.Send(phone, "Welcome", "Message!");
}
}
这是一个非常简单的类,但是您将如何着手并测试它呢
现在我在用mock,但我真的不喜欢它
[Test]
public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
{
var repository = new Mock<IRepository>();
var smsService = new Mock<ISmsService>();
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
var phone = "07012345678";
userRegistration.Register(phone);
repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());
}
[Test]
public void WhenRegistreringANewUser_ItWillSendANewSms()
{
var repository = new Mock<IRepository>();
var smsService = new Mock<ISmsService>();
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
var phone = "07012345678";
userRegistration.Register(phone);
smsService.Verify(x => x.Send(phone, It.IsAny<string>(), It.IsAny<string>()), Times.Once());
}
[测试]
当注册一个Wuser_thenewwUsersAvedtheDatabase()时,公共无效
{
var repository=newmock();
var smsService=new Mock();
var userRegistration=newuserregistrationprocess(repository.Object,smsService.Object);
var phone=“07012345678”;
用户注册。注册(电话);
Verify(x=>x.Save(It.Is(user=>user.Phone==Phone)),Times.Once();
}
[测试]
注册Anewuser_ItWillSendANewSms()时公共无效
{
var repository=newmock();
var smsService=new Mock();
var userRegistration=newuserregistrationprocess(repository.Object,smsService.Object);
var phone=“07012345678”;
用户注册。注册(电话);
验证(x=>x.Send(phone,It.IsAny(),It.IsAny()),Times.Once());
}
感觉我在这里测试错误的东西
有什么想法可以让它变得更好吗?在你的情况下,不需要写
repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());
同样对于smsService来说,使用Moq也是一种很好的方法。重构后再看
Mock<IRepository<>> repository;
private Mock<ISmsService> smsService;
const string phone = "0768524440";
[SetUp]
public void SetUp()
{
repository = new Mock<IRepository<>>();
smsService = new Mock<ISmsService>();
}
[Test]
public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
{
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
userRegistration.Register(phone);
repository.VerifyAll();
smsService.VerifyAll();
}
Mock库;
私人模拟SMS服务;
const string phone=“0768524440”;
[设置]
公共作废设置()
{
repository=newmock();
smsService=newmock();
}
[测试]
当注册一个Wuser_thenewwUsersAvedtheDatabase()时,公共无效
{
var userRegistration=newuserregistrationprocess(repository.Object,smsService.Object);
用户注册。注册(电话);
repository.VerifyAll();
smsService.VerifyAll();
}
按照@Serghei建议的方式重构模拟是好的
我还发现行为的名称实际上并没有描述行为。我喜欢用“应该”这个词,比如“我的班级应该做一些事情”
在注册用户时,类不应将用户发送到数据库。它应该要求存储库保存用户。这就是全部。它不知道存储库是将其发送到数据库、将其保存在内存中还是将其从轨道中删除。这不是你们班的责任
通过用这种方式表达行为,您可以明确地显示——并帮助其他人理解——您的类的职责范围结束于何处
如果您在注册新用户时将方法重命名为。\u AsksRepositoryToSaveIt()
,这可能会使您给出的示例更自然。您需要使用mock.Setup(…)
来实现该功能。上下文模式、设置预期、事件、结果,然后去验证并阅读您之前设置的内容。。。当你只是试图描述你想要的行为时,这并不容易理解。真的很喜欢你在这里用词的方式谢谢。我是一个铁杆BDDer;)
Mock<IRepository<>> repository;
private Mock<ISmsService> smsService;
const string phone = "0768524440";
[SetUp]
public void SetUp()
{
repository = new Mock<IRepository<>>();
smsService = new Mock<ISmsService>();
}
[Test]
public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
{
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
userRegistration.Register(phone);
repository.VerifyAll();
smsService.VerifyAll();
}