Not able to inject mock objects - unit-testing

I am newe to Mockito and Junit, I have written unit test cases for testing my rest service and made use of Mockito for injecting mocks. And code is below:
BillControllerTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class BillControllerTest{
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#InjectMocks
private BillController billController;
#Mock
private BillService mockBillService;
#Before
public void setupController() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testBills() throws Exception {
// some fake data
final List<Bill> fakeBillList = new ArrayList<>();
fakeBillList.add(CpsFake.bill("1234"));
when(mockBillService.getBills(BILL_UID))
.thenReturn(fakeBillList.stream());
mockMvc.perform(get("/bills/" + BILL_UID ))
.andExpect(content().contentType(MediaTypes.HAL_JSON))
// expect particular uid
.andExpect(content().string(containsString("\"uid\":\"1234\"")))
ApplicationTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ApplicationTest {
#Test
public void contextLoads() {
}
}
BillController.java:
#RestController
#RequestMapping(value = "/trials/{billUid}", produces = "application/hal+json")
public class BillController extends BaseController {
#Autowired
private BillService billService;
#Autowired
public BillController(BillService billService) {
this.billService = billService;
}
#RequestMapping(method = RequestMethod.GET, value = "")
public ResponseEntity<Resources<Resource<Bill>>> getBills(#PathVariable String billUid) {
return resourceListResponseEntity(
() -> billService.getBills(billUid),
bill-> createResource(bill),
resources -> resources.add(linkTo(methodOn(BillController.class)
.getBills(billUid)).withSelfRel()));
}
When I run the test (BillControllerTest), mockBillService is not getting invoked and instead it is calling actual billService. Please help me in this issue. Thank you in advance.

I think the problem is that you use mockito together with spring. Both make use of proxys.
Looking at your code of getBills - it is not dependent on the spring application context. So skip all your spring setup code (mockMvc and webApplicationContext) and use only Mockito. If yet invisible code depends on the ApplicationContext - mock the application context rather than setting up a real one.
This test would be:
simpler
container independent
faster
You could replace initMocks with the Annotation RunWith(MockitoJUnitRunner.class) if you want.

Related

Junit test becomes broken when I change a way to initialize Mockito

public class DummyTest {
#InjectMocks
private TestService service;
#Mock
private TestRepository repository;
#BeforeEach
public void before() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testMe() {
when(repository.findAllByQuery("")).thenReturn(List.of());
List<String> entities = service.findAllByQuery("test");
verify(repository, times(1)).findAllByQuery(anyString());
}
}
class TestService {
private TestRepository repository;
public List<String> findAllByQuery(String query) {
return repository.findAllByQuery(query);
}
}
class TestRepository {
public List<String> findAllByQuery(String query) {
return List.of("");
}
}
The test above successfully passed when I do Mockito initialization via #BeforeEach annotation.
After, I tried to remove #BeforeEach annotation and initialize Mockito with #ExtendWith(MockitoExtension.class) and the test failed due to:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at DummyTest.testMe(DummyTest.java:33)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
Could you please explain to me, why I got so different test execution results? Technically, I have just changed the way to initialize Mockito and that all.
#ExtendWith(MockitoExtension.class) and MockitoAnnotations.initMocks(this) configures Mockito in a little different way.
When you use #ExtendWith(MockitoExtension.class) additionally configures "strictness" of Mockito with default value STRICT_STUBS.
Which means you have to change the configuration of you stubs like this:
#Test
public void testMe() {
when(repository.findAllByQuery(anyString())).thenReturn(List.of());
List<String> entities = service.findAllByQuery("test");
verify(repository, times(1)).findAllByQuery(anyString());
}
or even like this:
#Test
public void testMe() {
when(repository.findAllByQuery(eq("test"))).thenReturn(List.of());
List<String> entities = service.findAllByQuery("test");
verify(repository, times(1)).findAllByQuery(anyString());
}

How to test POST method in Spring boot using Mockito and JUnit

I am newbie to Unit testing using JUnit and Mockito in Spring boot framework.
I want to test this method. How to test POST Request method:
// add Employee
#RequestMapping(method = RequestMethod.POST)
public void addEmployee(#RequestBody Employee employee){
this.employeeService.addEmployee(employee);
}
Thank you in advance
As #merve-sahin rightly pointed out, you can use #WebMvcTest to achieve this.
Look at the following example :
#RunWith(SpringRunner.class)
#WebMvcTest(YourController.class)
public class YourControllerTest {
#Autowired MockMvc mvc;
#MockBean EmployeeService employeeService;
#Test
public void addEmployeeTest() throws Exception {
Employee emp = createEmployee();
mvc.perform(post("/api/employee")
.contentType(MediaType.APPLICATION_JSON)
.content(toJson(emp)))
.andExpect(status().isOk());
}
}
In Above code you can mock your dependent service using #MockBean.
The test will perform post on your custom Employee object and validate the response
You can add headers, authorization while calling perform
Assuming you using JSON as media type, you can write toJson() method using any json library to convert Employee object into Json string format
private String toJson(Employee emp) {
If you are using XML, then you can do the same for XML
You can validate the response using expectations in chained way.
As rightly pointed out, please checkout MockedMvc link which should help you
Go through this following example:
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationControllerTest {
#Mock
EmployeeService employeeService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
initMocks(this);
YourController controller = new YourController(employeeService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void addEmployee() throws Exception {
Employee emp = new Employee("emp_id","emp_name");//whichever data your entity class have
Mockito.when(employeeService.addEmployee(Mockito.any(Employee.class))).thenReturn(emp);
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.content(asJsonString(emp))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"));
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In above given example mock your service class which is required to post the data to your Employee entity class.
I'm assuming that you're doing this via controller so you first need to initialize the controller which comes under the #Before annotation.
By doing above example you'll be able to post your data into the JSON format.
The below example is using JUnit5, Mockito3.x, spring-boot2.4.4, and
assertj3.x
The spring-boot-starter-test dependency from version 2.2.0
already comes with Junit 5 and contains also Hamcrest, assertj, and Mockito libraries.
In JUnit 5, “Runner” extension points, available in JUnit 4, are replaced by the Extension API.
You can register the Mockito extension via #ExtendWith.
Initializes mocks annotated with #Mock annotation so that explicit usage of MockitoAnnotations#initMocks(Object) is not required.
From spring-boot 2.1, there is no need to load the SpringExtension using annotation #ExtendWith because it's included as a meta-annotation in these annotations #DataJpaTest, #WebMvcTest, and #SpringBootTest.
Complete example with Github link: https://github.com/jdamit/DemoSpringBootApp.git
**#WebMvcTest(controllers = UserController.class)**
public class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper mapper;
#MockBean
private UserServiceImpl userService;
private List<UserDto> users;
private UserDto user;
private String URI = "/users";
#BeforeEach
void setUp(){
users = List.of(new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"));
user = new UserDto("Rahul", "Swagger", "rahul.swagger#gmail.com", "sector 120");
}
#Test
//#Disabled
void getUsersTest() throws Exception {
Mockito.when(userService.getUsers()).thenReturn(users);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(URI)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(users));
}
#Test
//#Disabled
void createUserTest() throws Exception {
Mockito.when(userService.createUser(Mockito.any(UserDto.class))).thenReturn(user);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URI)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(user).getBytes(StandardCharsets.UTF_8))
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isNotEmpty();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(user));
}
}

Mockito: stub function is not working

I am using Mockito to write a simple unit test.
Then, a function under test:
public class MyService {
public void getData() {
executor.execute(new MyRunnable() {
#Override
doTask() {
MyRestClient client = getRestClient();
Response resp = client.getFromServer();
persist(resp.getData());
}
});
}
}
protected MyRestClient getRestClient() {
return new MyRestClient();
}
My test case, I want to test doTask() has run & resp.getData() is persisted:
#Test
public void testGetData() {
MyService spyService = spy(MyService.getInstance());
// mock client
MyRestClient mockedClient = mock(MyRestClient.class);
mockedClient.setData("testData");
// stub getRestClient() function to return mocked client
when(spyService.getRestClient()).thenReturn(mockedClient);
// SUT
spyService.getData();
// run the Runnable task.
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Exception {
Object[] args = invocation.getArguments();
Runnable runnable = (Runnable) args[0];
runnable.doTask();
return null;
}
}).when(executor).execute(Mockito.any(Runnable.class));
...
}
As you see above, I stub the getRestClient() function to return a mocked MyRestClient. However when run the test case, it doesn't stub the getRestClient() but run the real function. Why?
[Edit] following comment and review feedback
A rule of thumb is not to mock the class under test. Also your testing will be much easier if your class under test does not use the new keyword. Instead use Factory classes to create objects. There will be no need to use Mockito.spy() only Mockito.mock().
The fact that the following answer requires significant test setup is telling you that MyService has too much reposibility and needs to be simplified. However for the sake of answering your question directly here is how you can refactor your code to support verifying the call to persist() using Mocks.
MyService accepts in the constructor the objects that you will be mocking in your test setup. Having them passed into the constructor allows your JUnit test case to create the Mocks and keep a reference to them for verification later.
public class MyService {
private MyRunnableFactory runFactory;
private MyRestClientFactory restFactory;
private MyRestDao dao;
// inject constructor arguments
public MyService(MyRunnableFactory runFactory, MyRestClientFactory restFactory, MyRestDao dao) {
this.runFactory = runFactory;
this.restFactory = restFactory;
this.dao = dao;
}
public void getData() {
MyRestClient restClient = restFactory.createInstance();
MyRunnable runner = runFactory.createInstance(restClient, dao);
executor.execute(runner);
}
}
MyRunnable is created so that it can be tested in isolation if required. Again we inject the Mock objects into the constructor. It is tempting to inline Runnables as you have written in your question, however you lose the ability to control the new instance being created within you tests.
public class MyRunnable implements Runnable {
private MyRestClient restClient;
private MyRestDao dao;
public MyRunnable(MyRestClient restClient, MyRestDao dao) {
this.restClient = restClient;
this.dao = dao;
}
public void run() {
Response resp = restClient.getFromServer();
dao.persist(resp.getData());
}
}
MyRestDao is created because this is the class that you want to Verify in your test case. I don't see where persist() is defined in your question so we create a Data Access Object (DAO) to implement it.
public class MyRestDao {
public void persist() {
// save to some repository
}
}
Now let's write the test case that uses the above classes. We want to verify that the persist() method has been called
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock MyRestDao dao;
#Mock MyRestClient restClient;
#Mock MyRunnableFactory runFactory;
#Mock MyRestClientFactory restFactory;
#Test
public void testPersistIsCalled() {
Response expectedResponse = new Response("some data"); // real implementation, not mocked
MyRunnable runner = new MyRunnable(restClient, dao); // real implementation, not mocked
when(restFactory.createInstance()).thenReturn(restClient);
when(runFactory.createInstance(restClient, dao)).thenReturn(runner);
when(restClient.getFromServer()).thenReturn(expectedResponse);
when(restClient.getData()).thenReturn(myRunnable);
// method under test
MyService service = new MyService(runFactory, restFactory);
service.getData();
verify(dao).persist(expectedResponse.getData());
}
}
Note that this test case is brittle because it is tightly coupled to the actual implementation of the MyService class. Ideally you want tests that don't need to know about the internal workings of your class under test.

