【问题标题】:How to testing Spring validator with Junit and Mockito如何使用 Junit 和 Mockito 测试 Spring 验证器
【发布时间】:2015-09-27 08:44:30
【问题描述】:

我有一个 Spring 验证器:

@Component
public class AddClientAccountValidator implements Validator {

    @Autowired
    private ValidatorUtils validatorUtils;

    @Override
    public boolean supports(Class<?> clazz) {
        return UserDto.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        UserDto user = (UserDto) target;
        validatorUtils.setParam(errors, user);

        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "username.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "password.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "confirmPassword",
                "confirmPassword.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "firstName.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "lastName.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "personalId", "personalId.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "city", "city.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address", "address.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "email.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "phone", "phone.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "contribution", "contribution.required");

        validatorUtils.validateAddClientAccount();
    }
}

在上面的 Validator 已经注入到 ValidatorUtils 类:

@Component
class ValidatorUtils {

    @Autowired
    private PersonalIdValidator personalIdValidator;
    private Errors errors;
    private UserDto user;

    void setParam(Errors errors, UserDto user) {
        this.errors = errors;
        this.user = user;
    }

    void validateAddClientAccount() {
        validateAccount();
        validContributionAccount();
    }

    private void validateAccount() {
        validUsername();
        validPassword();
        validFirstName();
        validLastName();
        validPersonalId();
        validCity();
        validAddress();
        validEmail();
        validPhone();
    }
    public void validateChangePassword() {
        validChangePassword();
    }

    private void validUsername() {
        if (!user.getUsername().isEmpty()) {
            String username = user.getUsername();
            if ((username.length() < 3) || (username.length() > 40)) {
                errors.rejectValue("username", "username.format");
            }
        }
    }

    private void validPassword() {
        if (!user.getPassword().isEmpty()) {
            String password = user.getPassword();
            if ((password.length() < 3) || (password.length() > 40)) {
                errors.rejectValue("password", "password.format");
            }
            if (!password.equals(user.getConfirmPassword())) {
                errors.rejectValue("confirmPassword", "password.confirm");
            }
        }
    }

    private void validFirstName() {
        if (!user.getFirstName().isEmpty()) {
            String firstName = user.getFirstName();
            String regex = "[A-ZŁ{1}]+[a-zł]+";
            boolean validFirstName = Pattern.matches(regex, firstName);
            if ((firstName.length() < 3) || (firstName.length() > 40) || !validFirstName) {
                errors.rejectValue("firstName", "firstName.format");
            }
        }
    }

    private void validLastName() {
        if (!user.getLastName().isEmpty()) {
            String lastName = user.getLastName();
            String regex = "[A-ZĆŁŚŻŹ{1}]+[a-ząćęłńóśżź]+";
            String regexWithTwoLastName = "[A-ZĆŁŚŻŹ{1}]+[a-ząćęłńóśżź]++[\\s]+[A-ZĆŁŚŻŹ{1}]+[a-ząćęłńóśżź]+";
            boolean validLastName = Pattern.matches(regex, lastName);
            boolean validWithTwoLastName = Pattern.matches(regexWithTwoLastName, lastName);
            if ((lastName.length() < 3) || (lastName.length() > 40)
                    || (!validLastName && !validWithTwoLastName)) {
                errors.rejectValue("lastName", "lastName.format");
            }
        }
    }

这个类有更多的字段验证器,但我跳过了它。

我想使用 Junit 或最终使用 mockito 测试我的 Validator 类。我写了这个测试类:

    @RunWith(MockitoJUnitRunner.class)
public class AddClientAccValidatorTest {

    @InjectMocks
    private AddClientAccountValidator validator;
    @Mock
    private ValidatorUtils validatoUtils;
    private UserDto userDto;
    public Errors errors;

    @Before
    public void setUp() {
        UserDto userDto = new UserDto();
        errors = new BeanPropertyBindingResult(userDto, "userDto");
    }

