I'm working on spring boot project. All things work perfectly but while unit testing saveBooking() method of controller then unit testing become failure. In Failure Trace I get java.lang.AssertionError: Response content expected:<Saved> but was:<SAVED>. This code worke perfectly in my postman but failure in unit testing.
Here down is my code:
Model
public class Booking {
private String bookingId;
private String passangerName;
private String flightName;
private String source;
private String destination;
// constructor, getter and setter
}
Controller
#RestController
public class BookingController {
#Autowired
private BookingService bookingService;
#PostMapping("/booking")
public String saveBooking(#RequestBody Booking booking) {
boolean saved = bookingService.saveBooking(booking);
return "SAVED";
}
}
TestController
#WebMvcTest(controllers = BookingController.class)
public class BookingControllerTest {
#MockBean
private BookingService bookingService;
#Autowired
private MockMvc mockMvc;
#Test
public void testSaveBooking() throws Exception
{
Mockito.when(bookingService.saveBooking(any())).thenReturn(true);
String bookingDetails = "{\r\n"
+ " \"bookingId\": \"AA0456\",\r\n"
+ " \"passangerName\": \"Michael\",\r\n"
+ " \"flightName\": \"Air American\",\r\n"
+ " \"source\": \"California\",\r\n"
+ " \"destination\": \"Dubai\"\r\n"
+ "}";
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/booking").contentType(MediaType.APPLICATION_JSON).content(bookingDetails);
mockMvc.perform(requestBuilder).andDo(print()).andExpect(status().isOk()).andExpect(content().string("Saved"));
}
}
SAVED != Saved
return "SAVED";
but expect
andExpect(content().string("Saved"));
Solution 1:
Try to use an enum so you can't misspell it
Solution 2:
check the string with .toLowerCase() when you check, so the case doesn't matter.
Solution 3:
Use
return "SAVED";
and
andExpect(content().string("SAVED"));
so the strings match.
Related
This is my CurriculoControllerTest.java class
#SpringBootTest
#ExtendWith(SpringExtension.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#DisplayName("Curriculo Controller Test")
#ActiveProfiles("local")
#AutoConfigureMockMvc
#Import(CurriculoController.class)
class CurriculoControllerTest {
private final String JSON_FORMAT = "application/json; charset=utf-8";
private final String BASE_PATH = "/curriculos";
#MockBean
private CurriculoServiceImpl curriculoService;
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
#Autowired
private MockMvc mockMvc;
public static CurriculoDTO createCurriculoInput() {
return CurriculoDTO.builder()
.id(UUID.randomUUID())
.dadosPessoais(DadosPessoaisDTO.builder()
.nome("joão")
.cargo("programador")
.email("joao#email.com")
.build())
.build();
}
CurriculoDTO novoCurriculo = CurriculoDTO.builder()
.id(UUID.randomUUID())
.dadosPessoais(DadosPessoaisDTO.builder()
.nome("Bruno")
.build())
.build();
CurriculoDTO curriculoExpected = CurriculoDTO.builder()
.id(UUID.randomUUID())
.dadosPessoais(DadosPessoaisDTO.builder()
.nome("Bruno")
.cargo("programador")
.email("joao#email.com")
.build())
.build();
#Test
#DisplayName("Deve retornar sucesso ao atualizar os dados pessoais do currículo")
public void deveRetornarSucessoAoAtualizarDadosPessoaisDoCurriculo() throws Exception {
var ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
var json = ow.writeValueAsString(curriculoExpected.getDadosPessoais());
doReturn(curriculoExpected).when(curriculoService)
.updateDadosPessoais(createCurriculoInput().getDadosPessoais(), novoCurriculo.getId());
mockMvc.perform(patch(BASE_PATH + "/dados-pessoais/" + createCurriculoInput().getId()).contentType(JSON_FORMAT).content(json))
.andExpect(status().isOk());
}
}
CurriculoController.java
#RestController
#RequestMapping("/curriculos")
public class CurriculoController {
private final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final CurriculoServiceImpl service;
#Autowired
public CurriculoController(CurriculoServiceImpl service) {
this.service = service;
}
#PatchMapping("/dados-pessoais/{id}")
public ResponseEntity<CurriculoDTO> updateDadosPessoais(#RequestBody #Valid DadosPessoaisDTO dto,
#PathVariable UUID id) {
Optional<CurriculoDTO> curriculo = Optional.ofNullable(service.findById(id));
if (curriculo.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(service.updateDadosPessoais(dto, id), HttpStatus.OK);
}
CurriculoServiceImpl
public CurriculoDTO updateDadosPessoais(DadosPessoaisDTO dto, UUID id) {
Optional<Curriculo> optCurriculo = repository.findById(id)
.map(curriculo -> {
curriculo.setNome(Objects.nonNull(dto.getNome())
? dto.getNome() : curriculo.getNome());
curriculo.setCargo(Objects.nonNull(dto.getCargo())
? dto.getCargo() : curriculo.getCargo());
curriculo.setEmail(Objects.nonNull(dto.getEmail())
? dto.getEmail() : curriculo.getEmail());
curriculo.setSumario(Objects.nonNull(dto.getSumario())
? dto.getSumario() : curriculo.getSumario());
curriculo.setLinguagem(Objects.nonNull(dto.getLinguagem())
? dto.getLinguagem() : curriculo.getLinguagem());
return repository.save(curriculo);
});
CurriculoDTO curriculoDTO = converter.mapCurriculoToCurriculoDTO(optCurriculo.orElse(null));
curriculoDTO.setDadosPessoais(dto);
return curriculoDTO;
}
I've tried dozens of different ways, but I keep getting the 404 error, even though my URL is correct, could it be because the ID is not being found?
java.lang.AssertionError: Status expected:<200> but was:<404>
Expected :200
Actual :404
You mocked CurriculoServiceImpl but haven't stubbed service.findById(id) - you get an empty curriculo and return HttpStatus.NOT_FOUND.
As a side note - you seem to be testing only one controller mocking a service it depends on - you may want to consider #WebMvcTest instead of #SpringBootTest
The purpose of this question is to find out if the codes are written with the right approach. Let's do CRUD operations on categories and posts in the blog website project. To keep the question short, I shared just create and update side.
(Technologies used in the project: spring-boot, mongodb)
Let's start to model Category:
#Document("category")
public class Category{
#Id
private String id;
#Indexed(unique = true, background = true)
private String name;
#Indexed(unique = true, background = true)
private String slug;
// getter and setter
Abstract BaseController class and IController Interface is created for fundamental level save, delete and update operations. I shared below controller side:
public interface IController<T>{
#PostMapping("/save")
ResponseEntity<BlogResponse> save(T object);
#GetMapping(value = "/find-all")
ResponseEntity<BlogResponse> findAll();
#GetMapping(value = "/delete-all")
ResponseEntity<BlogResponse> deleteAll();
}
public abstract class BaseController<T extends MongoRepository<S,String>, S> implements IController<S> {
#Autowired
private T repository;
#Autowired
private BlogResponse blogResponse;
#PostMapping(value = "/save", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public #ResponseBody ResponseEntity<BlogResponse> save(S object) {
try {
S model = (S) repository.save(object);
String modelName = object.getClass().getSimpleName().toLowerCase();
blogResponse.setMessage(modelName + " is saved successfully").putData(modelName, object);
} catch (DuplicateKeyException dke) {
return new ResponseEntity<BlogResponse>(blogResponse.setMessage("This data is already existing!!!"), HttpStatus.BAD_REQUEST);
} catch (Exception e) {
return new ResponseEntity<BlogResponse>(blogResponse.setMessage(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<BlogResponse>(blogResponse, HttpStatus.OK);
}
// delete, findAll and other controllers
#RestController
#RequestMapping(value = "category")
#RequestScope
public class CategoryController extends BaseController<ICategoryRepository, Category>{
// More specific opretions like findSlug() can be write here.
}
And finally BlogResponce component is shared below;
#Component
#Scope("prototype")
public class BlogResponse{
private String message;
private Map<String, Object> data;
public String getMessage() {
return message;
}
public BlogResponse setMessage(String message) {
this.message = message;
return this;
}
public BlogResponse putData(String key, Object object){
if(data == null)
data = new HashMap<String,Object>();
data.put(key,object);
return this;
}
public Map<String,Object> getData(){
return data;
}
#Override
public String toString() {
return "BlogResponse{" +
"message='" + message + '\'' +
", data=" + data +
'}';
}
}
Question: I am new spring boot and I want to move forward by doing it right. BlogResponse is set bean by using #Component annotation. This doc said that other annotations like #Controller, #Service are specializations of #Component for more specific use cases. So I think, I cant use them. BlogResponse is set prototype scope for create new object at each injection. Also it's life end after response because of #RequestScope. Are this annotations using correcty? Maybe there is more effective way or approach. You can remark about other roughness if it existing.
I have a rest controller like this;
#RestController
#RequiredArgsConstructor
#RequestMapping(PO)
public class PoController {
private final PoService service;
#GetMapping(value = FILTER, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<List<PoDTO>> filter(PoFilterCriteria poFilterCriteria) {
return ok().body(service.getPos(poFilterCriteria));
}
}
And I want to write an unit test for it but I couldn't achieve to mock the service to return list.
This is my poFilterCriteria model;
#Data
public class PoFilterCriteria {
private double hp;
private FilterOperationType hpOperationType;
private double attack;
private FilterOperationType attackOperationType;
private double defense;
private FilterOperationType defenseOperationType;
}
And this is my test;
#WebMvcTest(value = PoController.class)
class PoControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private PoService service;
private PoDTO poDTO;
private List<PoDTO> poDTOList;
#BeforeEach
void setUp() {
poDTOList = new ArrayList<>();
poDTO = new Po();
poDTOList.add(poDTO);
}
#Test
public void filter_success() throws Exception {
PoFilterCriteria poFilterCriteria= new PoFilterCriteria ();
poFilterCriteria.setAttack(40);
poFilterCriteria.setAttackOperationType(GT);
poFilterCriteria.setHp(49);
poFilterCriteria.setHpOperationType(EQ);
poFilterCriteria.setDefense(60);
poFilterCriteria.setDefenseOperationType(LT);
when(service.getPos(poFilterCriteria)).thenReturn(poDTOList);
mockMvc.perform(get(PO + FILTER)
.param("hp", String.valueOf(40))
.param("hpOperationType", String.valueOf(GT))
.param("attack", String.valueOf(49))
.param("attackOperationType", String.valueOf(EQ))
.param("defense", String.valueOf(60))
.param("defenseOperationType", String.valueOf(LT))
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(new ObjectMapper().writeValueAsString(poDTOList)));
}
}
But the list that should return with size of 1 is returning empty.
What did I do wrong?
org.mockito.ArgumentMatchers#any(java.lang.Class)
when(service.getPos(any(PoFilterCriteria.class))).thenReturn(poDTOList);
or
org.mockito.ArgumentMatchers#same
when(service.getPos(same(poFilterCriteria))).thenReturn(poDTOList);
I'm trying to test if #Async annotation of Spring is working as expected on my project. But It doesn't.
I have this test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = GlobalConfiguration.class)
public class ActivityMessageListenerTest {
#Autowired
private ActivityMessageListener activityMessageListener;
private Long USER_ID = 1l;
private Long COMPANY_ID = 2l;
private Date DATE = new Date(10000000);
private String CLASSNAME = "className";
private Long CLASSPK = 14l;
private Integer TYPE = 22;
private String EXTRA_DATA = "extra";
private Long RECIVED_USER_ID = 99l;
#Before
public void setup() throws Exception {
}
#Test
public void testDoReceiveWithException() throws Exception {
System.out.println("Current thread " + Thread.currentThread().getName());
Map<String, Object> values = new HashMap();
values.put(ActivityMessageListener.PARAM_USER_ID, USER_ID);
values.put(ActivityMessageListener.PARAM_COMPANY_ID, COMPANY_ID);
values.put(ActivityMessageListener.PARAM_CREATE_DATE, DATE);
values.put(ActivityMessageListener.PARAM_CLASS_NAME, CLASSNAME);
values.put(ActivityMessageListener.PARAM_CLASS_PK, CLASSPK);
values.put(ActivityMessageListener.PARAM_TYPE, TYPE);
values.put(ActivityMessageListener.PARAM_EXTRA_DATA, EXTRA_DATA );
values.put(ActivityMessageListener.PARAM_RECEIVED_USER_ID, RECIVED_USER_ID);
Message message = new Message();
message.setValues(values);
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
}
}
As you can see I'm printing the name of the current thread.
The class containing the #Async method is:
public class ActivityMessageListener extends BaseMessageListener {
public static final String PARAM_USER_ID = "userId";
public static final String PARAM_COMPANY_ID = "companyId";
public static final String PARAM_CREATE_DATE = "createDate";
public static final String PARAM_CLASS_NAME = "className";
public static final String PARAM_CLASS_PK = "classPK";
public static final String PARAM_TYPE = "type";
public static final String PARAM_EXTRA_DATA = "extraData";
public static final String PARAM_RECEIVED_USER_ID = "receiverUserId";
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
#Override
#Async(value = "activityExecutor")
public void doReceive(Message message) throws Exception {
System.out.println("Current " + Thread.currentThread().getName());
if (1> 0)
throw new RuntimeException("lalal");
Map<String, Object> parameters = message.getValues();
Long userId = (Long)parameters.get(ActivityMessageListener.PARAM_USER_ID);
Long companyId = (Long)parameters.get(ActivityMessageListener.PARAM_COMPANY_ID);
Date createDate = (Date)parameters.get(ActivityMessageListener.PARAM_CREATE_DATE);
String className = (String)parameters.get(ActivityMessageListener.PARAM_CLASS_NAME);
Long classPK = (Long)parameters.get(ActivityMessageListener.PARAM_CLASS_PK);
Integer type = (Integer)parameters.get(ActivityMessageListener.PARAM_TYPE);
String extraData = (String)parameters.get(ActivityMessageListener.PARAM_EXTRA_DATA);
Long receiverUserId = (Long)parameters.get(ActivityMessageListener.PARAM_RECEIVED_USER_ID);
ActivityLocalServiceUtil.addActivity(userId, companyId, createDate, className, classPK, type, extraData, receiverUserId);
}
}
Here I'm printing the name of the current thread inside of the #Async method, and the name is the same as before, main. So it's not working.
The GlobalConfiguration is:
#Configuration
#EnableAspectJAutoProxy
#EnableTransactionManagement
#ComponentScan({
"com.shn.configurations",
...some packages...
})
public class GlobalConfiguration {...}
And inside one of the specified packages has the activityExecutor bean:
#Configuration
#EnableAsync(proxyTargetClass = true)
public class ExecutorConfiguration {
#Bean
public ActivityMessageListener activityMessageListener() {
return new ActivityMessageListener();
}
#Bean
public TaskExecutor activityExecutor()
{
ThreadPoolTaskExecutor threadPoolTaskExecutor =
new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setQueueCapacity(100);
return threadPoolTaskExecutor;
}
}
What I'm doing wrong?
Tricky.
Asynchronous behavior is added through proxying.
Spring provides you with a proxy that wraps the actual object and performs the actual invocation in a separate thread.
It looks something like this (except most of this is done dynamically with CGLIB or JDK proxies and Spring handlers)
class ProxyListener extends ActivityMessageListener {
private ActivityMessageListener real;
public ProxyListener(ActivityMessageListener real) {
this.real = real;
}
TaskExecutor executor; // injected
#Override
public void doReceive(Message message) throws Exception {
executor.submit(() -> real.doReceive(message)); // in another thread
}
}
ActivityMessageListener real = new ActivityMessageListener();
ProxyListener proxy = new ProxyListener(real);
Now, in a Spring world, you'd have a reference to the proxy object, not to the ActivityMessageListener. That is
ActivityMessageListener proxy = applicationContext.getBean(ActivityMessageListener.class);
would return a reference to the ProxyListener. Then, through polymorphism, invoking doReceive would go to the overriden Proxy#doReceive method which would invoke ActivityMessageListener#doReceive through delegation and you'd get your asynchronous behavior.
However, you're in a half Spring world.
Here
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
the reference this is actually referring to the real ActivityMessageListener, not to the proxy. So when, presumably, you send your message on the bus here
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
you're sending it to the real object, which doesn't have the proxy asynchronous behavior.
The full Spring solution would be to have the MessabeBus (and/or its queue) be Spring beans in which you can inject the fully process (proxied, autowired, initialized) beans.
In reality, since CGLIB proxies are really just subclasses of your types, so the ProxyListener above would actually also add itself to the bus since the super constructor would be invoked. It would seem though that only one MessageListener can register itself with a key, like MKTDestinationNames.ACTIVITY_REGISTRY. If this isn't the case, you'd have to show more of that code for explanation.
In your test, if you do
activityMessageListener.doReceive(message);
you should see that asynchronous behavior since activityMessageListener should hold a reference to the proxy.
I'm trying to test some services with Mockito but I have problems when the main class that I test and where I inject Mocks calls to super.
I run the project with spring and these are the steps I follow to get the error.
Here is where I create the test
public class UrlShortenerTests {
private MockMvc mockMvc;
#Mock
private ShortURLRepository shortURLRepository;
#Mock
private ClickRepository clickRespository;
#InjectMocks
private UrlShortenerControllerWithLogs urlShortenerWL;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(urlShortenerWL).build();
}
#Test
public void thatShortenerCreatesARedirectIfTheURLisOK() throws Exception {
mockMvc.perform(post("/link")
.param("url", "http://www.google.com"))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.target", is("http://example.com/")));
}
}
Here is the class UrlShortenerControllerWithLogs with the method shortener, which is the one I want to test with the previous POST call
#RestController
public class UrlShortenerControllerWithLogs extends UrlShortenerController {
#Autowired
private ClickRepository clickRepository;
#Autowired
private ShortURLRepository SURLR;
public ResponseEntity<ShortURL> shortener(#RequestParam("url") String url,
#RequestParam(value = "sponsor", required = false) String sponsor,
#RequestParam(value = "brand", required = false) String brand,
HttpServletRequest request) {
ResponseEntity<ShortURL> su = super.shortener(url, sponsor, brand,
request);
return su;
}
And this is the super class
#RestController
public class UrlShortenerController {
#Autowired
protected ShortURLRepository shortURLRepository;
#Autowired
protected ClickRepository clickRepository;
#RequestMapping(value = "/link", method = RequestMethod.POST)
public ResponseEntity<ShortURL> shortener(#RequestParam("url") String url,
#RequestParam(value = "sponsor", required = false) String sponsor,
#RequestParam(value = "brand", required = false) String brand,
HttpServletRequest request) {
ShortURL su = createAndSaveIfValid(url, sponsor, brand, UUID
.randomUUID().toString(), extractIP(request));
if (su != null) {
HttpHeaders h = new HttpHeaders();
h.setLocation(su.getUri());
return new ResponseEntity<>(su, h, HttpStatus.CREATED);
} else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
protected ShortURL createAndSaveIfValid(String url, String sponsor,
String brand, String owner, String ip) {
UrlValidator urlValidator = new UrlValidator(new String[] { "http",
"https" });
if (urlValidator.isValid(url)) {
String id = Hashing.murmur3_32()
.hashString(url, StandardCharsets.UTF_8).toString();
ShortURL su = new ShortURL(id, url,
linkTo(
methodOn(UrlShortenerController.class).redirectTo(
id, null)).toUri(), sponsor, new Date(
System.currentTimeMillis()), owner,
HttpStatus.TEMPORARY_REDIRECT.value(), true, ip, null);
return shortURLRepository.save(su);
} else {
return null;
}
}
So, when I call to shortURLRepository.save(su) in the second method (createAndSaveIfValid), it never enters in the method save, so it returns me null instead of the object I want.
The code of the implementation of ShortURLRepository and the method save is:
#Repository
public class ShortURLRepositoryImpl implements ShortURLRepository {
private static final Logger log = LoggerFactory
.getLogger(ShortURLRepositoryImpl.class);
#Override
public ShortURL save(ShortURL su) {
try {
jdbc.update("INSERT INTO shorturl VALUES (?,?,?,?,?,?,?,?,?)",
su.getHash(), su.getTarget(), su.getSponsor(),
su.getCreated(), su.getOwner(), su.getMode(), su.getSafe(),
su.getIP(), su.getCountry());
} catch (DuplicateKeyException e) {
log.debug("When insert for key " + su.getHash(), e);
return su;
} catch (Exception e) {
log.debug("When insert", e);
return null;
}
return su;
}
I think that the problem is that the object ShortURLRepository created in the test class is not initialized on the super class (UrlShortenerController) or something similar.
Is it possible?
Can anybody help me?
The full code is in: https://github.com/alberto-648702/UrlShortener2014
The class UrlShortenerTests is in:
bangladeshGreen/src/test/java/urlshortener2014/bangladeshgreen
The class UrlShortenerControllerWithLogs is in:
bangladeshGreen/src/main/java/urlshortener2014/bangladeshgreen/web
The class UrlShortenerController is in:
common/src/main/java/urlshortener2014/common/web
The class ShortURLRepositoryImpl is in:
common/src/main/java/urlshortener2014/common/repository
This is not an error. This is the expected behaviour. #Mock creates a mock. #InjectMocks creates an instance of the class and injects the mocks that are created with the #Mock. A mock is not a real object with known values and methods. It is an object that has the same interface as the declared type but you control its behaviour. By default the mocked object methods do nothing (e.g. return null). Therefore if ShortURLRepository is mocked and injected in UrlShortenerControllerWithLogs calling save in the injected ShortURLRepository does not call the real code as you expected, it does nothing. If you want to mock the behaviour of save, add the following code in your setup:
when(shortURLRepository.save(org.mockito.Matchers.any(ShortURL.class))).
then(new Answer<ShortURL>() {
#Override
public ShortURL answer(InvocationOnMock invocation) throws Throwable {
ShortURL su = (ShortURL) invocation.getArguments()[0];
// Do something with su if needed
return su;
}
});