Mockito mocks and ensuring method level spring security is tested [duplicate]

This question already has answers here:
Injecting Mockito mocks into a Spring bean
(23 answers)
Closed 7 years ago.
I'm trying to test some controller with method-level spring security and I want to mock out the repository dependencies in the controller. Basically I want to test that (a) the methods are enforcing security and (b) other beans invoked in SpEL expressions are working.
My issue is that when using Mockito's #InjectMocks for instantiating the controller the spring security proxies are not being applied to the controller and the method security is bypassed. If I use #Autowired to allow Spring to create the controller, my custom method level security logic does get called but the #Mock objects are not injected.
#RestController
#RequestMapping("/api/projects/{projectId}")
public class ProjectKeywordResource {
//I want to mock this repository
#Inject
private ProjectKeywordRepository projectKeywordRepository;
//Invokes another bean if user not assigned admin role.
#PreAuthorize("hasRole('ROLE_ADMIN')" + " or "
+ "#projectService.canEditProjectData(#projectId)")
#RequestMapping(value = "/projectKeywords", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<ProjectKeyword> create(
#PathVariable String projectId,
#RequestBody ProjectKeyword projectKeyword)
throws URISyntaxException {
projectKeywordRepository.save(projectKeyword);
return ResponseEntity.created(
new URI("/api/" + projectId + "projectKeywords/"
+ projectKeyword.getId())).body(result);
}
}
My Test Case is here:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ProjectKeywordResourceSecurityTest {
private static final String DEFAULT_PROJECT_ID = "1";
#Mock
private ProjectKeywordRepository projectKeywordRepository;
//#Inject - Adding the following annotation adds the necessary spring security proxies,
but then ProjectKeywordResource uses the real ProjectKeywordRepository not the mock one.
#InjectMocks
private ProjectKeywordResource projectKeywordResource;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test(expected = AccessDeniedException.class)
#WithMockUser
#Transactional
public void testCreateThrowsAccessDenied() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
#Test
#WithMockUser(username = "admin", roles={"ADMIN"})
#Transactional
public void testCreateAuthorizationSuceedsForAdminUser() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
}
Is there a bit of config magic that allows me to wrap the Mockito mock controller with the necessary spring proxies, or alternatively force the use of the Mock on the injected bean in my test case?
The link that Bewusstsein posted got me on the right track to a viable answer posted by jfcorugedo. Basically what I had to do was to create a new bean in my test configuration class that mocks the Repository class and annotate it with the #Primary annotation. Adding the Spring profile annotation allows these beans to be switched off by default and therefore doesn't interfere with other tests. The revised test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#ActiveProfiles({"useMockRepositories","default"})
public class ProjectKeywordResourceSecurityTest {
private static final String DEFAULT_PROJECT_ID = "1";
#Inject
private ProjectKeywordResource projectKeywordResource;
#Test(expected = AccessDeniedException.class)
#WithMockUser
public void testCreateThrowsAccessDenied() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
#Test
#WithMockUser(username = "admin", roles={"ADMIN"})
public void testCreateAuthorizationSuceedsForAdminUser() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
My Test Configuration class has the following:
#Configuration
public class TestConfiguration {
#Profile("useMockRepositories")
#Bean
#Primary
public ProjectKeywordRepository MockProjectKeywordRepository() {
return Mockito.mock(ProjectKeywordRepository.class);
}
}

Mock object is not injected

I try mock controler:
#RestController
public class SthControl {
#Autowired
private ObjRepo repo;
#RequestMapping(value = "/dosth", method = RequestMethod.POST, produces = "application/json")
public ModelMap handleSth(#RequestParam("key") String key) {
final Logger logger = Logger.getLogger(getClass());
logger.info("Is Mock "+ new MockUtil().isMock(repo));//return FALSE- is real object
logger.info("Key " + repo.loadByKey(key);//return NULL- always call real Method
Test Case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml")
#WebAppConfiguration
public class SthControlTest {
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
protected WebApplicationContext wac;
private MockMvc mockMvc;
#Mock
private ObjRepo repo;
#InjectMocks
#Autowired
private SthControl contr;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(this.wac).build();
BasicConfigurator.configure();
}
#Test
public void testRegister() throws Exception {
final UUID uuid = UUID.randomUUID();
final String keyValue = "KeyVal";
final Logger logger = Logger.getLogger(getClass());
repo = Mockito.mock(ObjtRepo.class);
Mockito.when(repo.loadByKey(keyValue)).thenReturn(new Obj(uuid, keyValue, TimeUtils.currentTimeSecond(), false));
Problem still exist if replace #Mock Annotation with this lines
repo = Mockito.mock(ObjRepo.class);
ReflectionTestUtils.setField(contr, "repo", repo, ObjRepo.class);
logger.info("Obj " + repo.loadByKey(keyValue).getId());//return correct object
logger.info("Mock Is "+new MockUtil().isMock(ReflectionTestUtils.getField(contr,"repo")));//True
There are two issues here.
You need to swap the lines this.mockMvc = webAppContextSetup(this.wac).build(); and MockitoAnnotations.initMocks(this);, otherwise the injection done by the Spring web application context setup will overwrite the injection done by Mockito. Always do the Spring injection first.
You need to remove the line repo = Mockito.mock(ObjtRepo.class); from testRegister, because this line replaces the value in repo with one that differs from the one you injected, so when you stub the new value, it won't affect the behaviour of SthControl.