简体   繁体   English

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

[英]Unit testing with Mocks. Test behaviour not implementation

I always had a problem when unit testing classes that calls other classes, for example I have a class that creates a new user from a phone-number then saves it to the database and sends a SMS to the number provided. 当单元测试类调用其他类时,我总是遇到问题,例如,我有一个类,它从电话号码创建一个新用户,然后将其保存到数据库,并向提供的号码发送短信。

Like the code provided below. 像下面提供的代码一样。

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

It is a really simple class but how would you go about and test it? 这是一个非常简单的课程,但你会如何进行测试呢?

At the moment im using Mocks but I dont really like it 目前我正在使用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());
    }

It feels like I am testing the wrong thing here? 感觉就像我在这里测试错误的东西?

Any thoughts on how to make this better? 关于如何让这更好的想法?

Refactoring the mocks out in the way that @Serghei suggests is good. 以@Serghei建议的方式重构模拟是好的。

I also see that the name of the behaviour isn't actually describing the behaviour. 我还看到行为的名称实际上并没有描述行为。 I like to use the word "should", as in, "My class should do some stuff ". 我喜欢用“应该”这个词,比如“我的班级should do some stuff ”。

Your class shouldn't send the user to the database when it's registering a user. 您的类在注册用户时不应将用户发送到数据库。 It should ask the repository to save the user. 它应该要求存储库保存用户。 That's all. 就这样。 It doesn't know whether the repository sends it to the database, keeps it in memory or nukes it from orbit. 它不知道存储库是将其发送到数据库,将其保存在内存中还是从轨道上进行核对。 It's not your class's responsibility. 这不是你班级的责任。

By phrasing the behaviour this way, you can explicitly show - and help others understand - where the scope of your class's responsibility ends. 通过这种方式表达行为,您可以明确地显示 - 并帮助他人理解 - 您的班级职责范围的终点。

If you rename your method something like WhenRegisteringANewUser_AsksRepositoryToSaveIt() that might make the example you've given feel more natural. 如果你重命名你的方法就像WhenRegisteringANewUser_AsksRepositoryToSaveIt()那样可能会让你感觉更自然。

In your case don't need to write 在你的情况下,不需要写

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

because this method doesn't return a value you can write 因为此方法不会返回您可以编写的值

repository.VerifyAll();

Also for smsService it's a good way to use Moq. 同样对于smsService来说,这是使用Moq的好方法。

look at after some refactor 看看一些重构后

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM