我有注册和登录的休息资源。都在控制器类中。控制器类依赖于具有业务逻辑的服务类。服务类有更多的依赖项。因为我使用嵌入式数据库进行测试,所以我想使用我的应用程序的真正依赖关系,而不是用@injectmock @mock之类的东西来模拟它们。只有一种依赖我不得不嘲笑。它是在注册过程后发送电子邮件的依赖项。如何编写测试用例与@autowired功能和一个特定的模拟依赖于电子邮件通知?
@Controller
public class AccountCommandsController {
@Autowired
private LogoutService service;
@RequestMapping(value = "/rest/login", method = RequestMethod.POST)
public ResponseEntity login(@RequestBody Account account) {
AccountLoginEvent accountLoginEvent = service.loginAccount(new RequestAccountLoginEvent(account.getEmailAddress(), account.getPassword()));
if (accountLoginEvent.isLoginGranted()) {
return new ResponseEntity(HttpStatus.ACCEPTED);
} else {
return new ResponseEntity(HttpStatus.UNAUTHORIZED);
}
}
@RequestMapping(value = "/rest/signup", method = RequestMethod.POST)
public ResponseEntity signup(@RequestBody Account account) {
AccountSignupEvent signedupEvent = service.signupAccount(new RequestAccountSignupEvent(account.getEmailAddress(), account.getPassword()));
if (signedupEvent.isSignupSuccess()) {
return new ResponseEntity(HttpStatus.ACCEPTED);
} else if (signedupEvent.isDuplicateEmailAddress()) {
return new ResponseEntity(HttpStatus.CONFLICT);
} else if (signedupEvent.isNoSignupMailSent()) {
return new ResponseEntity(HttpStatus.SERVICE_UNAVAILABLE);
} else {
return new ResponseEntity(HttpStatus.FORBIDDEN);
}
}
}
@Service
public class LogoutService {
@Autowired
private AccountsRepository accountsRepository;
@Autowired
private MailService mailService;
@Autowired
private HashService hashService;
public AccountSignupEvent signupAccount(RequestAccountSignupEvent signupEvent) {
if (accountsRepository.existEmailAddress(signupEvent.getEmailAddress())) {
return AccountSignupEvent.duplicateEmailAddress();
}
Account newAccount = new Account();
newAccount.setCreated(new Date());
newAccount.setModified(new Date());
newAccount.setEmailAddress(signupEvent.getEmailAddress());
newAccount.setPassword(signupEvent.getPassword());
newAccount.setVerificationHash(hashService.getUniqueVerificationHash());
SignupMailEvent mailSentEvent = mailService.sendSignupMail(new RequestSignupMailEvent(newAccount));
if (!mailSentEvent.isMailSent()) {
return AccountSignupEvent.noMailSent();
}
Account persistedAccount = accountsRepository.persist(newAccount);
return AccountSignupEvent.accountCreated(persistedAccount);
}
public AccountLoginEvent loginAccount(RequestAccountLoginEvent loginEvent) {
if (accountsRepository.existLogin(loginEvent.getEmailAddress(), loginEvent.getPassword())) {
return AccountLoginEvent.granted();
}
return AccountLoginEvent.denied();
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class LogoutTest {
private MockMvc mockMvc;
@Autowired
private AccountCommandsController controller;
@Before
public void setup() {
mockMvc = standaloneSetup(controller).build();
}
@Test
public void signupNoMail() throws Exception {
doReturn(AccountSignupEvent.noMailSent()).when(service).signupAccount(any(RequestAccountSignupEvent.class));
// when(controller.service.signupAccount(any(RequestAccountSignupEvent.class))).thenReturn(AccountSignupEvent.noMailSent());
mockMvc.perform(post("/rest/signup")
.content(new Gson().toJson(new Account(UUID.randomUUID().toString(), UUID.randomUUID().toString())))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isServiceUnavailable());
}
}
我希望你能看到问题所在。每个依赖项都工作得很好,而不是mailservice。我不想使用@injectmock和@mock与MockitoAnnotations.initMocks(this);在我的测试文件中,由于需要为所有依赖项提供模拟。
如果你的依赖正在运行,并且你有一个定义了端点的配置类,你可以使用ConfigurableApplicationContext类,像这样:
public class test {
private static ConfigurableApplicationContext appContext;
private LogoutService service;
@AfterClass
public static void destroy() {
appContext.close();
}
@Before
public void setup() {
appContext = new AnnotationConfigApplicationContext(YourClassConfig.class);
service = appContext.getBean(LogoutService.class);
}
@Test
public void beansAreCreated() {
assertNotNull(service);
}
}
或者你可以用一个配置类重写你的端点,你可以使用WireMock (http://wiremock.org)用真实的数据模拟你的依赖,这应该是这样的:
public class test {
@Rule
public WireMockRule wireMockRule = new WireMockRule(15000);
private static ConfigurableApplicationContext appContext;
private LogoutService service;
private static String serviceMockUrl;
@AfterClass
public static void destroy() {
appContext.close();
}
@Before
public void setup() {
serviceMockUrl = "http://localhost:" + wireMockRule.port();
appContext = new AnnotationConfigApplicationContext(TestConfig.class);
stubFor(get(urlEqualTo("urlToRequest")).
willReturn(aResponse().
withStatus(SC_OK).
withBody(createJsonArray("MapWithYourData").
withHeader("Content-Type", "application/json")));
service = appContext.getBean(LogoutService.class);
}
@Test
public void beansAreCreated() {
assertNotNull(service);
}
@Configuration
static class TestConfig {
@Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertyPlaceholderConfigurer() {{
setProperties(new Properties() {{
setProperty("service.url", serviceMockUrl);
}});
}};
}
}
}
你想做的是很容易实现使用Spring配置文件。
实现它的方法如下:
@Configuration
public class TestConfiguration {
//this is the real mail service
@Bean
public MailService mailService() {
return new MailService(); //or whatever other bean creation logic you are using
}
//whatever else
}
@Configuration
@Profile("mockMail")
public class MockMailServiceConfig {
@Bean
@Primary
public MailService mockMailService() {
return mock(MailService.class);
}
}
你的测试类看起来像:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@Transactional
@TransactionConfiguration(defaultRollback = true)
@ActiveProfiles("mockMail")
public class LogoutTest {
//do your testing
}
注意MockMailServiceConfig
中@Primary
的使用。我之所以选择这种方式,是因为它不需要您在其他地方介绍配置文件,如果您还没有使用它们的话。@Primary
告诉spring,如果有多个可用的候选对象(在本例中有真实邮件服务和模拟服务),则使用特定的bean