Is it possible to test methods of serviceA and tell the test environment to replace all calls from serviceA to serviceB with a mockServiceB?
This is the method I´d like to test (ServiceA.java):
#Inject
ServiceB serviceB;
public boolean tokenExists(ItemModel item) throws Exception {
try {
List<ItemModel > items = serviceB.getItems();
String token = item.getToken();
return items.stream().filter(i -> i.getToken().equals(token)).findFirst().isPresent();
} catch (ApiException e) {
throw new Exception(500, "Data could not be fetched.", e);
}
}
Since serviceB.getItems() will result in a REST-call, I'd like to replace calls to serviceB with a custom mockServiceB where I just load mock data from a json file like this (TestServiceA.java):
#InjectMock
ServiceB serviceB;
#ApplicationScoped
public static class ServiceB {
private ObjectMapper objMapper = new ObjectMapper();;
public List<ItemModel> getItems() throws IOException {
final String itemsAsJson;
try {
itemsAsJson= IOUtils.toString(new FileReader(RESOURCE_PATH + "items.json"));
objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
List<Item> items= objMapper.readValue(itemsAsJson, objMapper.getTypeFactory().constructCollectionType(List.class, ItemModel.class));
return items;
} catch (IOException e) {
throw e;
}
}
}
And then test the tokenExists method like this (TestServiceA.java):
#Test
public void testTokenExists() {
try {
Assertions.assertTrue(this.serviceA.tokenExists(this.getTestItem()));
} catch (Exception e) {
fail("exception");
}
}
However, when I run the test testTokenExists it still calls the original serviceB.getItems(). So my question would be if this is even possible or if I have to take a different approach.
You need to mock the serviceB inside ServiceA class, you have two different options:
First
You can use whitebox or reflection and assign the mocked the serviceB to ServiceA class
public class TestExample {
#Mock
private ServiceB mockedServiceB;
private ServiceA serviceA;
#Before
public void setUp() {
serviceA = new ServiceA();
}
#Test
public void shouldDoSomething() {
// Arrange
Whitebox.setInternalState(serviceA, "serviceB", mockedServiceB);
when(serviceB.getItems()).thenReturn(list);
// Act & Assert
Assertions.assertTrue(serviceA.tokenExists(this.getTestItem()));
}
}
Second
You can use #InjectMocks
public class TestExample {
#Mock
private ServiceB mockedServiceB;
#InjectMocks
private ServiceA serviceA;
#Before
public void setUp() {
serviceA = new ServiceA();
}
#Test
public void shouldDoSomething() {
// Arrange
List<Item> list = new ArrayList<>();
list.add(new Item(...)); // add whatever you want to the list
when(serviceB.getItems()).thenReturn(list);
// Act & Assert
Assertions.assertTrue(serviceA.tokenExists(this.getTestItem()));
}
}
Note: prepare your runner also
Related
I am trying to write Junit test cases for a void method. This method is used for updating values in Database. I have tried certain test cases and its returning a success. But when I check the coverage its showing zero. Can anyone tell me the proper way to write test cases for void methods.
this is my service class :
public class CustomerServiceImpl implements CustomerService {
#Autowired
ERepository eRepository;
#Autowired
ActivityUtil activityUtil;
#Override
public void updateCustomer(RequestDTO requestDTO)
throws CustomAException {
if (Objects.nonNull(requestDTO.getAdmissionId())) {
Optional<Admission> optionalAdmission = eRepository.findById(
requestDTO.getAdmissionId());
if (optionalAdmission .isPresent()) {
EAdmission eAdmission = optionalAdmission.get();
updateCustomer(requestDTO, eAdmission);
} else {
throw new CustomAException ("Admission details not found");
}
}
else {
throw new CustomAException ("Admission id not found");
}
}
private void updateCustomer(RequestDTO requestDTO,
EAdmission eAdmission)
throws CustomAException {
logger.info("updating customer info");
try {
if (ObjectUtils.isNotEmpty(eAdmission.getCustomer())) {
eAdmission.getCustomer().setCustomerEmailAddress(
requestDTO.getEmail());
eAdmission.getCustomer().setCorporateTelephoneNumber(
requestDTO.getCustomerPhone());
eAdmission.getCustomer().setZipCode(requestDTO.getZipCode());
eAdmission.getCustomer().setCustomerAddress1(requestDTO.getAddress1());
evfAdmissionRepository.save(evfAdmission);
activityUtil.createActivityLog(eAdmission, Constants.ENTRY_COMPLETED);
} else {
throw new CustomAException ("Customer details not found ");
}
} catch (Exception exception) {
logger.error(Constants.CUSTOMER_UPDATE_ERROR_MESSAGE);
throw new CustomAException (Constants.CUSTOMER_UPDATE_ERROR_MESSAGE);
}
I am trying to write test cases for updateCustomer but my test class has zero coverage even though its a success.
test class :
#SpringBootTest
public class CustomerServiceImplTest {
#InjectMocks
CustomerServiceImpl CustomerServiceImpl;
#Mock
ERepository eRepository ;
#Mock
ActivityUtil activityUtil;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void updateCustomerException() throws Exception {
CustomerServiceImpl CustomerServiceImplTest = mock(CustomerServiceImpl.class);
when(evfAdmissionRepository.findById(any())).thenThrow(ConstraintViolationException.class);
Mockito.doThrow(CustomAException .class).when(CustomerServiceImplTest).updateCustomer(setRequestDTO());
}
#Test
public void updateCustomerSuccess() throws Exception {
CustomerServiceImpl CustomerServiceImplTest = mock(CustomerServiceImpl.class);
CustomerServiceImplTest .updateCustomer(setRequestDTO());
//doNothing().when(evfCustomerServiceImpl).updateEVFCustomerOnSubscribe(any());
verify(CustomerServiceImplTest ).updateCustomerOn(setRequestDTO());
}
private RequestDTO setRequestDTO() {
RequestDTO eRequestDTO = new RequestDTO ();
eRequestDTO .setEmail("test");
// rest of code for setting value
return eRequestDTO ;
}
ArgumentCaptor can be used to capture and Assert Arguments in your method. you can read about ArgumentCaptor here
I need to test if a lambda function is called n-times from a service instance.
I have a Service class, that interact with the repository, when an error occur on retriving data from repository the service should retry until a max number of retries is reached so I have implemented as follow:
interface Repository {
Collection<String> getData();
}
public class RetryHelper<T> {
private Integer retries;
public RetryHelper(Integer retries) {
this.retries = retries;
}
public interface Operation<T> {
T doIt() throws Exception;
}
public T doWithRetry(Operation<T> operation) throws Exception {
int remainRetries = retries;
do {
try {
return operation.doIt();
} catch (Exception e) {
if (remainRetries == 0) {
throw e;
}
//TODO: wait before retry
remainRetries--;
}
} while (true);
}
}
class Service {
#Inject
Repository repo;
private final RetryHelper<Collection<String>> retryHelper;
public Collection<String> callService() {
try {
Collection<String> res = retryHelper.doWithRetry(() ->
repo.getData());
return res;
} catch (Exception e) {
throw (CustomException) e;
}
}
}
I need to test using Mockito that repo.getData() is called n-times when error occurs. I can change the Service code and the RetryHelper, so I am open to suggestions.
I have try to implment the test following tutorials and documentations:
public class ServiceTest {
#Inject
Service service;
#InjectMock
Repository repository;
#InjectMock
RetryHelper<Collection<String>> retryHelper;
#Captor
ArgumentCaptor<RetryHelper.Operation<Collection<String>>> operation;
#BeforeEach
void init_mocks() {
MockitoAnnotations.openMocks(this);
}
#Test
void shouldRetryIfDataQueryFailsForNonFatalError() throws Exception {
when(repository.getData())
.thenThrow(new RuntimeException("Runtime Exception"));
service.callService();
verify(retryHelper).doWithRetry(operation.capture());
verify(repository, times(2)).getData();
}
}
The test fail with message that getData() is never called.
I have finally found the solution without using Captor
public class ServiceTest {
#Inject
Service service;
#InjectMock
Repository repository;
#Inject
RetryHelper<Collection<String>> retryHelper;
#Test
void shouldRetryIfDataQueryFailsForNonFatalError() throws Exception {
when(repository.getData())
.thenThrow(new RuntimeException("Runtime Exception"));
try {
service.callService();
} catch(Exception e) {
verify(repository, times(2)).getData();
}
}
}
I want to test a few cases in a method by mocking external dependency to return different results for every test case. But when always returns what is defined at first time (in this example - empty set) and that brokes the next tests.
If I run tests one by one they pass successfully but when I run the whole class only the first test pass and others fail.
Testing class:
class ExampleTest {
#Mock
private Dao dao;
#Mock
private Validator validator;
#Spy
#InjectMocks
Controller controller;
#BeforeEach
void setUp() {
initMocks(this);
}
private final static Set DATA = Set.of("data1", "data2");
#Test
void firstTest() throws UserDashboardException, DashboardException, WidgetException {
when(validator.filter(DATA)).thenReturn(Collections.emptySet());
assertThrows(Exception.class, () -> controller.create(DATA));
}
#Test
void secondTest() throws UserDashboardException, DashboardException, WidgetException {
when(validator.filter(DATA)).thenReturn(DATA);
controller.create(DATA);
verify(dao, times(1)).create(eq(DATA));
}
}
Tested class:
public class Controller {
private Dao dao;
private Validator validator;
public Controller(Dao dao,Validator validator) {
this.dao = dao;
this.validator = validator;
}
public String create(Set<String> data) {
data = validator.filter(data);
if (data.isEmpty()) {
throw new Exception("Invalid data.");
}
return dao.create(data);
}
}
So, in both tests create method throws an exception which is not what I expect. Maybe I miss some point?
Have you tried with doReturn method?
doReturn(DATA).when(validator).filter(DATA)
which can be import from org.mockito.Mockito.doReturn;
Edited: there might be a bug inside your code implementation:
data = validator.filter(data);
I have #transactional method that seems to be working (rolling back) if run the actual service and provide inputs that would cause a run-time error. If I create a Test for that method that throws a run-time error it doesn't seem to rollback anymore. Any idea why this doesn't work while testing?
it's somthing like:
#Service
public class SampleServiceImpl implements SampleService {
private final RepoA repoA;
private final RepoB repoB;
public SampleServiceImpl(RepoA repoA, RepoB repoB) {
this.repoA = repoA,
this.repoB = repoB
}
#Transactional
#Override
public void addItems() {
repoA.save(new ItemA(1,'name1')); //works
repoB.save(new ItemB(2,'name2')); //throws run-time error
}
}
#RunWith(SpringRunner.class)
#DataJpaTest
public class Tests {
#Autowired
private RepoA repoA;
#Mock
private Repob repoBMock;
#Test
public void whenExceptionOccurrs_thenRollsBack() {
var service = new SampleService(repoA, repoBMock);
Mockito.when(repoBMock.save(any(ItemB.class))).thenThrow(new RuntimeException());
boolean exceptionThrown = false;
try {
service.addItems()
} catch (Exception e) {
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
Assert.assertFalse(repoA.existsByName('name1')); // this assertion fails because the the first item still exists in db
}
}
Just add annotation Rollback and set the flag to false.
#Test
#Rollback(false)
In my test when I assert de exception message I'm getting null
I'm not getting mock the message inside Service.... :(
I have:
My test:
#RunWith(MockitoJUnitRunner.class)
public class ServiceImplTest {
#InjectMocks
private ServiceImpl service;
#Mock
private Message message;
public static final String REQUIRED_FIELD = "required.field";
#Before
public void setUp() {
when(message.getMessage(eq(REQUIRED_FIELD), any(List.class))).thenReturn(REQUIRED_FIELD);
System.out.println(message.getMessage(REQUIRED_FIELD, new ArrayList()));
}
#Test(expected = MyException.class)
public void testCancel_shouldValidCancellation_and_ThrowTicketException_with_RequiredFieldMessage() {
try {
Object object = //... new object
service.do(object);
} catch (Exception e) {
assertEquals(REQUIRED_FIELD, e.getMessage()); // <-- e.getMessage return null!!!
}
}
}
My service:
#Service
public class ServiceImpl implements Service {
#Autowired
Message message;
#Override
public Object do(Object object) {
if(object.getSomeAttribute() == null)) {
throw new MyException(message.getMessage("required.field", "attribute"));
}
//doSomething...
return something;
}
}
In the setUp() of test the printLn() prints required.field but I can't use message of Service!!
Can someone help me?
It is hard tell for sure, without knowledge about the interface of Message, but it is easy to spot that you configure mock object to stub method with signature getMessage(String, List):
when(message.getMessage(eq(REQUIRED_FIELD), any(List.class))).thenReturn(REQUIRED_FIELD);
However, ServiceImpl uses getMessage(String, String). The default value which is returned by mock in this case is null. You have to configure mock object properly.