NullPointer Exception everytime I try to run my Junit Test Case - spring

Every time i try to run the junit test case , i get null pointer exception and also when i tried to hadle the exception everytime the function returns NULL value. Here is the code.
RestaurantRepository.java
package org.springsource.restbucks;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
#Component
#Transactional
public interface RestaurantRepository extends PagingAndSortingRepository<Restaurant, Long> {
#Query("SELECT p FROM Restaurant p Where (p.id)=(:Id) and p.deleted=false")
Restaurant findById(#Param("Id") long Id);
#Query("SELECT p FROM Restaurant p Where LOWER(p.name)=LOWER(:name) and p.deleted = false")
Restaurant findByName(#Param("name") String name);
}
RestaurantController.java
#RestController
public class RestaurantController {
#Autowired
RestaurantRepository repository;
#RequestMapping(value = "/restaurants/{id}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public ResponseEntity<Restaurant> getRestaurant(#PathVariable("id") long restaurantId) {
Restaurant responseRestaurant = repository.findOne(restaurantId);
if(responseRestaurant==null){
logger.error("No Restaurant found with id:"+restaurantId);
return new ResponseEntity<Restaurant>(HttpStatus.NOT_FOUND);
}else if(responseRestaurant.isDeleted()==true){
logger.error("Restaurant Object is deleted");
return new ResponseEntity<Restaurant>(HttpStatus.NOT_FOUND);
}else{
return new ResponseEntity<Restaurant>(responseRestaurant,HttpStatus.OK);
}
}
}
RestaurantRepositoryTest.java
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class RestaurantRepositoryTest{
#Autowired
private RestaurantRepository repository;
#Test
public void testfindById() throws Exception{ //testing findbyid method in restaurant repository
Restaurant responseRestaurant = repository.findByName("xyz"); //getting error over here
assertEquals("xyz",responseRestaurant.getName());
}
}
I am getting null pointer exception whenever i run mvn test . the stack trace is below what i get displayed in terminal.
testfindById(org.springsource.restbucks.RestaurantRepositoryTest)
Time elapsed: 0.018 sec <<< ERROR! java.lang.NullPointerException:
null at
org.springsource.restbucks.RestaurantRepositoryTest.testfindById(RestaurantRepositoryTest.java:19)
what shall i do to run my test successfully??

You're running that as a pure unit test with no Spring context. Therefore, the repository will not be autowired. You should be able to fix that by adding a few annotations to the top of your test class.
If using Spring Boot, then something like the following should work, to let Spring Boot do all the hard work:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
You can alternatively load the Spring #Configuration class which would initialise your repositories (In my case, this is called DbConfig), like so:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { DbConfig.class },
loader = AnnotationConfigContextLoader.class)

Your test is not starting up a Spring context at all, so the annotations are completely ignored. The best solution is to modify your controller class to take the repository as a constructor argument instead of using field injection. This allows you to pass in a simple mock, such as a Mockito mock, and avoid the complexity of starting up a Spring context to run a unit test.

Related

Got LazyInitializationException in jupiter #BeforeAll method

