Зависимость внутри класса Validator не инициализируется в модульном тесте - PullRequest
0 голосов
/ 01 мая 2018

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

Я пытаюсь выполнить модульный тест для класса Validator, который имеет зависимость внутри класса.

@NoArgsConstructor
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

    @Autowired
    private AccountService accountService;

    @Override
    public void initialize(final UniqueEmail constraintAnnotation) {
    }

    @Override
    public boolean isValid(final String email, final ConstraintValidatorContext context) {
        return !this.accountService.findByEmail(email).isPresent();
    }
}

Вот стек, где UniqueEmailValidator.java: 47 равно return !this.accountService.findByEmail(email).isPresent();

javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:177)
    at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:68)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:73)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:127)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:120)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:533)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:496)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:465)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:430)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:380)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:169)
    at com.x.x.AccountValidatorTest.shouldDetectDuplicatedEmailAddress(AccountValidatorTest.java:95)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.lang.NullPointerException
    at com.x.x.validator.UniqueEmailValidator.isValid(UniqueEmailValidator.java:47)
    at com.x.x.validator.UniqueEmailValidator.isValid(UniqueEmailValidator.java:1)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:171)
    ... 43 more

У меня вопрос: если в модульном тесте валидатор является инициатором как таковым, как я могу обеспечить внедрение accountService во время модульного теста? Как мне кажется, accountService не вводится или что-то в этом роде, следовательно, NPE.

@RunWith(SpringRunner.class)
@DataJpaTest
public class AccountValidatorTest {

    private static Validator validator;

    @BeforeClass
    public static void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Autowired
    private AccountRepository accountRepository;

    @Test
    public void shouldDetectDuplicatedEmailAddress() {

        User user = new User(); 
        // Setters omit

        // accountRepository.save(user);

        Set<ConstraintViolation<AccountRegistrationForm>> violations = validator.validate(user);

        assertEquals(1, violations.size());
    }

}

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Думаю, с помощью Дэвида я понял, что перепутал юнит-тест и интеграционный тест. Так что в основном с модульным тестом ниже должно быть достаточно, конечно, требуется больше тестов, но это идея.

@RunWith(MockitoJUnitRunner.class)
public class AccountValidatorTest {

    private UniqueEmailValidator uniqueEmailValidator;

    @Mock
    private AccountService accountService;

    @Before
    public void setUp() {
        this.uniqueEmailValidator = new UniqueEmailValidator(this.accountService);
    }

    @Test
    public void shouldDetectDuplicatedEmailAddress() {

        // create user object with email "hello@world.com"
        when(accountService.findByEmail(Mockito.anyString())).thenReturn(user);

        boolean violations = uniqueEmailValidator.isValid("hello@world.com", null);

        assertFalse(violations);
    }

    @Test
    public void shouldNotDetectDuplicatedEmailAddress() {
        when(accountService.findByEmail(Mockito.anyString())).thenReturn(Optional.empty());

        boolean violations = uniqueEmailValidator.isValid("hello@world.com", null);

        assertTrue(violations);
    }
}
0 голосов
/ 01 мая 2018

1) Если вы хотите написать модульный тест для класса валидатора, изолируйте зависимости, высмеяв их все.

У вас есть два способа:

  • a) Введите боб UniqueEmailValidator, например:

    @Autowired UniqueEmailValidator UniqueEmailValidator;

И используйте фальшивый фреймворк (отлично подходит Mockito) для насмешки над зависимостью accountService.

  • b) Создайте оператор UniqueEmailValidator с new и замените пружинный держатель на бегунок mockito.
    Это ускорит выполнение теста.

2) Если вы хотите написать интеграционный тест , учтите, что аннотация @DataJpaTest, используемая в вашем тестовом классе, ограничивает Spring для загрузки ограниченного контекста, содержащего в основном компоненты JPA.
@DataJpaTest состояния:

Аннотация, которую можно использовать в сочетании с @RunWith (SpringRunner.class) для типичного теста JPA. Может использоваться, когда тест фокусируется только на компонентах JPA.

И ваш сервис не является компонентом JPA, поэтому зависимость не передается пружиной в bean-компоненте Validator.

Таким образом, либо @Autowired служба и средство проверки и установите службу на validator, либо упростите задачу: используйте @SpringBootTest вместо @DataJpaTest.

...