In Spring boot framework, I'm finding a difficulty with the controller Unit testing using JUnit and Mockito. I want to test this method. How to test DELETE Request method:
// delete application
Controller class
#DeleteMapping("/applications")
public String deleteApplicationByObject(#RequestBody Application application) {
applicationService.deleteById(application.getId());
return "Deleted";
}
// delete application
Service class
#Override
#Transactional
public String removeById(Long id) {
dao.deleteById(id);
return "SUCCESS";
}
// delete application
Dao class
#Override
public void deleteById(Long id) {
Application application = findById(id);
em.remove(application);
}
Thank you in advance.
After a while i'm able to find a solution of my question which is,
ApplicationControllerTest.class
package com.spring.addapplication.test.controller;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.addapplication.controller.ApplicationController;
import com.spring.addapplication.model.Application;
import com.spring.addapplication.service.ApplicationService;
import com.spring.addapplication.url.UrlChecker;
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationControllerTest {
#Mock
ApplicationService applicationService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
initMocks(this);// this is needed for inititalization of mocks, if you use #Mock
ApplicationController controller = new ApplicationController(applicationService,urlChecker);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void deleteApplication() throws Exception {
Mockito.when(applicationService.removeById(10001L)).thenReturn("SUCCESS");
mockMvc.perform(MockMvcRequestBuilders.delete("/applications", 10001L))
.andExpect(status().isOk());
}
Related
I am writing tests and I was looking to mock the result of the kafka admin client, when a topic is created.
I am using Mockito to write my unit tests.
Here is the test code:
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.common.KafkaFuture;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Mock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.main.Launch;
import io.restassured.RestAssured;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
#QuarkusTest
public class AppTest {
#InjectMocks
private App mockApp;
#Mock
private Client mockClient;
#Mock
private Database mockDatabase;
#Mock
private Admin mockKafkaAdmin;
#Mock
private ListTopicsResult mockListTopicResult;
#Mock
private KafkaFuture<Set<String>> mockKafkaFuture;
#Mock
private KafkaFuture<Void> mockKafkaFutureVoid;
#Mock
private Set<String> mockSet;
#Mock
private CreateTopicsResult mockCreateTopicResult;
#Mock
private Map<String, KafkaFuture<Void>> mockKafkaTopicResult;
#BeforeAll
public static void setupAll() {
RestAssured.filters(new RequestLoggingFilter(), new ResponseLoggingFilter());
}
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
#Launch(value = {}, exitCode = 1)
public void testLaunchCommandFailed() {}
#Test
public void testCreateTopic() throws InterruptedException, ExecutionException {
Mockito.when(mockClient.getKafka()).thenReturn(mockKafkaAdmin);
Mockito.when(mockClient.getKafka().createTopics(Mockito.anyList())).thenReturn(mockCreateTopicResult);
Mockito.when(mockCreateTopicResult.values()).thenReturn(mockKafkaTopicResult);
Mockito.when(mockKafkaTopicResult.get("meme.transmit.test")).thenReturn(mockKafkaFutureVoid);
mockApp.createTopic("test");
Mockito.when(mockClient.getKafka().listTopics()).thenReturn(mockListTopicResult);
Mockito.when(mockClient.getKafka().listTopics().names()).thenReturn(mockKafkaFuture);
Mockito.when(mockClient.getKafka().listTopics().names().get()).thenReturn(mockSet);
Assertions.assertTrue(mockApp.containsTopic("test"));
}
// ...
}
I get a nullpointer error when this line is called in the production code:
Mockito.when(mockCreateTopicResult.values()).thenReturn(mockKafkaTopicResult);
But as you can see I mocked it with mockKafkaTopicResult. What might I be missing here? Also is there an easier method to work with KafkaAdminClient when writing unit tests?
Mockito.when(mockClient.getKafka().createTopics(Mockito.anyList()))
.thenReturn(mockCreateTopicResult);
The issue was that I was using anyList(), when the createTopics() only accepts Collections.
The fix: Mockito.anyCollections()
I am new in springboot. I am just watching the Spring in Action and programming follow the author.
then things get difficult when i just reading the chapter 1. I need to test a controller. the code in this book is:
package tacos;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#RunWith(SpringRunner.class)
#WebMvcTest(HomeController.class) // <1>
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc; // <2>
#Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) // <3>
.andExpect(status().isOk()) // <4>
.andExpect(view().name("home")) // <5>
.andExpect(content().string( // <6>
containsString("Welcome to...")));
}
}
I am using springboot 2.7.3 so i just remove #RunWith(SpringRunner.class) from my code. and i immediately get an error when do the test:
after google that i realized it might not find my controller so I add #Import(HomeController.class) to the test class. It looks like:
package com.qph.tacos;
import org.hamcrest.Matchers;
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.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#WebMvcTest(HomeControllerTest.class)
#Import(HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testHomePage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("home"))
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("Welcome to...")));
}
}
then it just pass the test!!!
now i just wonder why the code written by the author can pass the test without #Import(HomeController.class)?
my directory structure is like:
the controller to be tested is:
package com.qph.tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/")
public String home() {
// return the name of template
return "home";
}
}
I am new in springboot. maybe it's a simple question but i really spent so much time on it.
Thanks for your help!
Change to
#WebMvcTest(HomeController.class)
You should reference the controller under test with this annotation, otherwise it won't be loaded into the application context.
I have a method that evicts all the caches. PFB code for same:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import com.admin.AdminResponse;
#Service
public class CachingService {
private final static Logger logger = LoggerFactory.getLogger(CachingService.class);
#Autowired
protected CacheManager cacheManager;
public AdminResponse evictAllCaches() {
logger.info("Start - Clearing of cache");
cacheManager.getCacheNames().parallelStream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
AdminResponse adminResponse = new AdminResponse();
adminResponse.setMessage("ok");
logger.info("End - Clearing of cache");
return adminResponse;
}
}
Below is the unit test I'm trying to write:
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.admin.AdminResponse;
#ExtendWith(SpringExtension.class)
public class CachingServiceTest {
#InjectMocks
private CachingService testCachingService;
#Mock
protected CacheManager cacheManager;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testEvictAllCaches() {
AdminResponse adminResponse = testCachingService.evictAllCaches();
assertEquals("ok", adminResponse.getMessage());
}
}
I'm unable to understand how to write unit tests for code
cacheManager.getCacheNames().parallelStream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
Can someone please help? Thank you for your time!
You can write test code for Cache as follow:
#Test
public void testEvictAllCaches() {
Cache cache = Mockito.mock(Cache.class);
when(cacheManager.getCacheNames()).thenReturn(List.of("cacheName1", "cacheName2"));
Mockito.when(cacheManager.getCache(anyString())).thenReturn(cache);
AdminResponse adminResponse = testCachingService.evictAllCaches();
assertEquals("ok", adminResponse.getMessage());
}
I have a RestController that I want to test:
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PetController implements PetApi {
#Autowired
PetRepository pr;
#Override
public ResponseEntity<Pet> addPet(#Valid Pet pet) {
pr.save(new PetBE(9L, "dummy"));
return new ResponseEntity<Pet>(pet, HttpStatus.OK);
}
}
import org.springframework.data.repository.CrudRepository;
public interface PetRepository extends CrudRepository<PetBE, Long> {
}
I want to mock PetRepository and test if the object passed is the object returned:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#SpringBootTest
public class PetControllerTest {
#InjectMocks
private PetController petController;
#MockBean
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
/*L35*/ ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
When I run this method as a gradle test, I get
com.example.petstore.backend.api.implementation.PetControllerTest > testAddPet() FAILED
java.lang.NullPointerException at PetControllerTest.java:35
which is petController.addPet(p1);.
My printlns in addPet are not displayed and I can't set any breakpoints there because it is mocked. The only reference in addPet that could be null is pr, but it works fine when I send a request with curl.
I've also tried adding
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
because it was suggested here but that gave an InitializationException:
com.example.petstore.backend.api.implementation.PetControllerTest > initializationError FAILED
org.junit.platform.commons.JUnitException at LifecycleMethodUtils.java:57
How can I debug this?
How can I get this to work?
You're mixing annotations from various testing frameworks here. If you wish to use the Mockito annotation #InjectMocks then I'd recommend not using any Spring-related mocking annotations at all, but rather the #Mock annotation to create a mocked version of the bean you want to inject (into the #InjectMocks-annotated field). Also make sure you bootstrap the Mockito extension with #ExtendWith(MockitoExtension.class). Something like:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#ExtendWith(MockitoExtension.class)
public class PetControllerTest {
#InjectMocks
private PetController petController;
#Mock
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
EDIT: Calling MockitoAnnotations.initMocks(this) inside for example a #BeforeEach-annotated method is necessary if you don't want to use the MockitoExtension. They're essentially the same thing, but it's less necessary in JUnit Jupiter because you can extend a test class with multiple extensions, which was not possible in JUnit 4.x. So if you wanted to bootstrap your test with both a Spring context and Mockito, then you had to pick one of them and setup the other one yourself.
Here's the problem: I keep running into the MissingMatrixVariableException in my stacktrace when attempting to test my Repository interfaces. I've got 5 and want to increase my overall code coverage.
Be nice - I'm a noob. I've attempted to Mock after doing my own research, and it's been a bit overwhelming.
I've tried Mocking the repository and instantiated my MockMvc mockMvc variables. I've setUp the MockitoAnnotations.initMocks(this);
I run the test and for any testing I've done, I've hit the MissingMatrixVariableException error.
I've found nothing in Stack / Google searches for this Exception when testing repositories.
package example.repository;
import example.model.Metrics;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
#EnableJpaRepositories
public interface MetricsRepository extends JpaRepository<Metrics, Long> {
List<Metrics>findByDate(String date);
#Modifying
#Transactional
#Query(nativeQuery = true, value = "delete from backlogreport.metrics where date = ?1")
void deleteDate(String date);
}
This is my test class:
package example.repository;
import example.controller.RestApiController;
import example.model.Metrics;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
import java.util.Collections;
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;
public class MetricsRepositoryTest {
#InjectMocks
private RestApiController restApiController;
#Mock
private MetricsRepository MetricsRepository;
private MockMvc mockMvc;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(restApiController).build();
}
#Test
public void shouldGetByDate() throws Exception {
Metrics Metrics = new Metrics();
Metrics.setDate("2019-04-01");
Metrics.setAgeLength("20 days old");
when(MetricsRepository.findAll())
.thenReturn(Collections.singletonList(Metrics));
mockMvc.perform(get("/report/2019-04-01"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(print());
}
}
What I'm trying to achieve is when the mockMvc is setup, I put in the date which accepts a String ("2019-04-01") and an age length of ("20 Days").
I then perform my urL "get" and expect the status is ok, then print... except I keep hitting this MissingMatrixVariableException. Any ideas?