Dependency injection with mockito example - spring

I am very new with Mockito and I don't get the following example (classes were provided, only test to write) and how to solve it.
What I try to do is use a test double for the supplier so that we can control the returned greeting in the test and assert that the GreetingService does not modify the greeting message in any way. Then assert that the returned greeting string is equal to "Hello Andy.".
public class Greeting {
private final String template;
public Greeting(String template) {
this.template = template;
}
public String forName(String world) {
return String.format(template, world);
}
}
#Component
public class GreetingService {
private final Supplier<Greeting> greetingSupplier;
public GreetingService(Supplier<Greeting> greetingSupplier) {
this.greetingSupplier = greetingSupplier;
}
public String greet(String name) {
return greetingSupplier.get().forName(name);
}
}
#Component
public class RandomGreetingSupplier implements Supplier<Greeting> {
private final List<Greeting> greetings = Arrays.asList(
new Greeting("Hello %s."),
new Greeting("Hi %s!"),
);
private final Random random = new Random();
#Override
public Greeting get() {
return greetings.get(random.nextInt(greetings.size()));
}
}
#SpringBootTest
public class GreetingServiceTest {
#Autowired
GreetingService greetingService;
#MockBean
Supplier<Greeting> greetingSupplier;
#Test
void getGreetingForPerson() {
String name = "Andy";
// that test cannot know which greeting will be returned by the supplier
// WHY IS IT NULLPOINTEREXCEPTION AFTER INITIALIZING #MockBean
//String greeting = greetingService.greet(name);
//assertThat(greeting).contains(name);
// WROTE SUCH TEST HERE -> NullPointerException WHY?
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
assertThat(greetingSupplier.equals("Hello Andy."));
// THIS IS WORKING & TEST PASSED BUT I GUESS ITS WRONG?
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
assertThat(greetingSupplier.equals("Hello Andy."));
}
}

Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You can't chain calls like that, you need to produce intermediate results, like
Supplier<Greeting> supplier = mock(Supplier.class);
Mockito.when(supplier).forName().thenReturn("Hello %s.");
Mockito.when(greetingSupplier.get()).thenReturn(supplier);
For dependency injection, you need to create the subject under test with the mocked Supplier. You can do that in a #Before method for example.

Your mocking is wrong.
Mockito.when(greetingSupplier.get().forName(name)).thenReturn("Hello %s.");
You mocked Supplier<Greeting> and the default behavior is to return null. So when you call greetingSupplier.get() in your first line it returns null. You directly chain forName which nou basicall is null.forName which leads to an error.
Your second part is actually (kind of) correct.
Mockito.when(greetingSupplier.get()).thenReturn(new Greeting("Hello %s."));
You now properly return a response from greetingSupplier.get(). Instead of chaining the call.
However I would argue that your excercise is wrong. Why? When using a Supplier<?> in Spring it actually is a lazy beanFactory.getBean call. You can lazily inject dependencies this way. You should have a mock for Greeting which returns a hardcoded String which you can check.

Related

Spring boot with Mockito mocking NamedParameterJdbcTemplate

