So I am new to Mockito for testing facades. So basically I want to check, if a method of Service gets called once.
Here would be a simplified example
My Service
public class Service {
public int myMethod(int index, int number) {
if (index<4){
index = index + number;
}
return index;
}
}
My Facade:
public class Facade {
private Service service;
public void method(){
int i = service.myMethod(4, 2);
}
}
and finally my Test:
public class FacadeTest {
#InjectMocks
private Facade classUnderTest;
#Mock (name="service")
private Service service;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void test(){
verify(classUnderTest, times(1)).service.myMethod(4,2);
}
}
I know it is possible to use Getter/Setter-methods in my Facade to return the Service, but I want to do it, without doing that.
Is it possible, in the way I want to, without doing any change to the Facade?
And is there a difference, when I have a Spring-project and used #Autowired for the service variable inside the Facade?
Thanks!
Using #InjectMocks will inject your annotated #Mock service in your facade.
So you don't need any getter or setter for your test.
It seems you forgot to call method in your test.
Try with this :
#Test
public void test(){
classUnderTest.method();
verify(service, times(1)).service.myMethod(4,2);
}
Using #Autowired service in your facade will have no impact on your test since spring is not used in it. Spring will inject the right bean when you'll run your application.
Related
I am doing a migration from Spring 4.x to 5.x and am following the recommendation to wrap the object with an ObjectProvider to handle beans that return null: https://stackoverflow.com/a/49393682/10863988
This is the class set up I have:
class ConfigurationClass{
#Autowired
private ObjectProvider<MyObject> myObject;
public SomeOtherClass getSomeOtherClass() {
return new SomeOtherClass(myObject.getIfAvailable());
}
}
class TestSomeOtherClass {
#Mock
MyObject myObject;
#InjectMocks
ConfigurationClass;
SomeOtherClass someOtherClass;
public void setup() {
this.someOtherClass = spy(configuration.getSomeOtherClass());
}
}
The problem is when I run this test. the myObject in the ConfigurationClass returns a null pointer exception.
I've tried adding this to the TestSomeOtherClass but I still can't seem to mock the ObjectProvider<MyObject>:
class TestSomeOtherClass {
#Mock
MyObject myObject;
#Mock
ObjectProvider<MyObject> myObjectObjectProvider;
#InjectMocks
ConfigurationClass;
SomeOtherClass someOtherClass;
public void setup() {
doReturn(myObject).when(myObjectObjectProvider).getIfAvailable();
this.someOtherClass = spy(configuration.getSomeOtherClass());
}
}
Any advice on how to handle this?
You do not tell Mockito to handle it's annotations (#Mock, #InjectMocks) anywhere in your code, so they do not have any effect. By default all non-primitive fields in Java are initialized as null - that's where the NullPointerException comes from.
openMocks/initMocks method
Depending on the version of Mockito you're using, you need to call initMocks() or openMocks() static method from the MockitoAnnotations class:
AutoCloseable openMocks;
#BeforeEach
public void setup() {
// the line below is where the magic happens
openMocks = MockitoAnnotations.openMocks(this);
doReturn(myObject).when(myObjectObjectProvider)
.getIfAvailable();
someOtherClass = spy(configuration.getSomeOtherClass());
}
#AfterEach
void tearDown() throws Exception {
openMocks.close();
}
#Test
void test() {
assertNotNull(someOtherClass);
}
#ExtendWith(MockitoExtension.class)
You can also use the #ExtendWith(MockitoExtension.class) annotation over your class and it has the same effect as the methods described above.
You can find both approaches tested in a GitHub repository I've created (all tests pass).
I have the following Spring Service class that I'm trying to test with Mockito:
#Service
public class ObjectExportService {
#Autowired
protected List<SecuredService<? extends SecuredObject>> securedServices;
public void doStuff() {
for(int i = 0; i < this.securedServices.size(); i++){
SecuredService<? extends SecuredObject> securedSrv = this.securedServices.get(i);
//this access works
}
for (SecuredService<? extends SecuredObject> securedSrv : this.securedServices) { //this access does not work
}
}
}
This is my Test class for that service:
#RunWith(MockitoJUnitRunner.class)
public class ObjectExportServiceTest {
#InjectMocks
private ObjectExportService objectExportService;
#Mock
protected List<SecuredService<? extends SecuredObject>> securedServices;
#Test
public void testDoStuff(){
objectExportService.doStuff();
Assert.assertTrue(true);
}
}
When I run the test, I get a NullpointerException, but only in the for-each loop.
First I assumed is a similar problem as described in this thread:
I have Mocked the List and would therefore need to mock the iterator() call.
The solutions provided in that thread didn't work for me, because I am actually autowiring a List.
So I stumbled across this solution in another thread. Simply changing the #Mock to #Spy resolved the issue for me:
#RunWith(MockitoJUnitRunner.class)
public class ObjectExportServiceTest {
#InjectMocks
private ObjectExportService objectExportService;
#Spy // <-- change here
protected List<SecuredService<? extends SecuredObject>> securedServices;
#Test
public void testDoStuff(){
objectExportService.doStuff();
Assert.assertTrue(true);
}
}
I have this service:
#Service
public class MyService {
#Value("${my.value}")
private String myValue;
public String someMethodWhichUsesMyValueField() {
return myValue;
}
// Also contains other methods that use some external services accessed with a `RestTemplate`
}
And this integration test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyControllerTest {
private MockMvc myControllerMockMvc;
#Autowired
private MyController myController; // This controller injects an instance of MyService
#MockBean
private MyService myServiceMock;
#Before
public void setup() {
this.myControllerMockMvc = MockMvcBuilders.standaloneSetup(myController).build();
when(myServiceMock.someMethodWhichUsesMyValueField()).thenCallRealMethod();
// Also mock the other methods in myServiceMock that call the external webservices
}
#Test
public void someTest() throws Exception {
// use the myControllerMockMvc to call a POST method that calls myService.someMethodWhichUsesMyValueField()
}
}
The problem is that when myService.someMethodWhichUsesMyValueField() is called from the MyController method called from the someTest() method, the field myValue (and all the field annotated with #Autowired) is null even if my application.properties correctly defines my.value=some value.
Is there a way to correctly make myValue inject the value described in the application.properties like any normal #Autowired component?
Use a #SpyBean instead of a #MockBean because then you will have the real Spring object injected with some method that you can mock (thanks for #thomas-andolf's answer for the #Spy hint):
// [...] Nothing to change in annotations
public class MyControllerTest {
// [...] Nothing to change in the other fields
#SpyBean
private MyService myServiceMock;
#Before
public void setup() throws NoSuchFieldException, IllegalAccessException {
// [...] Nothing to change before mocking
// Then, do not mock anymore with .thenCallRealMethod() which is the default behavior with the spy
// And when mocking the other methods in myServiceMock that call the external webservices, use this kind of declaration:
doAnswer(invocation -> /* reply something */).when(myServiceMock).someMethodWhichUsesExternalWebService(any());
// instead of when(myServiceMock.someMethodWhichUsesExternalWebService(any())).thenAnswer(invocation -> /* reply something */);
}
// [...] Nothing to change in the test methods
}
The other (ugly?) way is to inject the field manually directly from the test class, like this for example:
// [...] Nothing to change in annotations
public class MyControllerTest {
// [...] Nothing to change in the other fields
#Value("${my.value}")
private String myValue;
#Before
public void setup() throws NoSuchFieldException, IllegalAccessException {
ReflectionTestUtils.setField(myServiceMock, "myValue", myValue); // thanks to #thomas-andolf's [answer](https://stackoverflow.com/a/55148170/535203)
// // Equivalent with traditional reflection tools:
// Field myValueField = MyService.class.getDeclaredField("myValue");
// myValueField.setAccessible(true);
// myValueField.set(myServiceMock, myValue);
// [...] the rest of the original method
}
// [...] Nothing to change in the test methods
}
You can set your properties in a test class
#TestPropertySource(properties = {"my.value=value"})
public class MyControllerTest {
//
}
A couple of ways you can solve this is the following, you can either use:
Constructor injection:
#Service
public class MyService {
private final myValue;
public MyService(#Value("${my.value}") myValue) {
this.myValue = myValue;
}
public String someMethodWhichUsesMyValueField() {
return myValue;
}
}
And instead of using a mock, just use the class and feed it it's value.
Or you can use:
ReflectionTestUtils.setField(myService, "myValue", "FooBar");
to set the myValue property to foobar.
Or you could change it to use a #Spy instead, which is a "partial mock". A spy is the original class with its original methods and then you can choose to mock some of the methods but keep the logic from the original class in some of the methods.
I am new to Spring Boot and Mockito and having a problem mocking out a repository call in my service test.
I have a "delete" service method call as follows that I am trying to test with Mockito by mocking out the repository calls:
public interface IEntityTypeService {
public EntityType getById(long id);
public EntityType getByName(String name);
public List<EntityType> getAll();
public void update(EntityType entityType);
public void delete(long id);
public boolean add(EntityType entityType);
}
#Service
public class EntityTypeServiceImpl implements IEntityTypeService {
#Autowired
private EntityTypeRepository entityTypeRepository;
#Override
public void delete(long id) {
entityTypeRepository.delete(getById(id));
}
#Override
public EntityType getById(long id) {
return entityTypeRepository.findById(id).get();
}
....implementation of other methods from the interface
}
My repository looks as follows:
#RepositoryRestResource
public interface EntityTypeRepository extends LookupObjectRepository<EntityType> {
}
I have not implemented any of the methods in the repository as I am letting Spring Boot wire it up for me.
My test is as follows:
#RunWith(SpringRunner.class)
public class EntityTypeServiceTest {
#TestConfiguration
static class EntityTypeServiceImplTestContextConfiguration {
#Bean
public IEntityTypeService entityTypeService() {
return new EntityTypeServiceImpl();
}
}
#Autowired
private IEntityTypeService entityTypeService;
#MockBean
private EntityTypeRepository entityTypeRepository;
#Test
public void whenDelete_thenObjectShouldBeDeleted() {
final EntityType entity = new EntityType(1L, "new OET");
Mockito.when(entityTypeRepository.findById(1L).get()).thenReturn(entity).thenReturn(null);
// when
entityTypeService.delete(entity.getID());
// then
Mockito.verify(entityTypeRepository, times(1)).delete(entity);
assertThat(entityTypeRepository.findById(1L).get()).isNull();
}
}
When I run the test, I get an error saying "java.util.NoSuchElementException: No value present"
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at xyz.unittests.service.EntityTypeServiceTest.whenDelete_thenObjectShouldBeDeleted(OriginatingEntityTypeServiceTest.java:41)
It references the line in the test saying Mockito.when(originatingEntityTypeRepository.findById(1L).get()).thenReturn(entity).thenReturn(null);
The reason I think I have to mock that call out is because the delete method in the Service calls the getById() method in the same service, which in turn calls entityTypeRepository.findById(id).get()
It is that, that I am assuming I have to mock out on the delete. But clearly I am wrong. Any assistance would be appreciated.
Many thanks
#Test
public void whenDelete_thenObjectShouldBeDeleted() {
final EntityType entity = new EntityType(1L, "new OET");
Optional<EntityType> optionalEntityType = Optional.of(entity);
Mockito.when(entityTypeRepository.findById(1L)).thenReturn(optionalEntityType);
// when
entityTypeService.delete(entity.getID());
// then
Mockito.verify(entityTypeRepository, times(1)).delete(entity);
//I dont think you need to assert to confirm actual delete as you are testing mock registry. to assert somethink like below you need to return null by mocking the same call again and return the null but thats of no use
//assertThat(entityTypeRepository.findById(1L).get()).isNull();
}
Updated your test. Basically we first need to mock the result of findById. refer my comment above asserting the actual delete.
when I run the test case I get AnObj mocked. this is used from inside the target classes method. when that method gets invoked the 'anOtherObj' is accessed and that is found to be null. Can some one please point out how to make sure 'anOtherObj' is not null so that I dont get nullpointer there?
#Test
public class TestTargetTest {
#Mock
private AnObj anObj;
#InjectMocks
private TestTarget testTarget;
#BeforeMethod
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testTarget() {
when(anObj.someMethod()).thenCallRealMethod();
testTarget.testTarget();
}
}
#Component
public class TestTarget {
#Autowired
private AnObj anObj;
public void testTarget(){
anObj.someMethod();
}
}
#Component
public class AnObj {
#Autowired
private AnOtherObj anOtherObj;
public void someMethod(){
syso(anOtherObj.toString());
}
}
You need to initialize annotated mocks in your test class.
#BeforeMethod
public void beforeClass() {
MockitoAnnotations.initMocks(this);
}
Why would you care what's inside mock (AnObj)? I assume you haven't yet declared interactions on that mock using Mockito.when.
As mentioned by #Valya that point was valid. I shouldn't have mocked that. I needed to autowire 'AnObj'. Thanks a lot for all the help. It made the difference.