StackOverflow error on unit testing API controllers? - java

I'm working on a Spring Boot MVC project and I'm getting the following error when unit testing my CRUD controllers :
java.lang.StackOverflowError
at java.base/java.lang.Throwable.getOurStackTrace(Throwable.java:840)
at java.base/java.lang.Throwable.getStackTrace(Throwable.java:832)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:55)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
java.lang.StackOverflowError
at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:834)
at java.base/java.lang.StringBuilder.append(StringBuilder.java:247)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.subjoinSTEPArray(ThrowableProxyConverter.java:216)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.recursiveAppend(ThrowableProxyConverter.java:161)
at ch.qos.logback.classic.pattern.ThrowableProxyConverter.recursiveAppend(ThrowableProxyConverter.java:168)
Here's one of the controllers unit test, the others follow the same logic.
#Autowired
private MockMvc mockMvc;
#MockBean
private PessoaRepository pessoaRepository;
#Test
public void deletarPessoa_quandoDelete() throws Exception {
Pessoa pessoa = new Pessoa();
pessoa.setNome("Test Nome");
pessoa.setId(89L);
doNothing().when(pessoaRepository).delete(pessoa);
mockMvc.perform(delete("/pessoas/" + pessoa.getId().toString())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
}
What could this be? I have no idea why I'm getting this error.
EDIT : here's the controller, it basically calls for the repo to delete a Pessoa object
#DeleteMapping("/{id}")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void deletar(#ApiParam(value = "ID", example = "1") #PathVariable Long id) {
Pessoa pessoaDeletada = pessoaRepository.getOne(id);
pessoaRepository.delete(pessoaDeletada);
}

Related

DB test entries not showing when called via api

I am trying to test my API in a Quarkus APP.
My test setup is I define an object and persist it to the DB with a direct call to the service object. I then call the API and expect to get the object back, but I don't....
My test class setup is;
#QuarkusTest
#TestTransaction
#TestHTTPEndpoint(CompanyController.class)
public class CompanyControllerIntegrationTest {
#Inject
CompanyService service;
#ClassRule
private static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:13.3-alpine")
.withDatabaseName("db")
.withUsername("user")
.withPassword("password");
#Test
void test_getCompanyThatExist() {
service.createCompany(createCompany());
given()
.when().get("/1")
.then().statusCode(200)
.body("size()", is(1));
}
private Company createCompany() {
Company company = new Company();
return company;
}
}
Controller endpoint is;
#GET
#Path("/{id}")
public Response getCompany(#PathParam("id") Long id) {
System.out.println("[CompanyController] Getting company with id - " + id);
Company company = service.getCompany(id);
System.out.println("[CompanyController] Company got was " + company);
return Response
.ok()
.entity(company)
.build();
Service call is;
public Company getCompany(Long id) {
Company company = repository.findById(id);
System.out.println("[CompanyService] Got company - " + company);
return company;
}
And the print outs, which really confuses me....
So the object is persisted with an ID of 1, but when I go to get the object with the ID of 1 its null. Any ideas why? As I am completely stumped at this stage.
QuarkusTest annotation uses JUnit Jupiter's ExtendWith annotation so in this case you should use #Container instead of #ClassRule, add #Testcontainer at class level and add org.testcontainers:junit-jupiter to your pom.xml or gradle.build
Your test should looks like
#Testcontainers
#QuarkusTest
#TestTransaction
#TestHTTPEndpoint(CompanyController.class)
public class CompanyControllerIntegrationTest {
#Inject
CompanyService service;
#Container
private static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:13.3-alpine")
.withDatabaseName("db")
.withUsername("user")
.withPassword("password");
}
The container can be started/stopped manually too
#QuarkusTest
#TestTransaction
#TestHTTPEndpoint(CompanyController.class)
public class CompanyControllerIntegrationTest {
#Inject
CompanyService service;
private static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:13.3-alpine")
.withDatabaseName("db")
.withUsername("user")
.withPassword("password");
#BeforeAll
void beforeAll() {
db.start();
}
#AfterAll
void beforeAll() {
db.stop();
}
}

How create unit test in Spring REST in POST Cotroller

I have a Spring Boot Controller with POST. Method works fine. I tested it by POSTMAN and from postgresql I recieved JSON. But I need test it.
#RestController
#RequestMapping("/api")
public class FamilyController {
private final FamilyService familyService;
public FamilyController(FamilyService familyService) {
this.familyService = familyService;
}
#GetMapping("/getFamily/{familyId}")
public List<FamilyMember> getFamily(#PathVariable Integer familyId) {
return familyService.searchFamilyMember(familyId);
}
}
I created test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(FamilyController.class)
class FamilyControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private FamilyService service;
#Captor
private ArgumentCaptor<Family> argumentCaptor;
#Test
void createFamily() throws Exception {
Family family = new Family();
family.setId(1);
family.setFamilyName("Kowal");
family.setNrOfInfants(2);
family.setNrOfChildren(2);
family.setNrOfAdults(1);
Mockito.when(service.saveAndFlush(argumentCaptor.capture())).thenReturn(1);
mockMvc.perform(post("/api/createFamily")
.contentType(MediaType.APPLICATION_JSON)
.contentType(objectMapper.writeValueAsString(family)))
.andExpect(status().isCreated());
// .andExpect(header().exists("Location"))
// .andExpect(header().string("Location", "http://localhost/api/getFamily/1"));
assertThat(argumentCaptor.getValue().getFamilyName(), is("Kowal"));
}
when I run a test, I received error like below:
WARN 15404 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException:
Invalid mime type "{"id":1,"familyName":"Kowal","nrOfInfants":2,"nrOfChildren":2,"nrOfAdults":1,"familyMembers":[]};charset=UTF-8":
does not contain '/']
What am I doing wrong?
The problem is this line:
.contentType(objectMapper.writeValueAsString(family)))
Right now you are sending the object in the content-type header, that's why you get the error message.
Change it to:
.content(objectMapper.writeValueAsString(family)))

