Issue
I am writing an app which is meant to help general doctors in treating elderly people. The goal is to avoid polypharmacy and it is based on the ATC (Anatomical Therapeutical Chemical) classification. I wrote a method that ads active substance to the DB and it works (I checked it on the h2 db) but I cannot write a proper test.
How can I investigate this problem?
Here is my test class:
package com.example.geriafarm.controllers;
import com.example.geriafarm.DTO.ATCdto;
import com.example.geriafarm.DTO.ActiveSubstDTO;
import com.example.geriafarm.entities.ATC;
import com.example.geriafarm.entities.ActiveSubst;
import com.example.geriafarm.repositories.ActiveSubstRepository;
import com.example.geriafarm.services.ATCService;
import com.example.geriafarm.services.ActiveSubstService;
import org.junit.jupiter.api.Test;
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.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.transaction.annotation.Transactional;
import java.util.UUID;
import static java.lang.String.format;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.hamcrest.Matchers.is;
@SpringBootTest
@AutoConfigureMockMvc
public class ActiveSubstControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ActiveSubstService activeSubstService;
@Autowired
ActiveSubstRepository activeSubstRepository;
@Test
@Transactional
void shouldAddActiveSubstance() throws Exception {
final UUID id = new UUID(1234,567);
final String testName = "furosemid";
final String atcDTOTest = "C03CA01";
final ATC testATC = new ATC(null, "C","03","C","A","01");
final ActiveSubst activeSubstTest = new ActiveSubst(id,testName, testATC);
when(activeSubstService.addSubstance(any(ActiveSubstDTO.class))).thenReturn(activeSubstTest.getId());
mockMvc.perform(
post("/activesubstances")
.contentType(MediaType.APPLICATION_JSON)
.content(format("{\"name\":\"%s\",\"atc\":\"%s\"}", testName, atcDTOTest)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name", is(testName)))
.andExpect(jsonPath("$.atc", is(atcDTOTest)));
}
}
This is the ActiveSubstanceService class:
package com.example.geriafarm.services.implementation;
import com.example.geriafarm.DTO.ATCdto;
import com.example.geriafarm.DTO.ActiveSubstDTO;
import com.example.geriafarm.entities.ATC;
import com.example.geriafarm.entities.ActiveSubst;
import com.example.geriafarm.repositories.ATCRepository;
import com.example.geriafarm.repositories.ActiveSubstRepository;
import com.example.geriafarm.services.ActiveSubstService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Service
public class DefaultActiveSubstService implements ActiveSubstService {
private final ActiveSubstRepository activeSubstRepository;
private final ATCRepository atcRepository;
public DefaultActiveSubstService(ActiveSubstRepository activeSubstRepository, ATCRepository atcRepository) {
this.activeSubstRepository = activeSubstRepository;
this.atcRepository = atcRepository;
}
@Override
public List<ActiveSubstDTO> getSubstances() {
return null;
}
public static ATC createATCEntity(ATCdto atcDto) { //metoda przepisuje dane z frontendu(ATCdto) do poszczególnych pól w bazie danych
ATC atcEntity = new ATC();
atcEntity.setAnatomicalGr(atcDto.getAtcDto().substring(0, 1));
atcEntity.setTherapeutSubgr(atcDto.getAtcDto().substring(1, 3));
atcEntity.setPharmacolSubgr(atcDto.getAtcDto().substring(3, 4));
atcEntity.setChemicalSubgr(atcDto.getAtcDto().substring(4, 5));
atcEntity.setChemicalSubst(atcDto.getAtcDto().substring(5));
return atcEntity;
}
@Override
public UUID addSubstance(ActiveSubstDTO activeSubstDTO) {
final ActiveSubst activeSubst = activeSubstRepository.saveAndFlush(new ActiveSubst(null, activeSubstDTO.getName(), createATCEntity(activeSubstDTO.getAtc())
)
);
return activeSubst.getId();
}
@Override
public Optional<ActiveSubstDTO> getSubstanceById(String id) {
return activeSubstRepository.findById(UUID.fromString(id)).map(ActiveSubstDTO::fromActiveSubstEnt);
}
@Override
public ActiveSubstDTO updateSubstance(String id, ActiveSubstDTO activeSubstDTO) {
return null;
}
@Override
public List<ActiveSubstDTO> getSubstancesByMedicine(UUID medicineId) {
return null;
}
@Override
public List<ActiveSubstDTO> getSubstancesByAnatomicalGroup(ATCdto atcDto) {
return null;
}
@Override
public List<ActiveSubstDTO> getSubstancesByTherapeuticSubgroup(ATCdto atcDto) {
return null;
}
}
And finally the controller:
package com.example.geriafarm.controllers;
import com.example.geriafarm.DTO.ActiveSubstDTO;
import com.example.geriafarm.exceptions.GeriaException;
import com.example.geriafarm.services.ActiveSubstService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.UUID;
@RestController
@RequestMapping("/activesubstances")
public class ActiveSubstController {
private final ActiveSubstService activeSubstService;
public ActiveSubstController(ActiveSubstService activeSubstService) {
this.activeSubstService = activeSubstService;
}
@PostMapping
public ResponseEntity<Void> addActiveSubst (@RequestBody ActiveSubstDTO activeSubstDTO) throws GeriaException, URISyntaxException {
UUID activeSubstId = activeSubstService.addSubstance(activeSubstDTO);
return ResponseEntity
.created(new URI("/activesubstances/" + activeSubstId))
.build();
}
}
Edit: And here's the stack of error that I get:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /activesubstances
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"36"]
Body = {"name":"furosemid","atc":"C03CA01"}
Session Attrs = {}
Handler:
Type = com.example.geriafarm.controllers.ActiveSubstController
Method = com.example.geriafarm.controllers.ActiveSubstController#addActiveSubst(ActiveSubstDTO)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Location:"/activesubstances/00000000-0000-04d2-0000-000000000237"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = /activesubstances/00000000-0000-04d2-0000-000000000237
Cookies = []
MockHttpServletRequest:
HTTP Method = POST
Request URI = /activesubstances
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"36"]
Body = {"name":"furosemid","atc":"C03CA01"}
Session Attrs = {}
Handler:
Type = com.example.geriafarm.controllers.ActiveSubstController
Method = com.example.geriafarm.controllers.ActiveSubstController#addActiveSubst(ActiveSubstDTO)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Location:"/activesubstances/00000000-0000-04d2-0000-000000000237"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = /activesubstances/00000000-0000-04d2-0000-000000000237
Cookies = []
2021-06-30 17:45:11.429 INFO 20568 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@43aaf813 testClass = ActiveSubstControllerTest, testInstance = com.example.geriafarm.controllers.ActiveSubstControllerTest@7d28efd5, testMethod = shouldAddActiveSubstance@ActiveSubstControllerTest, testException = java.lang.AssertionError: No value at JSON path "$.name", mergedContextConfiguration = [WebMergedContextConfiguration@57ac5227 testClass = ActiveSubstControllerTest, locations = '{}', classes = '{class com.example.geriafarm.GeriafarmApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@4ba302e0 key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@23202fce, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6f1c29b7, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@763dd5d5, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2b72cb8a, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@5d99c6b5, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4b3fa0b3, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@52e7a6b2, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@133e16fd], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
java.lang.AssertionError: No value at JSON path "$.name"
at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:304)
at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:73)
at org.springframework.test.web.servlet.result.JsonPathResultMatchers.lambda$value$0(JsonPathResultMatchers.java:87)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
at com.example.geriafarm.controllers.ActiveSubstControllerTest.shouldAddActiveSubstance(ActiveSubstControllerTest.java:63)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalArgumentException: json can not be null or empty
at com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:386)
at com.jayway.jsonpath.JsonPath.read(JsonPath.java:342)
at com.jayway.jsonpath.JsonPath.read(JsonPath.java:329)
at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:301)
... 69 more
I'm new to Java and coding in general.
Solution
Can you put the stack of error in your question?
Maybe your json from return of body of request, hasn`t a property with "name".
with your stack your body from return is empty.
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Location:"/activesubstances/00000000-0000-04d2-0000-000000000237"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = /activesubstances/00000000-0000-04d2-0000-000000000237
Cookies = []
you can remove the test of content like:
mockMvc.perform(
post("/activesubstances")
.contentType(MediaType.APPLICATION_JSON)
.content(format("{\"name\":\"%s\",\"atc\":\"%s\"}", testName, atcDTOTest)))
.andDo(print())
.andExpect(status().isCreated())
or you can put the responseEntity from controller to return a DTO object with informations, and check it.
Answered By - Lucas Costa