Написание поддерживаемых модульных тестов с фиктивными объектами - PullRequest
2 голосов
/ 29 июня 2010

Это упрощенная версия класса, я пишу модульный тест для

class SomeClass {

    void methodA() {
        methodB();
        methodC();
        methodD();
    }

    void methodB() {
        //does something
    }

    void methodC() {
        //does something
    }

    void methodD() {
        //does something
    }
}

Во время написания модульных тестов для этого класса я смоделировал объекты, используя EasyMock, используемый в каждом методе.В методах B, C и D. было легко настроить фиктивные объекты и их ожидание. Но чтобы протестировать метод A, мне нужно НАМНОГО больше настроить фиктивные объекты и их ожидания.Кроме того, я тестирую метод А в разных условиях, то есть мне приходится много раз настраивать фиктивные объекты с разными ожиданиями.

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

Ответы [ 5 ]

3 голосов
/ 29 июня 2010

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

В вашем случае, метод A, кажется, находится на более высоком уровне, чем методы A, B, C. Вы можете рассмотреть возможность удаления его в класс более высокого уровня, который обернул бы SomeClass:

class HigherLevelClass {
    ISomeClass someClass;

    public HigherLevelClass(ISomeClass someClass)
    {
        this.someClass = someClass;
    }

    void methodA() {
        someClass.methodB();
        someClass.methodC();
        someClass.methodD();
    }
}

class SomeClass : ISomeClass {
    void methodB() {
        //does something
    }

    void methodC() {
        //does something
    }

    void methodD() {
        //does something
    }
}

Теперь, когда вы тестируете метод A, все, что вам нужно, - это небольшой интерфейс ISomeClass и три вызова метода.

0 голосов
/ 30 июня 2010

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

Если вам небезразлично, что делает метод А и какой сотрудникдолжна быть вызвана функция, тогда вы должны настроить различные ожидания ... Я не понимаю, как вы можете пропустить этот шаг?!

Если вы testLogout, вы ожидаете вызова myCollaborator.logout () в противном случае, если вы выполните testLogin, вы ожидаете что-то вроде myCollaborator.login ().

Если у вас много методов с большим / разными ожиданиями, возможно, имеет место разделить ваш класс на соавторов

0 голосов
/ 29 июня 2010

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

Установите соответствующие контексты, проверьте результаты и используйте NiceMocksдля всего остального.

Я предпочитаю Mockito (Java) или Moq (.NET), которые работают таким образом по умолчанию.Вот страница Mockito, посвященная Mockito и EasyMock, чтобы вы могли понять (у EasyMock не было NiceMock до появления Mockito):

http://code.google.com/p/mockito/wiki/MockitoVSEasyMock

Вероятно, вы можете использовать EasyMock NiceMock вПодобный способ.Надеюсь, это поможет вам распутать ваши тесты.Вы всегда можете импортировать обе платформы и использовать их рядом друг с другом / постепенно переключаться, если это помогает.

Удачи!

0 голосов
/ 29 июня 2010

Это пример хрупкого теста , потому что макеты имеют слишком глубокие знания о SUT .

.Moq вам не нужно настраивать void методы.Однако в Moq методы должны быть общедоступными или защищенными и виртуальными.

0 голосов
/ 29 июня 2010

Вы можете извлечь общий код установки в отдельные (возможно, параметризованные) методы, а затем вызывать их при необходимости. Если тесты для метода A имеют отличное приспособление от тестов других методов, их может не хватить на сам метод @Before, поэтому вам нужно вызвать соответствующую комбинацию вспомогательных методов установки из самих методов тестирования. Это все еще немного громоздко, но лучше, чем дублировать код повсюду.

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

...