测试用过的bean代替模拟对象

时间:2017-12-06 14:39:47

标签: java spring junit mockito spring-test

我开始使用SpringMockito。 我使用JUnitMockitoSpring boot。 我测试了控制器,并尝试使其工作。

@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = CommonConfiguration.class)
@ActiveProfiles(profiles = "embedded")
public class UserController_IntegrationWContainer_Tests {

private MockMvc mockMvc;

@Autowired
private WebApplicationContext wac;

@MockBean(name = "SignatureValidator")
private SignatureValidator signatureValidator;

private ObjectMapper objectMapper;
...

@Before
public void before() {
    MockitoAnnotations.initMocks(this);
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).dispatchOptions(true).build();
    this.objectMapper = new ObjectMapper();
}

@Test
public void createConfirmation() throws Exception {

    ...

    ConfirmationDetails signature = new ConfirmationDetails("signature");
    when(this.signatureValidator.validateAndEnforceSignature(signature))
            .thenReturn("advanced");

    mockMvc.perform(post("/distributions/1/receive")
            .content(this.objectMapper.writeValueAsBytes(signature))
            .contentType(MediaType.APPLICATION_JSON)
    .andExpect(status().is(201));
}

此测试类用于测试UserController

@RestController
@Validated
@Transactional
public class UserController {

    private final static Logger logger = LoggerFactory.getLogger(UserController.class);
    private UserService userService;
    private SignatureValidator signatureValidator;
    ...

    @Autowired
    public UserController(UserService userService, SignatureValidator signatureValidator, ...) {
        this.userService = userService;
        this.signatureValidator = signatureValidator;
        ...
    }  
    @RequestMapping(
            value = "/distributions/{distributionId}/receive",
            method = RequestMethod.POST,
            produces = "application/json")
    public ResponseEntity<Object> confirmReceipt(
            @RequestBody ConfirmationDetails signature,
            @Context HttpServletRequest request,
            @Digits(fraction = 0, integer = 19, message = "ID cannot exceed 19 digits")
            @Min(value = 1, message = "ID must be > 0")
            @PathVariable("distributionId") Long distributionId) {

        logger.info("Processing request confirmReceipt");

        /*for this string I want to use stub. but stub doesn't work*/
        String advancedSignature = signatureValidator.validateAndEnforceSignature(signature);

        ...

        Map<String, String> resultsMap = new HashMap<>();
        resultsMap.put("code", "RECEIPT_SIGNED");
        resultsMap.put("message", "Distribution download confirmed");

        return new ResponseEntity<>(resultsMap, HttpStatus.CREATED);
    }    
}

SignatureValidator界面:

public interface SignatureValidator {

    @SuppressWarnings("unchecked")
    String validateAndEnforceSignature(ConfirmationDetails signature);
}

SignatureValidator的两个实现:

@Component
public class SignatureValidatorIml extends WebServiceGatewaySupport implements SignatureValidator {
...
}

@Component
public class SignatureValidatorJinn extends WebServiceGatewaySupport implements SignatureValidator {
...
}

Spring决定使用JinnServerConfiguration注入bean的实现:

@Configuration
public class JinnServerConfiguration {

    final Environment env;

    @Autowired
    public JinnServerConfiguration(Environment env) {
        this.env = env;
    }

    @Bean
    public Jaxb2Marshaller marshaller() {
        ...
        return marshaller;
    }

    @Bean
    public HttpComponentsMessageSender messageSender() {
        ...
        return messageSender;
    }

    @Bean
    @Profile("!local")
    public SignatureValidator signatureValidator(Jaxb2Marshaller marshaller,
                                                 HttpComponentsMessageSender messageSender) {
        SignatureValidatorJinn signatureValidatorJinn = new SignatureValidatorJinn(env);
        signatureValidatorJinn.setDefaultUri("http://.../SignatureValidationService");
        signatureValidatorJinn.setMarshaller(marshaller);
        signatureValidatorJinn.setUnmarshaller(marshaller);
        signatureValidatorJinn.setMessageSender(messageSender);
        return signatureValidatorJinn;
    }

    @Bean
    @Profile("local")
    public SignatureValidator signatureValidatorLocal(){
        return new SignatureValidatorIml();
    }

}

对于UserController中的所有服务,模拟工作正常。 但是,当我想模仿SignatureValidator时,它不起作用。 在调试模式中,我看到,在测试中它使用了模拟bean,但是然后UserController运行它使用Spring bean(SignatureValidatorJinn)而不是模拟bean。

为什么使用SignatureValidatorJinn?我认为是因为测试类有注释:@ActiveProfiles(profiles =“embedded”)。根据JinnServerConfiguration“@Profile(”!local“)”spring做出了这个决定。

此测试工作正常,直到注释@Component已添加到SignatureValidatorIml班级和SignatureValidatorJinn班级。

我无法理解,为什么它使用了Spring bean。因为其他服务模拟工作正常。我发现了很多关于mock的信息,但找不到解决这个问题的方法。

我尝试做下一件事,但他们没有帮助:

  • 不要使用MockitoAnnotations.initMocks(this);
  • 为SignatureValidator限定当前bean:Jinn或Iml;
  • 使用@InjectMocks UserController userController;

修改

如果我在SignatureValidatorJinn和SignatureValidatorIml中删除@Component注释,它会很好用。但我不明白为什么会这样。

例如,使用“@Service”注释的UserService类。它成功地嘲笑了。

所以,也许有人可以解释一下,为什么“@Component”在这种情况下不允许创建模拟对象?

EDIT2

问题已解决

我无法删除注释组件,因为没有这个注释,我的应用程序中的DI将无法正常工作。 我找到了决定。 Bean称为signatureValidator。我使用名称SignatureValidator来指定bean来模拟。 那是错误的)所以跟随构造它工作正常:

@MockBean(name = "signatureValidator") 
private SignatureValidator signatureValidator;

感谢大家的回复:)

2 个答案:

答案 0 :(得分:0)

你试图模仿SignatureValidator bean。但是你有两个bean SignatureValidatorJinnSignatureValidatorIml。你的控制器使用两个bean。 尝试模拟SignatureValidatorJinn和SignatureValidatorIml之一。

@MockBean(name = "SignatureValidatorJinn")
private SignatureValidator signatureValidatorJinn;

@MockBean(name = "SignatureValidatorIml")
private SignatureValidator signatureValidatorIml;

用于post("/distributions/1/receive")请求。

答案 1 :(得分:0)

你不能打电话

MockitoAnnotations.initMocks(this);

因为SpringRunner已经初始化了模拟。通过调用initMocks,您可以为signatureValidator分配一个新对象。所有Mockito操作(例如whenverify)都在新对象上执行,但Spring仍然使用由SignatureValidator创建的“旧”SpringRunner