I'm doing unit test using spring mvc test framework.
The following is my source code:
com.exmple.main
MyController.java
#Controller
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public Map<Object, Object> myControllerFunction(#RequestBody final Object jsonRequest) {
/* do something */
return response;
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<My, String> {
#Query(value="select * from my d where (d.start_date<to_date(:date,'YYYY/DD/MM')) and (d.end_date>to_date(:date,'YYYY/DD/MM'))", nativeQuery=true)
List<My> findByDate(#Param("date") String date);
}
MyService.java
public interface MyService {
List<My> findByDate(String date);
}
MyServiceImpl.java
#Service
public class MyServiceImpl implements MyService {
#Autowired
MyRepository destRepo;
#Override
public List<My> findByDate(String date) {
List<My> listDest = destRepo.findByDate(date);
return listDest;
}
}
com.example.test
MyControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
MyService myService;
#Autowired
protected WebApplicationContext webApplicationContext;
#Before
public void setup() throws Exception {
// this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void listAllMy() throws Exception {
}
}
TestConfig.java
#Configuration
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new MyServiceImpl();
}
}
When I run test, the following error is displayed
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
I know the exception occurred because MyService didn't find any bean of MyRepository.
But I don't know how to create a bean of repository.
Please teach me how to create a bean of repository class using Java (not xml).
You need to enable the JPA repositories in your config class, specify the package that contains the repositories as below
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.repository"
})
public class TestConfig {
#Bean
public MyService myService() {
// set properties, etc.
return new DestinationServiceImpl();
}
}
Edit: looks like you haven't defined entityManager, and dataSource. Refer to a tutorial here and also answer to similar question here
Related
Factory injection in Spring
//XML
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
//Java
How do you do dependency injection with only interfaces and factory classes and no configuration classes ?
//Service
public interface MyService {
void doSomething();
}
//ServiceFactory
public class MyServiceFactory {
MyService getInstance() {
//...
}
}
//Controller
#Controller
public class MyController {
#Autowired
private MyService myService;
}
you can do like this without adding the xml coding.
#Configuration
public class MyServiceFactory {
#Bean
MyService myService() {
return new MyService() {
#Override
public void doSomething() {
}
}
}
}
#Controller
public class MyController {
#Autowired
private MyService myService;
public void execute() {
myService.doSomething();
}
}
You can manually ask Spring to Autowire it.
Have your factory implement ApplicationContextAware. Then provide the following implementation in your factory:#Override public void setApplicationContext(final ApplicationContext applicationContext) { this.applicationContext = applicationContext; }
and then do the following after creating your bean:
YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.
I just migrated an application from Spring Boot 1.x to 2.1. One of my test is failing due to a change to bean overriding default
I tried to set spring.main.allow-bean-definition-overriding to true but it's not working.
You can reproduce the issue with the following classes:
#Configuration
public class ClockConfig {
#Bean
public Clock clock() {
return Clock.systemUTC();
}
}
#Service
public class MyService {
private final Clock clock;
public MyService(Clock clock) {
this.clock = clock;
}
public Instant now() {
return clock.instant();
}
}
#RestController
public class MyResource {
private final MyService myService;
public MyResource(MyService myService) {
this.myService = myService;
}
#GetMapping
public ResponseEntity<Instant> now() {
return ResponseEntity.ok(myService.now());
}
}
The failing test. The clock() method is never called with Spring Boot 2.1 whereas it was with Spring Boot 1.5 or Spring Boot 2.0.
#RunWith(SpringRunner.class)
#WebMvcTest(MyResource.class)
#ContextConfiguration(classes = MyService.class)
public class ResourceTest {
#Autowired
private MockMvc mvc;
#Test
public void test() {
}
#TestConfiguration
static class TestConfig {
#Bean
public Clock clock() {
return Clock.fixed(Instant.MIN, ZoneId.of("Z"));
}
}
}
Try to modify ContextConfiguration annotation. It should be: #ContextConfiguration(classes = {MyService.class,ClockConfig.class}) .
You explicitly specified which config you would like to import in your test with #ContextConfiguration annotation so your #TestConfiguration is not loaded at all. If you exclude your #ContextConfiguration it can work. Since you removed configuration of MyService, you have to provide MyService bean in your test config. Try this:
#RunWith(SpringRunner.class)
#WebMvcTest(MyResource.class)
public class DemoApplicationTests {
#Autowired
private MockMvc mvc;
#Test
public void test() {
}
#TestConfiguration
static class TestConfig {
#Bean
public Clock clock() {
return Clock.fixed(Instant.MIN, ZoneId.of("Z"));
}
#Bean
public MyService service() {
return new MyService(clock());
}
}
}
Love Spring Testing Even More With Mocking and Unit Test Assistant:
A mocked service replaces multiple dependencies
enter image description here
#Controller
#RequestMapping("/people")
public class PeopleController {
#Autowired
protected PersonService personService;
#GetMapping
public ModelAndView people(Model model) {
for (Person person: personService.getAllPeople()) {
model.addAttribute(person.getName(), person.getAge());
}
return new ModelAndView("people.jsp", model.asMap());
}
}
private MockMvc mockMvc:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
private MockMvc mockMvc;
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
I get a mistake when I want to run mockMvc
java.lang.NullPointerException
Perform the following steps:
create service mock instead of service original
("PersonServiceMock")
replace service original by service mock
#Autowired
PersonService personService;
#Autowired
PeopleController peopleController;
private MockMvc mockMvc;
#Before
public void setup() {
peopleController = new PeopleController(new personServiceMock());
mvc = MockMvcBuilders.standaloneSetup(peopleController).build();
}
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
That's because you are never initialising mockMvc in your code and the point where you access it results in nullPointerException. You need to initialise it before using it, and since multiple tests in your class could be using it, best place to do it is setup() method annotated with #before. Try below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
from the source code I see that the mockMvc doesn't have any value, thats why it hits "java.lang.NullPointerException" for this line of code :
ResultActions actions = mockMvc.perform(get("/people"));
to make it run, I think need to give value to mockMvc first.
by constructor :
#Test
public void testPeople() throws Exception {
mockMvc = new MockMvc();
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
or Autowired :
#Autowired
MockMvc mockMvc
depends on the purpose of MockMvc Class
Hello everyone I wanted to tested the full validation of a Request in my Spring Boot application I mean no testing one validator at a time but all of them on the target object)
First I have my object :
public class UserCreationRequest {
#JsonProperty("profileId")
#NotNull
#ValidProfile
private Integer profileId;
}
Then my Validator (#ValidProfile):
#Component
public class ProfileValidator implements ConstraintValidator<ValidProfile, Integer> {
#Autowired
private IProfileService profileService;
#Autowired
private IUserRestService userRestService;
#Override
public void initialize(ValidProfile constraintAnnotation) {
}
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
RestUser restUser = userRestService.getRestUser();
ProfileEntity profileEntity = profileService.getProfile(value, restUser.getAccountId());
return profileEntity != null;
}
}
Now I write my unit test :
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {ValidationTestConfiguration.class})
public class UserCreationRequestValidationTest {
private static LocalValidatorFactoryBean localValidatorFactory;
#Autowired
private IUserService userService;
#Autowired
private IProfileService profileService;
#Autowired
private IUserRestService restService;
#BeforeClass
public static void createValidator() {
localValidatorFactory = new LocalValidatorFactoryBean();
localValidatorFactory.setProviderClass(HibernateValidator.class);
localValidatorFactory.afterPropertiesSet();
}
#AfterClass
public static void close() {
localValidatorFactory.close();
}
#Test
public void validateUserCreationRequestStringfields() {
UserCreationRequest userCreationRequest = new UserCreationRequest();
/* Here fill test object*/
when(userService.getUser(any(Integer.class), any(Integer.class))).thenReturn(new UserEntity());
when(profileService.getProfile(any(Integer.class), any(Integer.class))).thenReturn(new ProfileEntity());
when(restService.getRestUser()).thenReturn(new RestUser());
Set<ConstraintViolation<UserCreationRequest>> violations
= localValidatorFactory.validate(userCreationRequest);
assertEquals(violations.size(), 8);
}
}
and my TestConfiguration is like that :
#Configuration
public class ValidationTestConfiguration {
#Bean
#Primary
public IProfileService profileService() {
return Mockito.mock(IProfileService.class);
}
#Bean
#Primary
public IUserRestService userRestService() { return Mockito.mock(IUserRestService.class); }
}
On execution I can see that in the test itself the injection works :
restService is mapped to "Mock for IUserRestService"
But in my validator it is not injected, userRestService is null.
Same thing for ProfileService
I tried several things seen here, nothing works (code is running, only test conf is failing)
This is because you do not produce the Validator bean so it can be injected.
As you manually instantiate the LocalValidatorFactoryBean, it cannot access to the spring DI defined for this test.
You should produce instead a bean for the Validator, or even reference an existing spring configuration to do so.
I have a rest resource for signup and login. both in a controller class. the controller class has a dependency to a service class with the business logic. the service class has further dependencies. cause i use an embedded db for testing, i want to use the real dependencies of my app instead to mock them with something like #injectmock #mock. there is only one certain dependency i have to mock. its the dependency for sending emails after a signup process. how to write test cases with #autowired function and one certain mock dependency for email notification?
#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());
}
}
I hope you see the problem. Every dependency works fine instead mailservice. I dont want to use #injectmock and #mock with MockitoAnnotations.initMocks(this); in my test file, because of the neccessary to provide for all dependencies mocks.
if your dependencies are running and you have a configuration class where you have defined the endpoint, you can use ConfigurableApplicationContext class, something like this:
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);
}
}
Or you can re-write your endpoint with a configuration class and you can use WireMock (http://wiremock.org) to emulate your dependency with real data, this should be something like this:
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);
}});
}};
}
}
}
I hope this help you.
What you are trying to do is easily implemented using Spring Profiles.
On way to achieve it is the following:
#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);
}
}
Your test class would then look like:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#Transactional
#TransactionConfiguration(defaultRollback = true)
#ActiveProfiles("mockMail")
public class LogoutTest {
//do your testing
}
Note the use of #Primary in MockMailServiceConfig. I opted for this way since it wouldn't require you to introduce profiles anywhere else if you are not already using them. #Primary tells spring to use that specific bean if multiple candidates are available (in this case there is the real mail service and the mock service)