用Mocks进行单元测试。测试行为不是实现

时间:2010-12-21 08:10:25

标签: c# unit-testing mocking

当单元测试调用其他类的类时,我总是遇到问题,例如,我有一个类,它从电话号码创建一个新用户,然后将其保存到数据库,并向提供的号码发送短信。

与下面提供的代码一样。

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!");
    }
}

这是一个非常简单的课程,但你会如何进行测试呢?

目前我正在使用Mocks,但我真的不喜欢它

    [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());
    }

感觉我在这里测试错误的东西?

关于如何做到这一点的任何想法?

3 个答案:

答案 0 :(得分:4)

以@Serghei建议的方式重构模拟是好的。

我还看到行为的名称实际上并没有描述行为。我喜欢使用“应该”这个词,如“我的班级should do some stuff”。

您的班级在注册用户时不应将用户发送到数据库。它应该要求存储库保存用户。就这样。它不知道存储库是将其发送到数据库,将其保存在内存中还是从轨道上进行核对。这不是你班级的责任。

通过这种方式表达行为,你可以明确地表明 - 并帮助他人理解 - 你的职责范围在哪里结束。

如果您将方法重命名为WhenRegisteringANewUser_AsksRepositoryToSaveIt(),可能会让您感觉更自然。

答案 1 :(得分:0)

在你的情况下,不需要写

  repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());

因为此方法不返回值 你可以写

repository.VerifyAll();

对于smsService,这是使用Moq的好方法。

答案 2 :(得分:0)

看看一些重构

 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();
    }