Модульное тестирование с помощью Mocks. Тестовое поведение не реализация - PullRequest
4 голосов
/ 21 декабря 2010

У меня всегда была проблема при модульном тестировании классов, которые вызывают другие классы, например, у меня есть класс, который создает нового пользователя из телефонного номера, затем сохраняет его в базе данных и отправляет SMS на указанный номер. *

Как и код, указанный ниже.

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 ]

4 голосов
/ 22 декабря 2010

Рефакторинг макетов так, как считает @Serghei - это хорошо.

Я также вижу, что название поведения на самом деле не описывает поведение. Мне нравится использовать слово «должен», например, «Мой класс should do some stuff».

Ваш класс не должен отправлять пользователя в базу данных при регистрации пользователя. Стоит попросить репозиторий сохранить пользователя. Это все. Он не знает, отправляет ли репозиторий его в базу данных, хранит ли он в памяти или удаляет его с орбиты. Это не ответственность вашего класса.

Подобным образом сформулировав поведение, вы можете явно показать - и помочь другим понять - где заканчивается сфера ответственности вашего класса.

Если вы переименуете свой метод в что-то вроде WhenRegisteringANewUser_AsksRepositoryToSaveIt(), это может сделать приведенный вами пример более естественным.

0 голосов
/ 21 декабря 2010

посмотрите на какой-то рефакторинг

 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();
    }
0 голосов
/ 21 декабря 2010

В вашем случае не нужно писать

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

потому что этот метод не возвращает значение Вы можете написать

repository.VerifyAll();

Также для smsService это хороший способ использовать Moq.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...