Getting blank response in spring rest controller unit test cases

I have written unit test cases for a spring rest controller for below put method
#PutMapping("/offers/{jobTitle}")
public Offer updateOffer(#PathVariable String jobTitle,#Valid #RequestBody Offer offer) {
return offerService.updateNoOfPost(jobTitle, offer);
}
Below is my service class
#Override
public Offer updateNoOfPost(String jobTitle, Offer offer) {
if(!offerRepository.existsById(jobTitle))
throw new ResourceNotFoundException("JobTitle "+jobTitle+" not found !!");
offer.setNoOfPost(offer.getNoOfPost());
return offerRepository.save(offer);
}
I have written the unit test case for this method using testNg and mockito
public class OfferControllerTest {
private MockMvc mvc;
private JacksonTester<Offer> jsonOffer;
#Mock
private OfferService service;
#InjectMocks
OfferController offerController;
private Offer offer;
#BeforeMethod
public void setup() {
offer = new Offer("LSE", new Date(),1);
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(offerController)
.build();
JacksonTester.initFields(this, new ObjectMapper());
}
#Test
public void updateOffer() throws Exception {
Mockito.when(service.updateNoOfPost("LSE", offer)).thenReturn(offer);
MockHttpServletResponse response = mvc.perform(
put("/offers/LSE").contentType(MediaType.APPLICATION_JSON).content(
jsonOffer.write(new Offer("SE", new Date(), 19)).getJson()
)).andReturn().getResponse();
assertThat(response.getContentAsString()).isEqualTo(new ObjectMapper().writeValueAsString(offer));
}
I am getting response code as 200. but getting blank body.pls find error below
FAILED: updateOffer
org.junit.ComparisonFailure: expected:<"[{"jobTitle":"LSE","createdAt":"2018-10-27","noOfPost":1}]"> but was:<"[]">
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
what am i missing ? is this the standard way of writting unit test cases for spring rest controller ?
Below mocking should fix the issue
Mockito.when(service.updateNoOfPost(Mockito.any(String.class), Mockito.any())).thenReturn(offer);
Read more here: stack-46914252

Cannot inject service into Spring test

financialReportService is null in REST controller that denotes it fails to inject.
Test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SnapshotfindocApp.class)
public class FindocResourceIntTest {
#Inject
private FinancialReportService financialReportService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
FindocResource findocResource = new FindocResource();
ReflectionTestUtils.setField(findocResource, "findocRepository", findocRepository);
this.restFindocMockMvc = MockMvcBuilders.standaloneSetup(findocResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setMessageConverters(jacksonMessageConverter).build();
}
#Test
#Transactional
public void getFinancialRecords() throws Exception {
// Get all the financial-reports
restFindocMockMvc.perform(get("/api/financial-reports"))
.andExpect(status().isOk());
List<Findoc> finReports = financialReportService.getFinancialReports();
for (Findoc fr : finReports) {
assertThat(fr.getNo_months()).isBetween(12, 18);
LocalDate documentTimeSpanLimit = LocalDate.now().minusMonths(18);
assertThat(fr.getFinancial_date()).isAfterOrEqualTo(documentTimeSpanLimit);
}
}
The service:
#Service
#Transactional
public class FinancialReportService {
private final Logger log = LoggerFactory.getLogger(FinancialReportService.class);
#Inject
private FinancialReportDAO financialReportDAO;
public List<Findoc> getFinancialReports(){
return financialReportDAO.getFinancialReports();
}
}
Controller:
#GetMapping("/financial-reports")
#Timed
public List<Findoc> getFinancialReports() {
log.debug("REST request to get financial records");
return financialReportService.getFinancialReports(); // financialReportService is null
}
Update:
The application is generated by JHipster. Then the new service and DAO files were added to enable custom database queries to H2.
After #Injecting the service, you also need to set the field in the setup() method. Adding the below line should solve your problem
ReflectionTestUtils.setField(findocResource, "financialReportService", financialReportService);
On a separate note, the following part of the test looks weird. You are fetching the financial reports twice. This file is the FindocResourceIntTest, so I would remove any direct calls to financialReportService.
// Get all the financial-reports
restFindocMockMvc.perform(get("/api/financial-reports"))
.andExpect(status().isOk());
List<Findoc> finReports = financialReportService.getFinancialReports();

jsonRootName missing while performing unit test case for spring hateoas

I have developed an rest service using spring-boot-starter-Hateoas, and I am able to get the json output properly as shown below:
"_embedded": {
"bills":
{
uid: "123"
code: "0000"
And I need to write unit-test case for the same using mockito. The code I have written is as below.
ApplicationTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ApplicationTest {
BillControllerAutoTest:
public class BillControllerAutoTest {
private BillService mockBillService;
private MockMvc mockMvc;
private static final String BILL_UID = "99991";
#Before
public void setupController() {
mockBillService= mock(BillService .class);
BillController controller = new BillController (mockBillService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testGetBills() throws Exception {
// some fake data
final List<Bill> fakeBillList= new ArrayList<>();
fakeBillList.add(BillFake.bill("1234"));
when(mockBillService.getBills(BILL_UID))
.thenReturn(fakeBillList.stream());
// execute and verify
mockMvc.perform(get("/bills/" + BILL_UID ))
.andExpect(content().string(containsString("\"embedded\":{\"bills\"")))
BillController.java:
#RestController
#RequestMapping(value = "/bills/{billUid}", produces = "application/hal+json")
public class BillController extends BaseController {
private BillService billService;
#RequestMapping(method = RequestMethod.GET, value = "")
public ResponseEntity<Resources<Resource<Bill>>> getBills(#PathVariable String billUid) {
return resourceListResponseEntity(
() -> billService.getBills(billUid),
bill-> createResource(billUid),
resources -> resources.add(linkTo(methodOn(BillController .class)
.getBills(billUid)).withSelfRel()));
}
Dependencies:
dependencies {
compile "org.springframework.boot:spring-boot-starter-hateoas"
compile "org.springframework.boot:spring-boot-starter-ws"
compile "org.springframework.boot:spring-boot-starter-actuator"
testCompile("org.springframework.boot:spring-boot-starter-test")
}
My build is failing with the following stackTrace:
java.lang.AssertionError: Response content
Expected: a string containing "\"_embedded\":{\"bills\""
but: was
"content":[
{
uid: "123"
code: "0000"
This means "_embedded : { bills" is not available in the response returned by mockMvc of the unit test. Am I missing any configuration, kindly let me know. Any help would be greatly appreciated.
I've answered very similar question here: Spring's MockMVC Responses Don't Match Browser Response
In a nutshell: spring HATEOAS adds additional configuration for rendering hal properly (as described here: http://docs.spring.io/spring-hateoas/docs/0.19.0.RELEASE/reference/html/#configuration). In your tests you have to apply this configuration manually. Check the first link for details on how to do it

Categories