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();
}
}
Related
I have service method "customerDetails" to get more info about customer and change some field that using another method inside it "getById" when I do the unit test for customerDetails I mock the other method but the test faild because the mock return null . I try some solutions I found like checking the order of dependencies and using #InjectMocks (which I do)but they did not work for me and I do not know where the problem is.
code snippet to understand me better
customerService
public class customerService {
public Customer customerDetails(int id) {
CustomerDto customer = getById(id) //here is the problem
// rest of the code
}
public CustomerDto getById(int id) {
Optional<Customer> customer =
this.customerRepository.findCustomerByIdAndIsDeletedFalse(id); //return null here
if (!customer.isPresent()) {
// code to throw Exception customer not found
}
//code to retrieve customer
}
}
customerServiceTest
public class CustomerServiceTest {
#Mock
private CustomerRepository customerRepository;
#InjectMocks
private CustomerService customerService;
#BeforeEach
public void createMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCustomerDetails() {
CustomerDto actualResponse = DummyCutomer.createDto(); // the actualResponse is filled successfully
when(customerService.getById(actualResponse.getId()).thenReturn(actualResponse); // but here it send to getById null !!
//rest of code
}
}
You need to mock the customerRepository not the getById method
For example:
when(customerRepository.findCustomerByIdAndIsDeletedFalse(actualResponse.getId()).thenReturn(Optional.of(WHAT_EVER_YOU_WANT));
In your case I think you have tow scenarios:
findCustomerByIdAndIsDeletedFalse => Optional.of(a customer instance)
findCustomerByIdAndIsDeletedFalse => Optional.empty()
I have s service methos which i want to test:
#Override
public void updateImage(long id, ImageAsStream imageAsStream) {
Product product = productRepository.findById(id)
.orElseThrow(() -> new ProductException("Product can not be found"));
updateProductImage(imageAsStream, product.getImage().getId());
}
private void updateProductImage(ImageAsStream imageAsStream, Long existingImageId) {
imageRepository.updateProductImage(existingImageId, imageAsStream);
imageRepository.copyImageToThumbnail(existingImageId);
}
So to be able to call service method, i need to mock imageRepository somehow:
#Test
void updateProductImage() {
when(imageRepository)
.updateProductImage(1L, imageAsStream).thenReturn(???);
productService.updateProductImage(1L, imageAsStream);
}
Can you please advise whats the general approach in such cases?
When I would need to test this method, then these things need to be validated:
The id is of an existing product and there is a call to the imageRepository to update the product image
The id is not of an existing product. An exception is thrown and nothing is saved in the imageRepository
For your question, it does not really matter what you return there. It can be a mock of Product, or it can be a real instance.
My preference is usually to have an Object Mother, for example ProductMother to create a "default" instance.
In code:
class ProductServiceTest {
#Test
void testHappyFlow() {
ProductRepository repository = mock(ProductRepository.class);
ProductService service = new ProductService(repository);
when(repository.findById(1L))
.thenReturn(ProductMother.createDefaultProduct());
ImageAsStream imageAsStream = mock(ImageAsStream.class);
service.updateImage(1L, imageAsStream);
verify(repository).updateProductImage(1L, imageAsStream);
verify(repository).copyImageToThumbnail(1L);
}
#Test
void testProductNotFound() {
ProductRepository repository = mock(ProductRepository.class);
ProductService service = new ProductService(repository);
assertThatExceptionOfType(ProductException.class)
.isThrownBy( () -> {
ImageAsStream imageAsStream = mock(ImageAsStream.class);
service.updateImage(1L, imageAsStream);
});
}
}
I have a REST API written in Spring Boot.
Now I want to create unit tests with JUnit and Mockito that mock the database operations.
Here is the endpoint that inserts a new customer to the db.
#RestController
public class CustomerController {
#Autowired
private CustomerRepository customerRepository;
#PostMapping(value="/customers", produces = "application/json")
public ResponseEntity<Customer> addCustomer(#RequestBody Customer customer){
Customer newCustomer = customerRepository.save(customer);
return new ResponseEntity<Customer>(newCustomer, HttpStatus.OK) ;
}
Test:
#Test
public void testAddCustomer() {
Customer customer = Mockito.mock(Customer.class);
customer.setIdCustomer(1L);
customer.setName("Pete");
customer.setAge(35);
customer.setEmail("pete#test.com");
when(customerRepository.save(customer)).thenReturn((Customer) Stream.of(customer));
CustomerController customerController = new CustomerController();
ResponseEntity<Customer> respCustomer = customerController.addCustomer(customer);
assertTrue(respCustomer.getBody().getIdCustomer() != null);
}
I'm getting a null pointer exception on the following line:
when(customerRepository.save(customer)).thenReturn((Customer) Stream.of(customer));
Use
#Mock
private CustomerRepository customerRepository
in your test and configure the repository using mockito API
it should be something like this
#Test
public void testFindTheGreatestFromAllData() {
DataService dataServiceMock = mock(DataService.class);
when(dataServiceMock.retrieveAllData()).thenReturn(new int[] { 24, 15, 3 });
SomeBusinessImpl businessImpl = new SomeBusinessImpl(dataServiceMock);
int result = businessImpl.findTheGreatestFromAllData();
assertEquals(24, result);
}
This is the service I have :
#Service
public class UserInfoService {
#Autowired
private UserInfoServiceClient UserInfoServiceClient; // Call another Rest API
public ResponseEntity<ResponseUserInfoData> sendUserInfo(String UserId) throws RuntimeException {
ResponseUserInfoData responseUserInfoData = new ResponseUserInfoData();
//Get the body from the User service client
UserServiceDTO UserServiceDTO = UserInfoServiceClient.sendResponse(UserId).getBody();
//Set the values of responseUserInfoData
Optional<UserServiceDTO> UserServiceDTOOptional = Optional.ofNullable(UserServiceDTO);
if (UserServiceDTOOptional.isPresent()) {
UserServiceDTOOptional.map(UserServiceDTO::getId).ifPresent(responseUserInfoData::setid);
}
else return ResponseEntity.noContent().build();
}
}
I have to test it. I'm new to JUnit testing. I want to test the following points:
To check if the service return the response entity
To check if the get and set method works
This is what I started?
#RunWith(MockitoJUnitRunner.class)
public class ServiceTests {
#InjectMocks
private UserInfoService UserInfoService;
#Mock
private UserInfoServiceClient UserInfoServiceClient;
#Mock
private UserServiceDTO UserServiceDTO;
#Test
public void shouldReturnUserInfoData() throws IOException{
UserInfoService.sendUserInfo("ABC");
}
}
Any help is appreciated?
Mockito is useful to mock the dependencies of the service so that you can test all the code path in you service. In your case you will want to stub the call to serInfoServiceClient.sendResponse(UserId) and have it return a specific UserServiceDTO for each test case.
The test file looks like it is set up correctly, you only need to mock the method to give you the result you need for the particular test, for example
#RunWith(MockitoJUnitRunner.class)
public class ServiceTests {
#InjectMocks
private UserInfoService UserInfoService;
#Mock
private UserInfoServiceClient UserInfoServiceClient;
#Test
public void shouldReturnUserInfoData() throws IOException{
final String userId = "123";
// The mocked return value should be set up per test scenario
final UserServiceDto dto = new UserServiceDto();
final ResponseEntity<UserServiceDTO> mockedResp = new ResponseEntity<>(dto, HttpStatus.OK);
// set up the mock service to return your response
when(UserInfoServiceClient.sendResponse(userId)).thenReturn(mockedResp);
// Call your service
ResponseEntity<ResponseUserInfoData> resp = UserInfoService.sendUserInfo(userId);
// Test the result
Assert.isNotNull(resp);
}
}
There are also other ways to mock the dependencies using Mockito. I suggest going through the quick start of https://site.mockito.org/
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();