Модульное тестирование метода без видимого изменения состояния - PullRequest
0 голосов
/ 13 августа 2010

(C #, Rhino Mocks, MbUnit).

У меня есть класс AccountManager, у которого есть метод RegisterUser (). Этот метод возвращает void, но выдает исключение для любых ошибок. AccountManager вызывает IDataRepository, вызывая его метод AddUser () для вставки базы данных.

Я издеваюсь над IDataRepository, используя Rhino Mock и выбрасываю и исключение для заданного набора аргументов, имитирующих исключение, вызываемое в хранилище.

[Test]
    public void RegisterKnownUser()
    {
        MockRepository mocks = new MockRepository();
        IDataRepository dataRepository = mocks.StrictMock<IDataRepository>();

        using (mocks.Record())
        {
            Expect.Call(() => dataRepository.AddUser("abc", "abc", "abc@abc.com", "a", "bc")).Throw(
                new InvalidOperationException());
        }

        using (mocks.Playback())
        {
            AccountManager manager = new AccountManager(dataRepository);
            Assert.Throws(typeof (InvalidOperationException), () => manager.RegisterUser("abc", "abc", "abc@abc.com", "a", "bc"));
        }
    }

Этот тест отлично работает.

Мой вопрос заключается в том, что делать с ситуацией, когда аргументы, переданные в RegisterUser, являются правильными и действительными. Настоящий IDataRepository не будет ничего возвращать и не будет выдавать никаких исключений. Короче говоря, состояние AccountManager не изменилось бы. Означает ли это, что мне не нужно тестировать AccountManager.RegisterUser, когда это не даст ничего, что я могу наблюдать непосредственно в тестируемом классе и методе. Проверка на состояние в макете немного пахнет для меня. Я думаю, что пока я тестирую IDataRepository.AddUser отдельно, мне не нужно проверять AccountManager.RegisterUser для входных данных, которые не приведут к ничему наблюдаемому в классе.

Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 13 августа 2010

Если AccountManager вызывает в DataPrepository, ваш тестовый пример все равно что-то проверяет. Запись / воспроизведение здесь подтверждает, что в нее сделан вызов. Если вызов не сделан, тестовый набор не пройден. Если он сделан дважды / с неправильными аргументами, он потерпит неудачу.

Это может быть очень простой тестовый пример, но он все еще хороший, и не требует, чтобы вы помещали состояние в фиктивный объект.

0 голосов
/ 13 августа 2010

Возможно, вы захотите проверить метод с допустимыми аргументами, чтобы убедиться, что не выдает никаких исключений .Другими словами, вы не можете наблюдать за изменением состояния или использовать возвращаемое значение (поскольку оно не имеет значения), но вы можете наблюдать, что метод выполнялся без исключений.

Кстати, если метод не возвращает ни значение, ниизмените AccountManager состояние, оно действительно изменит что-то иначе (если нет, то вам, вероятно, следует удалить из своего кода метод, который вообще ничего не делает *1009*).
Например,это может повлиять на DataRepository.Или добавить запись в базу данных.В этом случае вы можете проверить, по крайней мере, изменились ли данные или успешно добавлена ​​запись.Или он может регистрировать событие, сообщающее, что новый пользователь был зарегистрирован, поэтому вы сможете в своих тестах проверить, присутствует ли здесь событие журнала.

Я думаю, пока я тестирую IDataRepository.AddUser отдельно, тогда мне не нужно проверять AccountManager.RegisterUser для входных данных, которые не приведут к ничему наблюдаемому в классе

Если AccountManager.RegisterUser ничего не добавляет к IDataRepository.AddUser кроме применения аргументов, чем даВам не нужно проверять это, если вы уже проверяли IDataRepository.AddUser.Если он проверяет аргументы, вызывает AddUser и делает что-то еще , было бы хорошо проверить, правильно ли то, что он делает.

Допустим, у вас есть:

public void AddUser(string userName, string userMail, string passwordHash)
{
    // [...] Add a user to the database.
}

public void RegisterUser(string userName, string userMail, string passwordHash)
{
    if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException(...);
    if (string.IsNullOrEmpty(userMail)) throw new ArgumentNullException(...);
    if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentNullException(...);
    if (!Checks.IsValidMail(userMail)) throw new ArgumentException(...);

    this.AddUser(userName, userMail, passwordHash);

    this.SaveToLog(LogEvent.UserRegistered, userName, this.IPAddress);
}

В RegisterUser вы тестируете первые четыре строки, передавая неверные аргументы и ожидая исключения.Пятая строка не должна тестироваться, так как вы уже тестировали AddUser.Наконец, шестая строка должна быть проверена, чтобы убедиться, что при вызове RegisterUser с допустимыми аргументами создается запись журнала.

...