Создание нового экземпляра бина после каждого модульного теста - PullRequest
7 голосов
/ 10 февраля 2012

Я новичок в среде Spring и у меня есть вопрос о ее возможностях внедрения зависимостей с помощью Spring Context.

Это класс, для которого я пытаюсь написать интеграционный тест:

public class UserService {

private Validator validator;
private UserRepository userRepository;
private Encryptor encryptor;
private MailService mailService;

...

public void registerUser(User user) {
    user.setPassword(encryptor.encrypt(user.getPassword()));

    Errors errors = new BindException(user, "user");
    validator.validate(user, errors);

    if (errors.getErrorCount() == 0) {
        userRepository.addUser(user);
        mailService.sendMail(user.getEmail());
    }
}

В моих тестах (с использованием Mockito) я хочу убедиться, что четыре элемента называются, поэтому я создаю такие тесты, как:

public void testRegisterCallsValidateInValidator() {
    userService.registerUser(testUser);
    verify(userService.getValidator(), times(1)).validate(any(User.class), any(Errors.class));
}

Однако все тесты не дают результатов, говоря, что я вызывал метод несколько раз. Я могу только предположить, что компонент UserService создается один раз в начале всех тестов, но не перезагружается после каждого теста.

В моей тестовой конфигурации я использую следующий xml, чтобы решить, какие бины вводить:

<bean id="userService" class="be.kdg.coportio.services.UserService">
    <property name="validator" ref="validator"/>
    <property name="userRepository" ref="userRepository"/>
    <property name="encryptor" ref="encryptor"/>
    <property name="mailService" ref="mailService"/>
</bean>

Есть идеи?

Ответы [ 5 ]

28 голосов
/ 10 февраля 2012

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

Я полагаю, вы используете Junit 4.5+.Это было бы похоже на другие тестовые рамки.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"mycontext.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class MyTestClass {
...
    // my tests    
...
}

Вы можете поместить @DirtiesContext на уровне метода, если методов, которые нуждаются в "исправлении", немного, но если вы используете Spring, ваш лучший вариант - делать это после каждого теста.

В любом случае, я не думаю, что вы должны использовать mocks / spies в интеграционных тестах:

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

  • В интеграционных тестах используйте настоящие бины и дайте Spring войти.Здесь цель состоит в том, чтобы проверить, что bean-компоненты хорошо взаимодействуют друг с другом / со средой (база данных, сеть, ...). Вы не хотите изолировать bean-компоненты здесь, поэтому вы не должны использовать mocks.

См. Spring-документацию о тестировании для более подробных объяснений.

9 голосов
/ 10 февраля 2012

Чтобы четко разделить единичные и интеграционные тесты (пропуская дебаты о том, что означает каждая категория) - вы можете протестировать свой сервис двумя способами:

  • через интеграционный тест - вы запускаете всеSpring Context и тестирование сервиса как синглтон-компонента.
  • с помощью юнит-теста - вы просто инициализируете сервис самостоятельно, высмеиваете то, что нужно смоделировать, без необходимости в Spring.

Я предлагаю не смешивать Spring и mocks, если вы можете ему помочь - используйте Mockito для модульных тестов (что вам и нужно), и используйте интеграционные тесты, которые загружают весь Springконтекст для тестирования других вещей - постоянных забот, транзакций и т. д.

Вам не нужен Spring, чтобы высмеивать коллабораторов класса и проводить простое тестирование взаимодействия с Mockito.

2 голосов
/ 10 февраля 2012

в вашем методе @Before, убедитесь, что сбрасывали ваши фиктивные объекты.

@Before
public void setup(){
    Mockito.reset(validator);
}
0 голосов
/ 10 февраля 2012

Я никогда не использовал Mockito, но Spring-Beans по умолчанию являются Singletons - поэтому они не будут воссозданы, если вы не вызовете refresh() для Spring-Container.

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

0 голосов
/ 10 февраля 2012

Вы можете попробовать вызвать setDirty (true) в своем тестовом методе для перезагрузки контекста Spring.

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