In my integration tests I would like to populate my system with some data in advance that I can rely in my test cases, with #BeforeAll (see below).
package com.ksteindl.chemstore.service;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.transaction.Transactional;
//some other import of my own stuff
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
public class ShelfLifeServiceTest extends BaseControllerTest {
#Autowired
private ShelfLifeService shelfLifeService;
#Autowired
private ChemTypeService chemTypeService;
#BeforeAll
static void setUpTestDb(#Autowired ShelfLifeService shelfLifeService,#Autowired ChemTypeService chemTypeService) {
ShelfLifeInput input = LabAdminTestUtils.getSolidForAlphaInput();
Long chemTypeId = chemTypeService.getChemTypes().stream()
.filter(chemType -> chemType.getName().equals(LabAdminTestUtils.SOLID_COMPOUND_NAME))
.findAny()
.get()
.getId();
input.setChemTypeId(chemTypeId);
shelfLifeService.createShelfLife(input, AccountManagerTestUtils.BETA_LAB_MANAGER_PRINCIPAL);
}
#Test
#Rollback
#Transactional
public void testCreateShelfLife_whenAllValid_gotNoException() {
ShelfLifeInput input = LabAdminTestUtils.getSolidForBetaInput();
Long chemTypeId = chemTypeService.getChemTypes().stream()
.filter(chemType -> chemType.getName().equals(LabAdminTestUtils.SOLID_COMPOUND_NAME))
.findAny()
.get()
.getId();
input.setChemTypeId(chemTypeId);
shelfLifeService.createShelfLife(input, AccountManagerTestUtils.BETA_LAB_MANAGER_PRINCIPAL);
}
}
My problem is when I run tests, I got a LazyInicializtionException thrown from #BeforeAll method
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.ksteindl.chemstore.domain.entities.Lab.labManagers, could not initialize proxy - no Session
...
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at com.ksteindl.chemstore.service.ShelfLifeService.getAndValidateLab(ShelfLifeService.java:92)
at com.ksteindl.chemstore.service.ShelfLifeService.createOrUpdateShelfLife(ShelfLifeService.java:65)
at com.ksteindl.chemstore.service.ShelfLifeService.createShelfLife(ShelfLifeService.java:47)
at com.ksteindl.chemstore.service.ShelfLifeServiceTest.setUpTestDb(ShelfLifeServiceTest.java:39)
The funny thing is when I comment out the #BeforeAll method, my test runs as it should be without any Exception. As you can see, the content of the two methods are almost identical (only the input differs to prevent conflict each other).
The exact code that is throwing the Exception is something like this:
private Lab getAndValidateLab(String labKey, Principal principal) {
Lab lab = labService.findLabByKey(labKey);
lab.getLabManagers().stream().filter(/*some predicate*/).findAny().orElseThrow(/*throw validation Exception*/);
return lab;
}
That is true, that the relation between Lab and labManagers property is Lazy, but I can't understand why Spring doesn't get those data in one transaction, just like in testCreateShelfLife_whenAllValid_gotNoException. Plus of course ShelfLifeService.createShelfLife() works perfectly fine from the Rest Controller call. Thanks you for your help!
The Entities:
#Entity
#Data
public class Lab {
//...
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "MANAGER_OF_LAB_TABLE", joinColumns = #JoinColumn(name = "LAB_ID"), inverseJoinColumns = #JoinColumn(name = "APP_USER_ID"))
#JsonIgnore
private List<AppUser> labManagers;
}
#Entity
#Data
public class AppUser {
//...
#ManyToMany(mappedBy = "labManagers")
private List<Lab> managedLabs;
}
Change #BeforeAll (which runs before the Spring container is initialized) to #BeforeEach (which will be run before each test, after the Spring container is initialized), but after the test and Spring container has been built.
I think chemTypeService is being executed in #BeforeAll before the Spring container has been fully built. - BIT OF A GUESS
Look at #DirtiesContext also to re-build the Spring context before each test.
See this tutorial
Another, a bit hacky, solution is create your own class to load the test data after the spring container is initialised:
#Service
#Profile("test")
public class TestInit {
#Autowired
private ShelfLifeService shelfLifeService;
#Autowired
private ChemTypeService chemTypeService;
#PostConstruct
private void init() {
ShelfLifeInput input = LabAdminTestUtils.getSolidForAlphaInput();
Long chemTypeId = chemTypeService.getChemTypes().stream()
.filter(chemType -> chemType.getName().equals(LabAdminTestUtils.SOLID_COMPOUND_NAME))
.findAny()
.get()
.getId();
input.setChemTypeId(chemTypeId);
shelfLifeService.createShelfLife(input, AccountManagerTestUtils.BETA_LAB_MANAGER_PRINCIPAL);
}
}

The Junit Test not autowired my service class. But it works in the controller

