Mocking Repositories to test Service in Spring - spring

I occurred some failures after trying to mock repositories to unit test my services in Spring-Boot
Thats what I have (simplified)
#ExtendWith(SpringExtension.class)
#ExtendWith(MockitoExtension.class)
#SpringBootTest
#ActiveProfiles("test")
public class UserTest{
#InjectMocks
private UserServiceImpl userService;
#Mock
private UserRepostiory userRepository;
#Before
public void setUp() {
User user = new User(1L, "email#email", "name");
when(userRepostitory.findById(1L)).thenReturn(Optional.of(user));
}
#Test
public void findUserByIdReturnsUser() {
User user = userService.getById(1L); => always throws error in Service, that no User is found with that Id, it calls the regular Repository: mock does nothing
assertEquals(1L,user.getId());
}
}
But I never get the User returned when the service calls the repo. I am kinda new to Unit Testing, and I am pretty sure, that I miss something here.

In the setUp you do:
when(userRepostitory.findById(1L)).thenReturn(Optional.of(user));
But in the Test you call
User user = userService.getById(1L);
Either mock getById or call findById

"Repostiory" | "Repostitory" looks like a typo error.

Annotate the test class with #RunWith(SpringRunner.class)

Related

Is it possible to create a test repository for mockMVC?

I have the test below:
#AutoConfigureMockMvc
#SpringBootTest
public class ProjectDashboardTests {
#Autowired
private MockMvc mockMvc;
#Test
public void projectsDashboardShows() throws Exception {
this.mockMvc
.perform(get("/projects/1"))
.andDo(print())
.andExpect(status().isOk());
}
#Test
public void projectFormShows() throws Exception {
this.mockMvc
.perform(get("/get-project-form/1"))
.andDo(print())
.andExpect(status().isOk());
}
These rely upon path variables 1 = user ID. For these tests to work correctly there must a user account with user id = 1 which is currently logged in. How would I simulate that in mockMVC?
You have few options, depends which test you want to create and which configuration you have (more detail is needed to answer more accurately).
For unit tests, you can mock your repository (or service), and method in which you get the Project. For example if you have ProjectService with method getProjectById(Long id), you can use something like that:
#AutoConfigureMockMvc
#SpringBootTest
public class ProjectDashboardTests {
...
#MockBean
private ProjectService service; // <- mock your service
#Test
public void projectsDashboardShows() throws Exception {
// mock service method in selected test or in #BeforeEach method
Mockito.when(service.getProjectById(ArgumentMatchers.anyLong())
.thenReturn(new Project(/* your own test object*/));
...
}
...
}
Based on For these tests to work correctly there must a user account with user id = 1 which is currently logged in, I assume you work with Spring Security? You can take a look at the #WithMockUser annotation if you want simulate selected user.
For integration tests, you should prepare database (H2 for example) for tests only, before each test add project into db and remove after each test (run independently).

Junit 5 doesn't see controllers URL when using #WebMvcTest - Mockito and Spring Boot

I created a test class for SystemAuthorityController, as i only need part of the context to be loaded.
I've used #WebMvcTest annotation and I`ve specified which controller I want to test (I also tried with all controllers but that didn't work either).
#WebMvcTest(SystemAuthorityController.class)
#TestPropertySource("classpath:application.properties")
public class SystemAuthorityControllerTest
When I try to call any endpoint from this controller I get 404, because the endpoint wasn't found.
After some research I found the solution - that is to add #Import annotation with the controller which I need and everything worked after that, the URL was found.
#WebMvcTest(SystemAuthorityController.class)
#Import({SystemAuthorityController.class})
#TestPropertySource("classpath:application.properties")
public class SystemAuthorityControllerTest
My question here is why I need to explicitly import the controller I want to test as I never seen this annotation being used for this purpose (neither do I think that I should be used like this). From my understanding WebMvcTest should load all controller beans.
There is no need to explicitly import controller if working in same module.
If you are getting 404, it's probably due to some other reason. [Need to see logs]
This is the basic working example of ControllerTest. [In case you miss anything]
#RunWith(MockitoJUnitRunner.class)
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AControllerTest {
#InjectMocks
AController aController;
#Autowired
MockMvc mockMvc;
#Mock
AService aService;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(aController).build();
}
#Test
public void aTest() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
ADetails user = new ADetails();
user.setId("1234");
this.mockMvc.perform(MockMvcRequestBuilders.post("/a/signin").header("Referer", "test")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful());
}

Testing Spring Data Rest