Trying to unit test a method which is defined as :
public void myMethod(List<? extends MyModel> model){
int[] result = namedParameterJdbcTemplate.batchUpdate("update query", SqlParameterSourceUtils.createBatch(model));
}
In my test class i am defining test method as
class MyTestClass{
#Mock
NamedParameterJdbcTemplate namedParameterJdbcTemplate;
#InjectMocks
MyDao dao;
#Test
public void testMyMethod() {
final int[] rowsAffected = new int[]{1,2};
when(namedParameterJdbcTemplate.batchUpdate(any(), SqlParameterSourceUtils.createBatch(Arrays.asList(anySet())))).thenReturn(rowsAffected);
List<MyModel> myModels = new ArrayList<>();
MyModel mymodel = new MyModel();
mymodel.setSomeParam("");
myModels.add(mymodel);
dao.myMethod(myModels);
}
}
While running this test method , i am getting NullPointerException in called method(myMethod()). int[] result is coming as null. My understanding is it should get the result from the stub in the mock.
Please help me understand what am i doing wrong.
It seems that you're not using the correct import for any() because if it would be the correct ArgumentMatchers.any() from Mockito, Mockito would complain that you don't use an ArgumentMatcher for both parameters of .batchUpdate().
You can statically import it with import static org.mockito.ArgumentMatchers.*; or use ArgumentMatchers.any().
So as first step, try the following:
when(namedParameterJdbcTemplate.batchUpdate(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(rowsAffected);
or be less generic and match the return type of SqlParameterSourceUtils.createBatch() with:
// I don't know what SqlParameterSourceUtils.createBatch() returns, so you might have to adjust it
when(namedParameterJdbcTemplate.batchUpdate(ArgumentMatchers.any(), ArgumentMatchers.eq("SOME RETURN"))).thenReturn(rowsAffected);
It worked by adding the cast to the Argument matchers:
Updated Code :
when(namedParameterJdbcTemplate.batchUpdate(anyString(), (SqlParameterSource[]) any())).thenReturn(rowsAffected);

Mockito mock CassandraOperation Slice method

I am using the Mockito framework to write the mock Junit test cases below is my Service class to test the Junit test code
public class UserService {
#Autowired
private CassandraOperations template
public UserDTO getUserDTO(String date, String pagingState) {
Select select = QueryBuilder.select().all()
.from("tbl_user");
select.where(QueryBuilder.eq(date, date));
select.setFetchSize(30);
if (pagingState != null) {
select.setPagingState(PagingState.fromString(pagingState));
}
Slice<UserDTO> usgerDTO = template.slice(select, UserDTO.class);
if(usgerDTO.hasNext()) {
}
return usgerDTO.get();
}
Test case Class is writted
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#InjectMocks
private UserService service ;
#Mock
private CassandraOperations template;
#Mock
private UserDTO userDTO;
private String date= "2019-09";
#Test(expected = Exception.class)
public void test() {
Slice<UserDTO> userDTO= Mockito.mock(Slice.class);
Select select = Mockito.mock(Select.class);
Mockito.when(template.slice(select, UserDTO.class)).thenReturn(userDTO);
metricReportDTO = service.getUserDTO(date, null);
}
}
I am getting Null values on this line
Slice<UserDTO> usgerDTO = template.slice(select, UserDTO.class);
Can you please helm me how to test template.slice method
Note: I should not use the PowerMockito
Your code snippet doesn't make sense in that you call service.getMetricsReports(date, null) in your test but your UserService code example only has the method getUserDTO. Assuming that is just a typo then you need to use matchers to get the mock to work in your when.
Mockito.when(template.slice(any(Select.class), eq(UserDTO.class))).thenReturn(userDTO);
This means whenever the first param is a class of type Select and the second is equal to UserDTO.class then return the userDTO if those 2 params don't match those conditions it will return null.
You can also make it match the exact Select assuming it has an overridden equals method. In the case where you send a null pagingState then something like:
Select stubSelect = QueryBuilder.select().all()
.from("tbl_user");
stubSelect.where(QueryBuilder.eq(date, date));
stubSelect.setFetchSize(30);
Mockito.when(template.slice(eq(stubSelect), eq(UserDTO.class))).thenReturn(userDTO);

Value of doReturn not used

I use spring boot 2.
I search to test a private method in the facade
#RunWith(SpringRunner.class)
#SpringBootTest
public class SamplingsFacadeTest {
#Autowired
private SamplingsFacade facade;
#MockBean
private SamplingsService samplingsService;
#Test
public void exampleTest() throws Exception {
List<Samplings> samplings = new ArrayList<>();
Samplling sampling = new Samplings();
..
samplings.add(sampling);
//fake call
Mockito.doReturn(samplings).when(samplingsService.getSamplingContainingNonCompliantTest());
//real call
List<FactoryEmailNCDto> factoryEmails = Whitebox.invokeMethod(facade, "prepareDataNoncompliantSampling");
}
public List<Samplings> getSamplingContainingNonCompliantTest() {
return samplingsRepository.findSamplingContainingNonCompliantTest();
}
In Facade In
private List<FactoryEmailNCDto> prepareDataNoncompliantSampling() {
List<FactoryEmailNCDto> factoryEmailNC = new ArrayList<>();
List<Samplings> samplings = samplingsService.getSamplingContainingNonCompliantTest();
for (Samplings sampling : samplings) {
...
}
}
Why when I debug, samplings is null and not the value I created
Mockito.doReturn(samplings)
.when(samplingsService.getSamplingContainingNonCompliantTest());
One potential problem is that doReturn takes the form doReturn(value).when(mock).method(), not doReturn(value).when(mock.method()). The latter is considered an incomplete stubbing. Instead, it should look like this:
Mockito.doReturn(samplings)
.when(samplingsService)
.getSamplingContainingNonCompliantTest();
Note that there may be other problems with your test; the code you've written expects samplingsService to be public and non-final, and your getSamplingContainingNonCompliantTest() to likewise be public, non-static, and non-final, but the code sample you have does not confirm that. You may want to call Mockito.validateMockitoUsage() in an #After method, or use a #Rule that handles Mockito annotations and cleanup for you.

Is a mock MongoRepository saving objects?

I am using SpringBoot 1.5.9 and try to do Integration testing. Weirdly a MongoRepository.save() method updates the object when called on mock MongoRepository.
I have a Counter Class
public class Counter {
public String id;
public int seq;
public void increaseSeq() {
this.seq += 1;
}
}
And his repository
public interface CounterRepository extends MongoRepository<Counter, String>{
Counter findById(String id);
List<Counter> findAll();
}
And his service
#Service
public class CounterService {
#Autowired private CounterRepository counterRepository;
public Counter findCounter(String id) {
return counterRepository.findById(id);
}
public int getSeqAndIncrease(String id) {
Counter counter = findCounter(id);
if (counter == null) {
return -1;
}
counter.increaseSeq();
counterRepository.save(counter);
return counter.getSeq();
}
}
Now, when I do system integration and try to mock the counterRepository, it happens something that I don't expect. The counterRepository.findById() returns a Counter object where the 'seq' field is increased. Why? Does the counterRepository.save() affect the result in any way (the counterRepository is mocked, hence I suppose that save() should not have any effect)?
#RunWith(SpringRunner.class)
#SpringBootTest
public class FlowServiceTest {
#MockBean private CounterRepository counterRepository;
#Autowired private CounterService counterService;
#Before
public void setUp() {
Mockito.when(counterRepository.save(any(Counter.class))).then(arg -> arg.getArgumentAt(0, Counter.class));
Mockito.when(counterRepository.findById("flow")).thenReturn(new Counter("flow", 10));
}
#Test
public void testSavingInDatabase() {
System.out.println(counterRepository.findById("flow"));
counterService.getSeqAndIncreaseSafe("flow");
System.out.println(counterRepository.findById("flow"));
counterService.getSeqAndIncreaseSafe("flow");
System.out.println(counterRepository.findById("flow"));
}
}
It prints "10 11 12". Why doesn't it print '10 10 10'?
The problem is these lines
counterRepository.save(counter);
return counter.getSeq();
What you should be doing is this
Counter saveCounter = counterRepository.save(counter);
return savedCounter.getSeq();
In getSeqAndIncrease method, you are not returning sequence of the saved object.
By doing this you are making your mock statement for save useless. Because you are not using the value returned from mock.
tl;dr - The returned object from mock is initialized only once in mockito. So I basically got the same reference every time, and since it is a reference not a new object, the values are updated.
Complete answer: When setting
Mockito.when(counterRepository.findById("flow")).thenReturn(new Counter("flow", 10));
, it might seem intuitive to return a new object every time, but the return object is initialised only once when the test starts and will be returned at all subsequent calls.
Then, in my code I do
counter.increaseSeq();
which increases the 'seq' of found object (this object comes from Mockito). Then at the next call, Mockito returns the firstly initialised object which was updated in the meantime; Mockito does not return a new object as it might seem like.

Groovy + Spring - DI with no boilerplate (constructor) code

I have created spring-boot project which bases on Groovy instead of Java.
Now I have following #RestController:
#RestController
class HelloRest {
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
helloService.createHelloMessage(name)
}
}
Question is how to inject
#Service
class HelloService {...}
in most simple way avoiding boilerplate (in this case the constructor) code?
In Java I would use: #lombok.RequiredArgsConstructor and in fact it works also if I use it in my groovy project.
On the other hand for example the #Immutable annotation from groovy.transform doesn't work as it creates in fact more than single constructor. Whereas Spring expects single constructor to be able automatically #Autowired the dependencies.
As far I see 2 solutions:
Generate the constructor
Use lombok with its annotations
Is there any solution build into Groovy which could be used here instead?
At this moment there is no Groovy mechanism that does same thing as #lombok.RequiredArgsConstructor. The main problem in your case is that Groovy always generates no-args default constructor for all currently known features like #Immutable annotation. The closest (but not accurate) way is to use #TupleConstructor like:
#RestController
#TupleConstructor(includes = ['helloService'], includeFields = true, includeProperties = false, force = true)
class HelloRest {
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
return helloService.createHelloMessage(name)
}
}
This Groovy code will produce bytecode similar to this Java code:
#RestController
#TupleConstructor(
includeFields = true,
force = true,
includeProperties = false,
includes = {"helloService"}
)
public class HelloRest implements GroovyObject {
private final HelloService helloService;
public HelloRest(HelloService helloService) {
CallSite[] var2 = $getCallSiteArray();
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
this.helloService = (HelloService)ScriptBytecodeAdapter.castToType(helloService, HelloService.class);
}
public HelloRest() {
CallSite[] var1 = $getCallSiteArray();
this((HelloService)null);
}
#GetMapping({"hello"})
public String hello(#RequestParam("name") String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
}
}
It is almost what you need, except this default constructor that was generated as well.
Things are getting even more complicated when using #Immutable annotation, because this version would generate 3 constructors:
public HelloRest(HelloService helloService)
public HelloRest()
public HelloRest(HashMap args)
Of course in this case you would have to remove private final in front of HelloService field definition, because this AST transformation works only with fields that are not yet final.
In this case two options you have found (creating construct manually or using Lombok) are probably the best solutions to your problem.
Alternative solution
There is also one "dirty" solution that allows you to write less amount of code, but promotes injection by reflection. Consider following code:
#RestController
class HelloRest {
#Autowired
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
return helloService.createHelloMessage(name)
}
}
It will generate bytecode similar to following Java code:
#RestController
public class HelloRest implements GroovyObject {
#Autowired
private final HelloService helloService;
public HelloRest() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
#GetMapping({"hello"})
public String hello(#RequestParam("name") String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
}
}
Even though there is only single default constructor that does not even touch our helloService field, Spring bean will get injected by reflection. I share this option only to show all alternatives, although your initial instinct to use constructor injection is the best possible way to use dependency injection in practice.
You can use #Cannonical or #Immutable on the class. That way the constructor will be created for you

Resources