Generating unit tests for my service implementations on the spring boot application - java

after hours of tries and fails I come to you in hope of a solution.
I'm struggle making unit tests for my spring boot application. I'm using mockito and Junit 5.
My architecture is made out like this:
A controller
An interface of the service
A implementation of the service interface
A repository extending CrudRepository<Entity, Long>
For now I just want to test out my service implementation.
This is how it looks like for now :
`
#SpringBootTest public class ServiceImplTest{
#Mock
private Entity e;
#MockBean
private EntityRepository entityRepository;
#MockBean
private EntityService entityService;
#BeforeEach
init(){
e = new Entity();
e.name ="abc";
}
#Test
private simpleTest(){
// saving my element in the mocked repository
entityRepository.save(e);
// I have a repository query to delete an element in a specific way. I ask it to return 1 if it receives the order to activate this method
doReturn(1).when(entityRepository).specialDeleteEntity(1L);
// in the code serviceDeleteEntity() does some operations then calls entityRepository.specialDeleteEntity
int howMany = entityService.serviceDeleteEntity(1L);
// this fails because there was nothing in the repository to be deleted
assertEquals(howMany, 1);
}
}
I just have a feeling the the Mocked Repository is not connected to my Mocked Service and by this, the operations between them don't work.
I have also tried another solution where I didn't mock the repository , just in case :
#SpringBootTest class ServiceImplTest {
#MockBean
private EntityRepository mockEntityRepository;
#Autowired
private EntityService entityService;
#Test
void testDelete() {
// Given
final Entity entity = new Entity();
entity.name = "abc";
// Setup
when(mockEntityRepository.specialDeleteEntity(1L)).thenReturn(1);
// When
final int result = entityService.specialDeleteEntity(1L);
// Then
assertThat(result).isEqualTo(1);
verify(mockEntityRepository).specialDeleteEntity(1L);
}
}
I may lack some anotations or some methods maybe. I just want your advice on the problem and maybe a step towards the solution. Thank you very much.

There are a few issues with your test:
1. Wrong use of mocks
A mock is a dummy implementation of a class. You use them to bypass the normal behaviour of those classes. For example, if you write a unit test for EntityService, you don't want to set up an entire database and insert/delete mock data. So in that case you want to mock dependencies such as EntityRepository.
This means that in your EntityServiceTest you should only be mocking EntityRepository. You shouldn't mock Entity because this class normally doesn't contain a lot of behaviour and you shouldn't mock EntityService because you want to test the actual behaviour of this service and not of a mock.
2. Choose one mocking framework
Your test is currently using a combination of #Mock and #MockBean.
Both of these annotations allow you to mock a class.
The difference is that #Mock relies only on Mockito and #MockBean mocks the class and creates a Spring bean of it to use for autowiring.
This means that the #MockBean annotation only works when you run (a part of) your Spring Boot application.
This is why you use the #SpringBootTest annotation.
The downside is that this might require additional configuration and slows down the tests due to starting/stopping the application.
If you only need to unit test a single class with some mocks, it would be easier to write a test with Mockito only.
To do so, use the #Mock annotation and replace #SpringBootTest with #ExtendWith(MockitoExtension.class):
// Replace #SpringBootTest with #ExtendWith to enable testing with Mockito
#ExtendWith(MockitoExtension.class)
class EntityServiceTest {
// Add #InjectMocks to the class where the mocks should be injected into.
// Normally this is the class that you want to test
#InjectMocks
private EntityService service;
// Use #Mock in stead of #MockBean
#Mock
private EntityRepository repository;
// ...
}
3. Test the behaviour of your class
As I mentioned before, a mock is a dummy implementation of a class.
So that means that if you call repository.save(..), it doesn't really do anything as there's no behaviour and no database behind it.
What you actually want to test is whether the service. serviceDeleteEntity() method calls the repository. specialDeleteEntity() method with the right arguments.
This means that your second example of your test is the right way.
The only thing you don't need is the entity because your test doesn't rely on it (assuming that your service passes the id argument to the repository and returns the result of the query):
#Test
void testDelete() {
// Given
when(mockEntityRepository.specialDeleteEntity(1L)).thenReturn(1);
// When
final int result = entityService.specialDeleteEntity(1L);
// Then
assertThat(result).isEqualTo(1);
verify(mockEntityRepository).specialDeleteEntity(1L);
}

thanks to #g00glen00b for the solution.
#ExtendWith(MockitoExtension.class)
public class EntityServiceImplTest {
#InjectMocks
private EntityServiceImpl entityServiceImpl;
#Mock
private EntityRepository entityRepository;
#Test{
Entity e = new Entity();
when(entityRepository.getEntityById(1L)).thenReturn(e);
final Entity result = entityServiceImpl.getEntityById(1L);
assertEquals(e, result);
}
}
The issue here was that i had to use MockitoExtension on one hand.
Another problem was that, in my service implementation method , there was a hidden "new Timestamp(System.currentTimeMillis())" used to call the method in the repository.
So there was always a difference between the time I "promised" and the time created during the call of the service implementation.
Thank you again

I think you have to insert the mocked repository into your service in order it to be used:
entityService.setRepository(mockEntityRepository);

Related

Calling real method using Mockito

I am using JDK 11 and spring boot.
I am implementing a rest API and have 3 layers:
controller
service layer
data access layer
I had classes against interfaces at the data-access-layer and did not have any interface at the service layer.
I wrote integration tests using MockMvc, Mockito, etc to exercise the whole path for each point, exposed by the controller. This was not a problem until I tried to introduce the interface at the service layer.
Initially, I mocked only repositories/Daos. So the class structure looked like:
public interface ClientRepo{
......
}
public class ClientRepoImpl implements ClientRepo{
......
}
Mocked the returned data as:
#MockBean
private ClientRepo client;
....
Mockito.when(client.isExistFkUnitId(Mockito.any(UUID.class))).thenReturn(false);
Everything was fine so far.
Now I have introduced interface at the service layer as :
public interface ClientService{
......
}
public class ClientServiceImpl implements ClientService{
......
}
And tried ( Trying to call actual service method):
#MockBean
private ClientService clientService;
....
Mockito.when(clientService.isExistFkUnitId(Mockito.any())).thenCallRealMethod();
But getting nothing but null all the time.
Is there a way to make the real method call keeping the interface?
I think you want to use #Spy annotation instead of #Mock annotation on the field where you want to call the real method. I don't happen to have an example to verify this though.
https://javadoc.io/doc/org.mockito/mockito-core/2.21.0/org/mockito/Spy.html
Then you can do doCallRealMethod().when(clientService.isExistFkUnitId(Mockito.any())).
Because with a spy object you call doReturn/when instead of when/doReturn.
https://javadoc.io/doc/org.mockito/mockito-core/2.21.0/org/mockito/Mockito.html#do_family_methods_stubs
Well, there is no "real" method to call. (Ignoring the fact that default methods in interfaces are a thing nowadays)
Generally, unit tests should be written for the target class in an isolated fashion. Like this, you are always "testing" the "isExistFkUnitId" method as well.
You could set the mock up for specific values:
Mockito.when(clientService.isExistFkUnitId("valueA").thenReturn("answerA");
Mockito.when(clientService.isExistFkUnitId("valueB").thenReturn("answerB");
Anyways... to respond to your actual question:
If possible, you can instantiate the implementation in a way that the desired method is working and call it through the mock:
ClientServiceImpl clientServiceImpl = new ClientServiceImpl(...);
// spaghetti code only for demonstration purposes ;)
Mockito.when(clientService.isExistFkUnitId(Mockito.any())).then(i -> clientServiceImpl.isExistFkUnitId((String) i.getArguments()[0]));
POC test:
#Test
public void testit() {
Myclass myclass = new Myclass();
Myinterface mock = Mockito.mock(Myinterface.class);
Mockito.when(mock.myMethod(Mockito.any())).then(i -> myclass.myMethod((String) i.getArguments()[0]));
assertThat(mock.myMethod(" works")).isEqualTo("yeehaa works");
}
public interface Myinterface {
String myMethod(String params);
}
public static class Myclass implements Myinterface {
#Override
public String myMethod(String params) {
return "yeehaa" + params;
}
}
Not exactly a beautiful solution, but if there is no way around it, it should work.
As you are mocking an interface Mockito doesn't know which implementation are you referring. The only way will be to use the Class.
I was having the same problem. My problem was due to the ClientService having dependencies that were not mocked when I set up the tests in this format. So ClientService had a mock, but if I tried clientService.productService.get() or something of that nature the dependant productService was always null. I solved this using testing reflection:
#MockBean
DependentService mockDependentService
ControllerToTest controllerToTest
#BeforeEach
public void setup() {
mockDependentService = mock(DependentService.class);
controllerToTest = mock(ControllerToTest.class);
ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
}
#Test
void test() {
//set up test and other mocks
//be sure to implement the below code that will call the real method that you are wanting to test
when(controllerToTest.methodToTest()).thenCallRealMethod();
//assertions
}
Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.
This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.
Hope this helps!

How to cleanly test Spring Controllers that retrieve parameters with DomainClassConverter?

I am big on clean well-isolated unit tests. But I am stumbling on the "clean" part here for testings a controller that uses DomainClassConverter feature to get entities as parameters for its mapped methods.
#Entity
class MyEntity {
#Id
private Integer id;
// rest of properties goes here.
}
The controller is defined like this
#RequestMapping("/api/v1/myentities")
class MyEntitiesController {
#Autowired
private DoSomethingService aService;
#PostMapping("/{id}")
public ResponseEntity<MyEntity> update(#PathVariable("id")Optional<MyEntity> myEntity) {
// do what is needed here
}
}
So from the DomainClassConverter small documentation I know that it uses CrudRepository#findById to find entities. What I would like to know is how can I mock that cleanly in a test.
I have had some success by doing this steps:
Create a custom Converter/Formatter that I can mock
Instantiate my own MockMvc with above converter
reset mock and change behaviour at each test.
The problem is that the setup code is complex and thus hard to debug and explain (my team is 99% junior guys coming from rails or uni so we have to keep things simple). I was wondering if there is a way to inject the desired MyEntity instances from my unit test while keep on testing using the #Autowired MockMvc.
Currently I am trying to see if I can inject a mock of the CrudRepository for MyEntity but no success. I have not worked in Spring/Java in a few years (4) so my knowledge of the tools available might not be up to date.
So from the DomainClassConverter small documentation I know that it uses CrudRepository#findById to find entities. What I would like to know is how can I mock that cleanly in a test.
You will need to mock 2 methods that are called prior the CrudRepository#findById in order to return the entity you want. The example below is using RestAssuredMockMvc, but you can do the same thing with MockMvc if you inject the WebApplicationContext as well.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SomeApplication.class)
public class SomeControllerTest {
#Autowired
private WebApplicationContext context;
#MockBean(name = "mvcConversionService")
private WebConversionService webConversionService;
#Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
SomeEntity someEntity = new SomeEntity();
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
}
}
At some point Spring Boot will execute the WebConversionService::convert, which will later call DomainClassConverter::convert and then something like invoker.invokeFindById, which will use the entity repository to find the entity.
So why mock WebConversionService instead of DomainClassConverter? Because DomainClassConverter is instantiated during application startup without injection:
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<>(conversionService);
Meanwhile, WebConversionService is a bean which will allow us to mock it:
#Bean
#Override
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
It is important to name the mock bean as mvcConversionService, otherwise it won't replace the original bean.
Regarding the stubs, you will need to mock 2 methods. First you must tell that your mock can convert anything:
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
And then the main method, which will match the desired entity ID defined in the URL path:
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
So far so good. But wouldn't be better to match the destination type as well? Something like eq(TypeDescriptor.valueOf(SomeEntity.class))? It would, but this creates a new instance of a TypeDescriptor, which will not match when this stub is called during the domain conversion.
This was the cleanest solution I've put to work, but I know that it could be a lot better if Spring would allow it.

How to verify if test is correctly implemented via code?

Introduction
I am trying to verify if the created tests pass my criteria/scenario.
Problem
Suppose we have two classes: SomeService.java and SomeRepository.java. Methods inside SomeService are already implemented. At the other hand, methods in SomeRepository are not yet implemented.
Now, using Mockito framework I want to mock SomeRepository. For instance:
#Mock SomeRepository repository;
#Test
public String someMethod() {
SomeService service = new SomeService(repository);
when(repository..).doReturn(...);
String result = service.call();
assertTrue(result).isNotNull();
}
Question
Is it possible to code some piece of logic (test case) in such a way that it will check for me, for instance:
for someMethod test, I want to have:
1. repository.callSth() should return some string
2. result should be checked for nullability
Note
I have to use Mockito.

Spring autowiring keeps JMockit mocks around for other tests

I'm trying to unit test a class (let's say, "ClassUnderTest") and its use of a particular library class (let's call it "Helper"). So I'm injecting a mocked Helper into the ClassUnderTest and using Expectations to check things out.
public class TestUseOfHelper {
#Autowired ClassUnderTest classUnderTest;
#Mocked HelperClass helper;
[...]
#Before
public void setUp() throws Exception {
Deencapsulation.setField(classUnderTest, "helper", helper);
}
[...]
#Test
public void test() {
new Expectations() {{
helper.doSomething(any);
}};
classUnderTest.useHelper();
}
So far, so good.
In other tests (same package, different file), I'm testing other aspects of the ClassUnderTest where I just want Helper to do its thing.
public class TestOtherQualities {
#Autowired ClassUnderTest classUnderTest;
[...]
#Test
public void test() {
result = classUnderTest.processSomething();
assertNonNull(result);
}
Those tests work fine as well.
The problem comes in when I run the suite of tests. Now, the second set fails, apparently because the Mocked Helper is still in place!
I assume that the full suite is being executed in the same JVM and that Spring has created one ClassUnderTest, injects the mock like it's told, but then just reuses the same ClassUnderTest object for the next test. How do I keep them separate? How can I get "fresh" objects for each separate test file? What am I not getting/googling?
I've tried numerous variations of defining objects within the Expectations blocks and using #Tested/#Injected but with no luck. BTW, I'm avoiding mocking the Helper in other tests because I need it to do its thing. Also ClassUnderTest autowires a bunch of other objects, which in turn autowire still others, so mocking everything that ClassUnderTest references is impractical.
Any insights, Oh knowledgeable Stack Overflow Magic 8 Ball? ("Try Later" is not acceptable.)
Annotate the test class with
#DirtiesContext(classMode = AFTER_CLASS)
if you want to reset the app context between test classes.
It's a unit test. So you shouldn't use Spring at all to create and autowire the objects. Just create a new ClassUnderTest every time you need one, and inject the mock dependencies manually :
#Before
public void setUp() {
this.classUnderTest = new ClassUnderTest();
Deencapsulation.setField(classUnderTest, "helper", mockHelper);
}
Note: I don't know JMockit, and it might have annotations that even create a new and inject mock dependencies automatically. The point of my answer is that Spring should be out of the picture for unit tests. That's the main advantage of dependency injection: being able to create instances of objects to test, and being able to manually inject mock dependencies.

Using JMockit to mock autowired interface implementations

We are writing JUnit tests for a class that uses Spring autowiring to inject a dependency which is some instance of an interface. Since the class under test never explicitly instantiates the dependency or has it passed in a constructor, it appears that JMockit doesn't feel obligated to instantiate it either.
Up until now we have been using SpringRunner to have Spring load mock dependencies for us, which works. Two things we don't like about this are 1) the Spring framework has to be loaded and initialized each time running the tests which is not exactly speedy, and 2) we are forced to explicitly create all mock dependencies as real classes, something which JMockit helps eliminate.
Here's a simplified example of what we're testing:
public class UnitUnderTest {
#Autowired
ISomeInterface someInterface;
public void callInterfaceMethod() {
System.out.println( "UnitUnderTest.callInterfaceMethod calling someInterface.doSomething");
someInterface.doSomething();
}
}
So, the question is, is there a way to have JMockit create a mock someInterface?
JMockit will always instantiate a mocked interface (except in the case of a final mock field), but that only occurs in test code. It will not automatically inject the instance into code under test.
You would have to manually inject the mock instance. For example:
public class SomeTest
{
#Autowired UnitUnderTest unitUnderTest;
#Mocked ISomeInterface theMock; // created and assigned automatically
#Test
public void testSomeMethod()
{
Deencapsulation.setField(unitUnderTest, theMock);
//proceed with unit test here
}
}
mockit.Deencapsulation is a Reflection-based utility class that lets you invoke private methods, get/set fields, etc.
You can use org.springframework.test.util.ReflectionTestUtils to explicitly inject your mocked ISomeInterface in your test case.
See documentation
With the hints kindly provided above, here's what I found most useful as someone pretty new to JMockit: JMockit provides the Deencapsulation class to allow you to set the values of private dependent fields (no need to drag the Spring libraries in), and the MockUp class that allows you to explicitly create an implementation of an interface and mock one or more methods of the interface. Here's how I ended up solving this particular case:
#Before
public void setUp() {
IMarketMakerDal theMock = new MockUp <IMarketMakerDal>() {
#Mock
MarketMakerDcl findByMarketMakerGuid( String marketMakerGuid ) {
MarketMakerDcl marketMakerDcl = new MarketMakerDcl();
marketMakerDcl.setBaseCurrencyCode( CURRENCY_CODE_US_DOLLAR );
return marketMakerDcl;
}
}.getMockInstance();
setField( unitUnderTest, theMock );
}
Thanks everyone for the help.
For those people who met
java.lang.IllegalStateException: Missing #Injectable for field ***
or
java.lang.IllegalStateException: Missing #Tested class for field ***
error when using jmockit to mock #autowired field in spring ( or spring boot) framework, I did below two steps to avoid above errors:
use #Tested(fullyInitialized=true) instead of #Tested
https://groups.google.com/forum/#!msg/jmockit-users/uo0S51lSX24/lQhLNN--eJcJ
revert jmockit's version back to 1.18 or previous ones
https://groups.google.com/forum/#!topic/jmockit-users/wMFZggsA8LM
If you have a #Qualifier annotation for the interface, you need to name your #Injectable field exactly as it is named in qualifier.
Here is quote from JMockit doc:
Custom names specified in field annotations from Java EE (#Resource(name), #Named) or the Spring framework (#Qualifier) are used when looking for a matching #Injectable or #Tested value. When such a name contains a - (dash) or . (dot) character, the corresponding camel-cased name is used instead.
For example:
#Component
public class AClass {
#Autowired
private Bean1 bean1;
#Autowired
#Qualifier("my-dashed-name")
private AmqpTemplate rpcTemplate;
}
Unit test class:
public class AClassTest {
#Injectable
private Bean1 bean1;
#Injectable
private AmqpTemplate myDashedName;
#Tested
private AClass aClass = new AClass();
}
Also there is no need to use setFiled for each #Autowired bean, all fields injects automatically when #Tested class instantiated. Tested on JMockit ver. 1.30

Categories

Resources