Переопределение ранее установленного поведения функции Typemock Isolator (WillThrow () / DoInstead ()) - PullRequest
3 голосов
/ 28 июня 2011

Пожалуйста, проверьте этот код:

        public UserRepository GetUserRepositoryMock()
        {
            // mock all UserRepository instances.
            var userRepositoryMock = Isolate.Fake.Instance<UserRepository>();
            Isolate.Swap.AllInstances<UserRepository>().With(userRepositoryMock);

            // make all public UserRepository members throw a NotSupportedException.
            Isolate.WhenCalled(() => userRepositoryMock.Select(null)).WillThrow(new NotSupportedException());
            Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).WillThrow(new NotSupportedException());
            Isolate.WhenCalled(() => userRepositoryMock.Save(null)).WillThrow(new NotSupportedException());
            Isolate.WhenCalled(() => userRepositoryMock.Remove(null)).WillThrow(new NotSupportedException());
            // ... et cetera until all public members of UserRepository will throw NotSupportedException.
        }

        [TestMethod]
        public void ActivateUser_UserNotFound_ThrowsException()
        {
            var userRepositoryMock = GetUserRepositoryMock();

            // override one of the public UserRepository members to return a specific value.
            Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).DoInstead(context =>
            {
                return null;
            });

            // call ActivateUser implementation.
            RegistrationServices.ActivateUser("foo@bar.com", "password", "activation-code");
        }

Что этот фрагмент кода на самом деле делает , создает исключение NotSupportedException для всех открытых членов UserRepository.

Что Я хочу , чтобы этот фрагмент кода сделал все открытые члены UserRepository , выдавшими NotSupportedException за исключением функции SelectOne () , которая возвращает null .

Я хочу убедиться, что функция ActivateUser () RegistrationServices не вызывает никакую функцию UserRepository , кроме SelectOne () , которую я явно указал.

Если, например, это так,изменив реализацию ActivateUser () , чтобы вызвать Save () из UserRepository и не изменяя соответствующий тест * ActivateUser_UserNotFound_ThrowsException *, я хочутест не пройден, потому что изменение можетвызвать неожиданное поведение.Таким образом, я могу полностью изолировать свое приложение от третьих лиц и свести к минимуму ошибки кодирования.

Мои вопросы относительно этого кода и его принципов:

  1. Как мне достичь желаемого поведения?
  2. Есть ли какие-нибудь альтернативы, которые я могу исследовать для достижения желаемого поведения?
  3. Являются ли основные принципы желаемого поведения допустимыми? т.е. Должен ли я хотеть изолировать все приложение от третьих лиц для целей тестирования, вызывать исключения, когда вызывается непредвиденная функция, и возвращать действительные данные только тогда, когда это явно указано?

Ответы [ 2 ]

2 голосов
/ 28 июня 2011

Примечание: я работаю в Typemock

Есть несколько подходов, которые вы можете использовать здесь, чтобы проверить, что вы хотите.Например, вы можете использовать API Isolate.Verify, чтобы удостовериться, что для вашего поддельного объекта не было сделано никаких особых вызовов.

Это позволит вам не указывать явно возврат других методов,так как вы можете убедиться, что они не произошли:

    [Test, Isolated]
    public void ActivateUser_UserNotFound_ThrowsException()
    {
        var userRepositoryMock = Isolate.Fake.Instance<UserRepository>();
        Isolate.Swap.AllInstances<UserRepository>().With(userRepositoryMock);

        Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).WillReturn(null);

        // call ActivateUser implementation.
        RegistrationServices.ActivateUser("foo@bar.com", "password", "activation-code");

        Isolate.Verify.WasNotCalled(() => userRepositoryMock.Save(null));
        Isolate.Verify.WasNotCalled(() => userRepositoryMock.Remove(null));
    }

WhenCalled() метод изолятора цепочек поведения в определенном порядке, что означает, что в вашем исходном тесте SelectOne в первый раз выдает исключениево второй раз и после этого вернется null.

Надеюсь, это поможет, если у вас есть другие вопросы, пожалуйста, не стесняйтесь связаться с нами через службу поддержки, или здесь!

1 голос
/ 28 июня 2011

Я не уверен, почему вы хотите так поступить.Вам лучше было бы настроить свой фиктивный объект внутри теста и создать новый фиктивный объект для каждого теста.

на первый взгляд это может показаться более трудоемким, но с использованием подхода, описанного вышезатруднит выполнение ваших тестов, а метод GetUserRepositoryMock будет становиться все менее и менее полезным, поскольку вы добавляете больше тестов, для которых требуется немного другое поведение.

, если вы хотите просто проверить, что эти методы не были вызваны, тогдаВы можете сделать это с помощью Isolate.Verify.WasNotCalled(x=>x.Select(null)); для каждого из методов.Я думаю, что это лучший подход, если вы настраиваете все методы для генерации исключений.

Вы можете получить что-то в соответствии с вашими предпочтениями, если ваш метод GetUserRepositoryMock будет использовать много логических значений, которые указывают, какие методы имитироватьс throw NotImplementedException, все по умолчанию на true.Тогда вы можете использовать именованные параметры, чтобы просто указать тот, который вы не хотите устанавливать.Что-то вроде этого:

    public UserRepository GetUserRepositoryMock(bool mockSelect=true, bool mockSelectOne=true, bool mockSave=true ... etc etc)
    {
        // mock all UserRepository instances.
        var userRepositoryMock = Isolate.Fake.Instance<UserRepository>();
        Isolate.Swap.AllInstances<UserRepository>().With(userRepositoryMock);

        // make all public UserRepository members throw a NotSupportedException.
        if(mockSelect) 
            Isolate.WhenCalled(() => userRepositoryMock.Select(null)).WillThrow(new NotSupportedException());
        if (mockSelectOne)
            Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).WillThrow(new NotSupportedException());
        if(mockSave)
            Isolate.WhenCalled(() => userRepositoryMock.Save(null)).WillThrow(new NotSupportedException());

        // ... et cetera until all public members of UserRepository will throw NotSupportedException where the corresponding flag is true.
    }

тогда вы можете вызывать это так в своем тесте, когда вы не хотите, чтобы SelectOne был смоделирован:

    [TestMethod]
    public void ActivateUser_UserNotFound_ThrowsException()
    {
        var userRepositoryMock = GetUserRepositoryMock(mockSelectOne:false);

        // override one of the public UserRepository members to return a specific value.
        Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).DoInstead(context =>
        {
            return null;
        });

        // call ActivateUser implementation.
        RegistrationServices.ActivateUser("foo@bar.com", "password", "activation-code");
    }

Так что вам нужно толькоукажите вещи, которые вы хотите отличать от значений по умолчанию, используя именованные параметры.

Я не говорю, что мне особенно нравится это решение, и вполне может быть лучшее решение, использующее некоторые из собственных методов TypeMocks,но это должно дать вам то, что вы хотите.Вы можете настроить этот метод таким образом, чтобы вместо этого делать проверки Isolate.Verify.WasNotCalled() для каждой вещи, основываясь на флагах.

...