How to mock Spring Message Resource inside another Mock class with Mockito? - java

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.

Related

Mockito always returns null as a result of calling an EJB

I'm trying to call the second method of this class, and always get null. Note that it returns new User() however in the test class I always get null:
#Stateless
public class UserDAO2 {
public Connection getConnFromPool(int i) {
return null;
}
public User readByUserid(String s) {
System.out.println("In DAO 2");
Connection c = getConnFromPool(1);
return new User();
}
}
And the test class:
#RunWith(MockitoJUnitRunner.class)
public class UserBeanUnitTest {
#InjectMocks
private UserDAO2 dao2;
#Before
public void setup() {
dao2 = Mockito.mock(UserDAO2.class);
MockitoAnnotations.initMocks(this);
}
#Test
public void testBean() {
Mockito.when(dao2.getConnFromPool(1)).thenReturn(null);
User expectedUser = new User();
expectedUser.setSk(1);
expectedUser.setFirstName("David");
expectedUser.setLastName("Gahan");
expectedUser.setUserid("user1");
User user = dao2.readByUserid("user1"); // <-- this method always returns null
assertThat(user).isEqualTo(expectedUser); // <-- test fails as expectedUser != null
}
}
Also, note that System.out.println is never printed. How to fix this to actually make a call to dao.readByUserid() ?
If you need to test the method of some class, and inside of it the other method of the same class is called which you want to mock, then you need to use #Spy:
#RunWith(MockitoJUnitRunner.class)
public class UserDAO2Test {
#InjectMocks
#Spy
private UserDAO2 dao;
#Test
public void testBean() {
Mockito.doReturn(null).when(dao).getConnFromPool(1);
User expectedUser = new User();
expectedUser.setSk(1);
expectedUser.setFirstName("David");
expectedUser.setLastName("Gahan");
expectedUser.setUserid("user1");
User user = dao.readByUserid("user1");
assertThat(user).isEqualTo(expectedUser);
}
}
Note that I slightly modified the line with mocking getConnFromPool because it's required when you use that technique.
See docs for spying.

Why doesn't my #Transactional method rollback when testing?

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)

How can I test a validator in a Spring Boot app?

I have a class:
#Component
public class ContractorFormValidator implements Validator {
Logger logger = LoggerFactory.getLogger(ContractorFormValidator.class);
#Inject IBusinessDataValidator businessDataValidator;
#Override
public boolean supports(Class<?> clazz) {
return Contractor.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Contractor contractor = (Contractor) target;
if (!businessDataValidator.isNipValid(contractor.getContractorData().getNip())) {
errors.rejectValue("contractorData.nip", "invalid");
}
if (!businessDataValidator.isRegonValid(contractor.getContractorData().getRegon())) {
errors.rejectValue("contractorData.regon", "invalid");
}
}
}
How can I test it? I have tried this: How to test validation annotations of a class using JUnit? but this doesn't work cause the validate method in my validator requires Errors class passed to it's method signature.
I have no Idea if I can pass this Errors object to the validator. Is there any other way?
Have you tried to write a simple unit test for this?
#RunWith(SpringJUnit4ClassRunner.class)
public class ContractorFormValidatorTest {
#Autowired
private ContractorFormValidator validator;
#Test
public void testValidation() throws Exception {
Contractor contractor = new Contractor();
// Initialise the variables here.
Errors errors = new BeanPropertyBindingResult(contractor, "contractor");
validator.validate(contract, errors);
// If errors are expected.
Assert.assertTrue(errors.hasErrors());
for (Error fieldError : errors.getFieldErrors()) {
Assert.assertEquals("contractorData.nip", fieldError.getCode());
}
}
}
If you are going to use the validator in a controller implementation, then you need to use the MockMvc apis
Your set up can be included in the class above.
private MockMvc mockMvc
#Autowired
private MyController controller;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.standaloneSetup(this.controller).build();
}
#Test
public void testMethod() {
MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.post("/yoururl")).
andExpect(MockMvcResultMatchers.status().isCreated()).andReturn();
}
Use the class org.springframework.validation.BeanPropertyBindingResult,
Errors newErrors = new BeanPropertyBindingResult(validateObject, "objectName");

How do I mock a generic method that accepts a class type?