I don't know why this is happening, the Autowired of the service works in the controller but not works in the Junit test class
Here is my JUnit Test:
package com.crmbackend.user;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import com.crmbackend.entity.Role;
import com.crmbackend.entity.User;
import com.crmbackend.userService.UserService;
#DataJpaTest(showSql = true)
#AutoConfigureTestDatabase(replace = Replace.NONE)
#Rollback(false)
public class UserServiceTests {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserService service;
#Test
public void testCreateNewUserWithSingleRolePasswordEncode() {
Role roleUser = entityManager.find(Role.class, 3);
User userLucas = new User("lucas9324", "Lucas", "Tom", "qqq542417349", "Lucas9324#outlook.com", "7364832234");
userLucas.addRole(roleUser);
User savedUser = service.save(userLucas);
assertThat(savedUser.getId()).isGreaterThan(0);
}
}
Here is my service class:
package com.crmbackend.userService;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.crmbackend.entity.User;
#Service
#Transactional
public class UserService {
#Autowired
private UserRepository userRepo;
#Autowired
private RoleRepository roleRepo;
#Autowired
private PasswordEncoder passwordEncoder;
public User getByUsername(String username) {
return userRepo.getUserByUsername(username);
}
private void encodePassword(User user) {
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
}
public User save(User user) {
boolean isUpdatingUser = (user.getId() != null);
if (isUpdatingUser) {
User existingUser = userRepo.findById(user.getId()).get();
if (user.getPassword().isEmpty()) {
user.setPassword(existingUser.getPassword());
} else {
encodePassword(user);
}
} else {
encodePassword(user);
}
return userRepo.save(user);
}
}
The #Autowired of
#Autowired
private UserService service;
Works fine in the controller but not working in the JUnit test....
Always got not qualifying bean found issue.
Any suggestions?
DataJpaTest is a slice test, it does not load the entire Spring Application Context i.e. it will not load your service but only whats required to test JPA functionality.
If you want to load your UserService as a bean you will need to import.
However given all the dependencies that are also autowired in your UserService you're likely better just using SpringBootTest to load the entire context.
You can exclude autoconfigurations/configurations, filter component scans if you are using component scanning over configurations if loading the entire context is taking too long.
For information on DataJpaTest
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.autoconfigured-spring-data-jpa
You can use the #DataJpaTest annotation to test JPA applications. By default, it scans for #Entity classes and configures Spring Data JPA repositories. If an embedded database is available on the classpath, it configures one as well. SQL queries are logged by default by setting the spring.jpa.show-sql property to true. This can be disabled using the showSql() attribute of the annotation.
Regular #Component and #ConfigurationProperties beans are not scanned when the #DataJpaTest annotation is used. #EnableConfigurationProperties can be used to include #ConfigurationProperties beans.

Can we stub in Spring Boot Test, Mockito, or powerMock in similar way as with Wiremock