    @Test
    public void testValidate() {
        validator.validate(userDto, errors);
        assertFalse(errors.hasErrors());
    }

}

但是当我运行我的测试时,我得到以下 Failet 跟踪:

java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertFalse(Assert.java:64)
at org.junit.Assert.assertFalse(Assert.java:74)
at pl.piotr.ibank.validator.AddClientAccValidatorTest.testValidate(AddClientAccValidatorTest.java:67)
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.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
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.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

为什么会出现此错误?我在这一行得到错误:

errors = new BeanPropertyBindingResult(userDto, "userDto");

我的第二个问题是我不能声明多个 RunWith 注释。当我添加:

@RunWith(MockitoJUnitRunner.class)

我无法使用 @RunWith(Parameterized.class) 参数化我的测试

如何解决?

有人可以帮助我吗?也许我的方法不好?使用 Junit 测试 Spring Validator 的最佳方法是什么?

【问题讨论】:

  • 在您的测试中,您没有向ValidatorUtils validatoUtils 添加任何行为,因此注入AddClientAccountValidator validator 的模拟将基本上是无操作的。此外,UserDto userDto 将被初始化为null,因此它绝对不会通过您定义的任何验证规则。阅读 Mockito 文档将是一个明显的起点。请注意,堆栈跟踪包含有用的信息:AddClientAccValidatorTest.java:67。请记住提及问题中的哪一行,因为它目前并不明显(甚至包括在内)
  • 对不起,我粘贴了错误的代码。 UserDto被初始化,第67行错误为:errors = new BeanPropertyBindingResult(userDto, "userDto");

标签: spring testing junit mockito


【解决方案1】:

您可以在没有 Mockito 的情况下成功运行测试。以下代码适用于 Spring @Configuration 类(需要 spring-test 作为依赖项):

package foo.bar;

import static org.junit.Assert.assertFalse;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class AddClientAccValidatorTest {

    @Configuration
    static class ContextConfiguration {

        @Bean
        public AddClientAccountValidator validator() {
            return new AddClientAccountValidator();
        }

        @Bean
        public ValidatorUtils validatorUtils() {
            return new ValidatorUtils();
        }
    }

    @Autowired
    private AddClientAccountValidator validator;
    private UserDto userDto;
    public Errors errors;

    @Before
    public void setUp() {
        userDto = new UserDto();
        userDto.setLastName("Doe");
        userDto.setFirstName("John");
        userDto.setUsername("username");
        userDto.setPhone("phone");
        userDto.setPassword("password");
        userDto.setConfirmedPassword("password");
        userDto.setEmail("email");
        userDto.setContribution("contribution");
        userDto.setAddress("address");
        userDto.setCity("city");
        userDto.setPersonalId("personalId");
        errors = new BeanPropertyBindingResult(userDto, "userDto");
    }

    @Test
    public void testValidate() {
        validator.validate(userDto, errors);
        assertFalse(errors.hasErrors());
    }
}

【讨论】:

  • 您还可以查看 JSR-303 以及使用属性注释进行验证。
【解决方案2】:

你不需要 Mock 任何东西。在验证时,我们需要我们想要验证的对象和错误。创建一个对象,其中包含要验证的必填字段和错误对象。例如,

@Test
public void shouldReturnErrorsWhenCustomObjectIsNull() {
    CustomValidator customValidator = new CustomValidator();

    Employee employee = new Employee();
    employee.setEmployeeFirstname("empName")


    Errors errors = new BeanPropertyBindingResult(employee, "employee");
    customValidator.validate(employee, errors);
    List<ObjectError> allErrors = errors.getAllErrors();
    assertTrue("Errors list size should not be null : ", allErrors.size() > 0);
    assertTrue(errors.hasErrors());
    assertNotNull( errors.getFieldError("empName") );
}

【讨论】:

    猜你喜欢
    • 2018-12-23
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多