I'm trying to write a unit test for a REST API client. I am following a pattern that has worked well for me in a variety of other unit tests. In particular, I have successfully mocked dependencies that have been injected into a repository under test. However, when I come to mock a Spring RestTemplate, I cannot find a way to get its getForObject() method to return anything other than null. Does anyone know how to do this? I suspect the issue might be that the signature for RestTemplate.getForObject() contains generics:
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException
Here is my REST client class that I am trying to test:
#Repository
public class WebApplicationClient {
private final RestTemplate template;
public WebApplicationClient(RestTemplate template) {
this.template = template;
}
public <T> T getData(String baseUrl, Class<T> clazz) {
String endpoint = process(baseUrl);
try {
return template.getForObject(endpoint, clazz); // Mock this call during testing
} catch (HttpClientErrorException | HttpServerErrorException e) {
String msg = "API call failed: " + endpoint;
LOG.warn(msg, e);
throw new WebApplicationException(e.getStatusCode(), msg, e);
}
}
}
Here is my unit test so far. What ever I try for when(template.getForObject(...)) always returns null. Hence result is always null and my assertion fails.
public class WebApplicationClientUnitTests {
#Mock private RestTemplate template;
private WebApplicationClient client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
client = new WebApplicationClient(template);
}
#Test
public void getData_Test1() {
// when(template.getForObject(any(), eq(String.class))).thenReturn("sample"); // Returns null
when(template.getForObject(any(), any())).thenReturn("sample"); // Returns null
String result = client.getData(TEST_URL, "db", expectedState, String.class);
Assert.assertEquals("sample", result);
}
}
How do I get getForObject() to return an actual value?
#Test
public void getData_Test1() {
when(template.getForObject((String) any(),eq(String.class))).thenReturn("sample");
//OR
//when(template.getForObject((String) any(),(Class)any())).thenReturn("sample");
//OR
//when(template.getForObject(any(String.class), any(Class.class))).thenReturn("sample");
String result = client.getData("TEST_URL", String.class);
Assert.assertEquals("sample", result);
}
The Above code works fine for me.

Method of mocked object returns null after interface downcast

I am trying to write tests for Spring boot application.
There are two interfaces INotifier, and IMonospaceNotifier which extends INotifier in the application.
public interface INotifier {
void send(String message);
}
public interface IMonospaceNotifier extends INotifier {
String monospace(String message);
}
Class TelegramNotifier implements IMonospaceNotifier
#Component
public class TelegramNotifier implements IMonospaceNotifier {
//Some code omitted
public void send(String message) {
//Implementation omitted
}
#Override
public String monospace(String message) {
return "```\n" + message + "\n```";
}
}
Class Report has field of type INotifier but in some cases, it is downcasted to IMonospaceNotifier
#Component
public class Report {
//Some code is omitted
private INotifier notifier;
#Autowired
public Report(/*params are omitted*/) {
// Some code is omitted
if (reportGenerator.requireMonospace() && !(notifier instanceof IMonospaceNotifier)) {
throw new IllegalArgumentException("If reportGenerator requests monospace method" +
" then notifier should be IMonospaceNotifier");
}
}
#Scheduled(cron = "${reportSchedule}")
public void sendReport() {
// Some code is omitted
String report = reportGenerator.generate(workerList);
if (reportGenerator.requireMonospace()) {
if (notifier instanceof IMonospaceNotifier) {
/**
* This is the problem part. It works fine with normal obejcts
* but method `monospace` returns null with mocked objects.
* I debugged it this codeline is definitely executed and
* `report` is not `null` before the execution of this line
*/
report = ((IMonospaceNotifier) notifier).monospace(report);
} else {
assert true : "Should never happen, checked in constructor";
}
}
notifier.send(report);
}
It all works fine until IMonospaceNotifier is mocked. With mocked version
IMonospaceNotifier.monospace() returns null (Please see comment in the code above). Mocked object seems to have the correct type IMonospaceNotifier$$EnhancerByMockitoWithCGLIB$$...
The object is mocked in the next way:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "scheduling.enabled=false")
public class MonitorTest {
#MockBean
private IMonospaceNotifier notifier;
#Test
public void doNothing(){
/** `notifier.send` is invoked in another bean constructor.
* That's why it is working without actual invocation. */
// This works fine as it doesn't use Report class and downcast
verify(notifier).send("Hi!, I'm starting");
// The next invocation is actually null
verify(notifier).send(matches("```┌───.*Worker Name.*"));
verify(notifier).send("I'm shutting down. Good Bye!");
}
}
This is how INotifier is invoked in a constructor of Monitor bean
#Service
public class Monitor {
#Autowired
public Monitor(/*params are omitted*/ INotifier notifier) {
// This line works fine as it doesn't invoke `monospace`
notifier.send("Hi!, I'm starting");
// In `Report` `send()` is executed with `null` as parameter
// because `monospace()` was invoked
report.sendReport();
}
}
You have to tell your mock to return what you want. In your case it looks like you want to return the same object passed in as parameter:
public class MonitorTest {
#MockBean
private IMonospaceNotifier notifier;
#Test
public void doNothing(){
doAnswer((invocation)-> invocation.getArguments()[0]).when(notifier).monospace(anyString());
// ...
The better option however is to define an independent "report" to be returned so that you have more control in the test case:
public class MonitorTest {
#MockBean
private IMonospaceNotifier notifier;
#Test
public void doNothing(){
doReturn(SOME_TEST_REPORT_STRING).when(notifier).monospace(anyString());
// ...

Categories

Resources