Indeed Wiremock is very powerful as for as integration tests are concerned. I love the way Wiremock stub a URL response without the mutation of the beans (The way we do in mockito or powermock for Unit tests).
#Mock
SomeRepository someRepository; //Zombie mutation
#Mock
AnotherComponent anotherComponent; //mutation
#InjectMocks
SomeService someService; //mutation - This is the class we are unit testing
In the integration test I want all 3 layers are get tested and mock external dependency
+---> Repository -----> MySQL (I managed this with in-memory h2 database)
|
controller---> service
|
+---> Proxy ---------> Another REST service (some way to mock the call???)
Is it possible to do the same with Spring Boot Test, or mockito or powermock (as I am already using them and just don't want to add a new library to the project)
Here is how we do stubbing in Wiremock.
service.stubFor(get(urlEqualTo("/another/service/call"))
.willReturn(jsonResponse(toJson(objResponse))));
Above code means, in our test whenever an external service will
be called (http://example.com/another/service/call), it will be intercepted and the sample response will be
injected - and external call will not leave the system
A sample code
#SpringBootTest
#AutoConfigureMockMvc
public class StubTest {
#Autowired
private MockMvc mockMvc;
private MockRestServiceServer server;
#BeforeEach
public void init() {
RestTemplate restTemplate = new RestTemplate();
server = MockRestServiceServer.bindTo(restTemplate).build();
}
#Test
public void testFakeLogin() throws Exception {
String sampleResponse = stubSampleResponse();
//Actual URL to test
String result = mockMvc.perform(get("/s1/method1")
.contentType("application/json"))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
assertThat(result).isNotNull();
assertThat(result).isEqualTo(sampleResponse);
}
private String stubSampleResponse() {
String response = "Here is response";
//URL to stub (this is in another service)
server.expect(requestTo("/v1/s2/dependent-method"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(response, MediaType.APPLICATION_JSON));
return response;
}
}
Feign Client
#FeignClient(value = "service-s2", url = "http://localhost:8888/")
public interface S2Api {
#GetMapping("/v1/s2/dependent-method")
public String dependentMethod();
}
but I get following error, which mean this url was not stubbed.
feign.RetryableException: Connection refused: connect executing GET http://localhost:8888/v1/s2/dependent-method
at feign.FeignException.errorExecuting(FeignException.java:213) ~[feign-core-10.4.0.jar:na]
Yes, Its possible with MockRestServiceServer.
Example:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
#RunWith(SpringRunner.class)
#RestClientTest(MyRestClient.class)
#AutoConfigureWebClient(registerRestTemplate = true)
public class MyRestClientTest {
#Autowired
private MockRestServiceServer server;
#Test
public void getInfo() {
String response = "response";
server.expect(requestTo("http://localhost:8090" + "/another/service/call"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(response,MediaType.APPLICATION_JSON));
}
}

UriComponentsBuilder/MvcComponentsBuilder usage in a Spring Boot Test

I've put a very simple sample project on GitHub to reproduce the problem.
The main issue is that I have a PersonController that has a PutMapping to create a new person. In order to populate the Location header with the URL to fetch that person, I add the UriComponentsBuilder as parameter for that PutMapping, as you can see here:
#PostMapping
public ResponseEntity<Person> add(#RequestBody final PersonForCreate personForCreate, UriComponentsBuilder uriComponentsBuilder) {
Person newPerson = new Person(this.people.size() + 1, personForCreate.getFirstName(), personForCreate.getLastName());
this.people.add(newPerson);
// create the URI for the "Location" header
MvcUriComponentsBuilder.MethodArgumentBuilder methodArgumentBuilder = MvcUriComponentsBuilder.fromMappingName(uriComponentsBuilder, "getById");
methodArgumentBuilder.arg(0, newPerson.getId());
URI uri = URI.create(methodArgumentBuilder.build());
return ResponseEntity.created(uri).body(newPerson);
}
This works fine when running the project. But when running a test this results in an IllegalArgumentException No WebApplicationContext. The error comes from the MvcUriComponentsBuilder.fromMappingName call, but I have no idea why.
My test looks as follows:
#ExtendWith(SpringExtension.class)
#WebMvcTest
class PersonControllerTest {
#Autowired
private PersonController personController;
#Test
void add() {
this.personController.add(new PersonForCreate("Charles", "Darwin"), UriComponentsBuilder.newInstance());
}
}
I'm not sure if passing UriComponentsBuilder.newInstance() is correct, but I've tried with other values and notice no difference.
FYI, The sample project uses Spring Boot 2.2.3 and JUnit 5, but I have the same problem using a sample project on JUnit 4.
Did you try MockMvc? The following code will be called in the same way HTTP request gets processed, as you're using #WebMvcTest, only the web layer is invoked rather than the whole context.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#WebMvcTest
class PersonControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
void add() throws Exception {
//this.personController.add(new PersonForCreate("Charles", "Darwin"), uriComponentsBuilder);
this.mockMvc.perform(MockMvcRequestBuilders.post("/person")
.content("{\"firstName\": \"Charles\",\"lastName\": \"Darwin\"}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.content().string("{\"id\":4,\"firstName\":\"Charles\",\"lastName\":\"Darwin\"}"));
}
}
Spring.io/guides reference
https://spring.io/guides/gs/testing-web/

Should Mockito be used with MockMvc's webAppContextSetup in Spring 4?

I'm having difficulties getting Mockito and MockMvc working together when I use the webAppContextSetup together. I'm curious if it's because I'm mixing the two in a way they were never intended.
Source: https://github.com/zgardner/spring-boot-intro/blob/master/src/test/java/com/zgardner/springBootIntro/controller/PersonControllerTest.java
Here is the test I'm running:
package com.zgardner.springBootIntro.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static java.lang.Math.toIntExact;
import static org.hamcrest.Matchers.is;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import com.zgardner.springBootIntro.Application;
import com.zgardner.springBootIntro.service.PersonService;
import com.zgardner.springBootIntro.model.PersonModel;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class PersonControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private DefaultListableBeanFactory beanFactory;
#Mock
private PersonService personService;
#InjectMocks
private PersonController personController;
#Before
public void setup() {
initMocks(this);
// beanFactory.destroySingleton("personController");
// beanFactory.registerSingleton("personController", personController);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getPersonById() throws Exception {
Long id = 999L;
String name = "Person name";
when(personService.findById(id)).thenReturn(new PersonModel(id, name));
mockMvc.perform(get("/person/getPersonById/" + id))
.andDo(print())
.andExpect(jsonPath("$.id", is(toIntExact(id))))
.andExpect(jsonPath("$.name", is(name)));
}
}
I was expecting that when mockMvc performed the mock of that HTTP call, it would use the PersonController I defined in my test. But when I debug through, it's using the PersonController which was created by the SpringJunit4ClassRunner on the test boot up.
I found two ways to get this to work:
Inject the bean factory, remove the old personController singleton, and add my own. This is ugly, and I am not a fan.
Wire everything up using the standaloneSetup instead of webAppContextSetup. I may do this instead as I don't have to touch the bean factory.
Here are some different articles I've found that somewhat touch on the topic:
Spring Tutorial - Building REST Services This just autowires in the repos to clear out the data before the integration test takes place.
Use Spring MVC Test framework and Mockito to test controllers This uses Mockito along with webAppContextSetup, but this is in Spring 3. (I'm using Spring Boot)
Unable to mock Service class in Spring MVC Controller tests This uses the standaloneSetup, which does work in my case too.
Thoughts?
You might be interested in the new testing features coming in Spring Boot 1.4 (specifically the new #MockBean annotation). This sample shows how a service can be mocked and used with a controller test.
For some reason the Mockito annotations #Mock et #InjectMocks won't work in this case.
Here's how I managed to make it work :
Instantiate the personService bean manually using your own Test context
make Mockito create a mock for this personService.
let Spring inject these mocks in the controller PersonController.
You should have your TestConfig :
#Configuration
public class ControllerTestConfig {
#Bean
PersonService personService() {
return mock(PersonService.class);
}
}
In your PersonControllerTest, you won't need the personController anymore, since it's managed by the mockMvc through the perform method. You also don't need to execute initMocks() because you initialize your mocks manually inside the Spring config. You should have something like :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Application.class, ControllerTestConfig.class})
#WebAppConfiguration
public class PersonControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
PersonService personService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getPersonById() throws Exception {
Long id = 999L;
String name = "Person name";
when(personService.findById(id)).thenReturn(new PersonModel(id, name));
mockMvc.perform(get("/person/getPersonById/" + id))
.andDo(print())
.andExpect(jsonPath("$.id", is(toIntExact(id))))
.andExpect(jsonPath("$.name", is(name)));
}
}
I sometimes use Mockito to fake Spring beans with usage of #Primary and #Profile annotations. I wrote a blog post about this technique. It also contains link to fully working example hosted on GitHub.
To extend florent's solution, I encountered performance issues and extensibility issues creating separate configurations for every controller test which needed a different set of service mocks. So instead, I was able to mock out my application's service layer by implementing a BeanPostProcessor alongside my tests which replaces all #Service classes with mocks:
#Component
#Profile("mockService")
public class AbcServiceMocker implements BeanPostProcessor {
private static final String ABC_PACKAGE = "com.mycompany.abc";
#Override
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
if (StringUtils.startsWith(bean.getClass().getPackage().getName(), ABC_PACKAGE)) {
if (AnnotationUtils.isAnnotationDeclaredLocally(Service.class, bean.getClass())
|| AnnotationUtils.isAnnotationInherited(Service.class, bean.getClass())) {
return mock(bean.getClass());
}
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
return bean;
}
}
I enabled these mocks in specific tests with an #ActiveProfiles annotation:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"classpath:/WEB-INF/application-context.xml"})
#ActiveProfiles("mockService")
public class AbcControllerTest {
private MockMvc mvc;
#Before
public final void testBaseSetup() {
mvc = MockMvcBuilders.webAppContextSetup(context).build();
}
Lastly, the injected Mockito mocks were wrapped in an AopProxy causing Mockito's expect and verify calls to fail. So I wrote a utility method to unwrap them:
#SuppressWarnings("unchecked")
protected <T> T mockBean(Class<T> requiredType) {
T s = context.getBean(requiredType);
if (AopUtils.isAopProxy(s) && s instanceof Advised) {
TargetSource targetSource = ((Advised) s).getTargetSource();
try {
return (T) targetSource.getTarget();
} catch (Exception e) {
throw new RuntimeException("Error resolving target", e);
}
}
Mockito.reset(s);
return s;
}

Resources