I want to test a Spring boot 2 respository as rest controller app.
App is working well from browser ( http://localhost:8080/api/v1/ehdata ), but I cannot find an example how can I test it with Spring test environment. Very important, there are no RestControllers and Services, only Repositories annotated like this:
#RepositoryRestResource(path = EhDataRepository.BASE_PATH,
collectionResourceRel = EhDataRepository.BASE_PATH)
public interface EhDataRepository extends
PagingAndSortingRepository<EhData, Long> {
public static final String BASE_PATH="ehdata";
}
I tried with this test, but responses was empty, and status code was 404:
#RunWith(SpringRunner.class)
#SpringBootTest
#WebMvcTest(EhDataRepository.class)
public class RestTest extends AbstractRestTest {
#Autowired MockMvc mvc;
#Test
public void testData() throws Exception {
mvc.perform(get("/api/v1/ehdata")
.accept(MediaTypes.HAL_JSON_VALUE))
.andDo(print())
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE,
MediaTypes.HAL_JSON_VALUE+";charset=UTF-8")
.andReturn();
}
}
thx,
Zamek
You will need to mock the output from the respository like this based on the method you are trying to test:
#MockBean
private ProductRepo repo;
And then
Mockito.when(this.repo.findById("PR-123")
.get())
.thenReturn(this.product);
this.mvc.perform(MockMvcRequestBuilders.get("/products/{id}", "PR-123")
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
Also, remove the server-context-path while calling API in perform() method.

Controller layer test in SpringBoot application

I have a controller in my SpringBoot app:
#Controller
#RequestMapping("/v1/item")
public class Controller{
#Autowired
private ServiceForController service;
#PostMapping()
public String createItem(#ModelAttribute Item item) {
Item i = service.createItem(item.getName(), item.getDomain());
return "item-result";
}
}
And I'd like to test it separately from service with a help of mocks.How to implement it?
There are at least two approaches to do it:
To start up the whole SpringBoot context and make a sort of integration tests
Example:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mvc;
#Test
#WithMockUser(roles = "ADMIN")
public void createItem() throws Exception {
mvc.perform(post("/v1/item/")
.param("name", "item")
.param("domain", "dummy.url.com"))
.andExpect(status().isOk());
//check result logic
}
Test exclusive controller layer and limit the whole loaded context exclusively to it. Example:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = Controller.class)
public class ControllerTest{
#Autowired
private MockMvc mvc;
#MockBean
private ServiceForController service;
//testing methods and their logic
...
}
Even though the second approach seems more sensible (as for me) in terms of resources used, it may cause plenty of inconveniences due to the lack of beans initialized. For instance, before I decided to try another option, I faced the need to create mocks of at least 5 beans that are added to the context on SpringBoot start in my ContollerTest class.
Thus, I had to switch to the approach with a use of #SpringBootTest in combination with #SpyBean, that allowed me to call a Mockito verify() method.

Unit Testing of Spring MVC Controller: Failing to fully perform request

I have a simple controller defined as below:
#Controller
#RequestMapping("/rest/tests")
public class TestController {
#Autowired
private ITestService testService;
#RequestMapping(value="/{id}", method=RequestMethod.DELETE)
#ResponseStatus(value = HttpStatus.OK)
public void delete(#PathVariable Integer id)
{
Test test = testService.getById(id);
testService.delete(test);
}
}
I have been trying to test the delete method, and have not succeeded so far. The test I have written is pretty simple too.
public class MockmvcTest {
#InjectMocks
private TestController test;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(test).build();
}
#Test
public void myTest() throws Exception {
this.mockMvc.perform(delete("/rest/tests/{id}", new Integer(4)))
.andExpect(status().isOk()
}
}
I have tested the method using "advanced rest client" extension in chrome and it works as expected.
By working I mean that the entity with the given id is deleted from database.
When myTest() is executed the status code is still 200, but the entity is not removed from the database.
What might be the reason behind this behavior?
You're using Mockito to inject mock service beans into your TestController (in particular, ITestService). All mocks by definition have no behaviour until you specify it, by default all operations you perform will either do nothing or return null. You can easily confirm that by setting a breakpoint inside TestController.delete method, executing the test in debug mode and inspecting values of test and testService variables.
Mockito is used for unit-level tests that replace SUT's collaborators with a mock that you set up to behave in a certain verifiable way. Once you call a method on your SUT (in your case that's TestController) you can assert whether it adheres to its contract or not.
It's actually a big no-no to allow your automated tests to modify a real instance of a database.

Resources