Spring / Mock components that are retrieved by using the context directly - java

We have a project that uses Spring annotations to configure its context.
To test this project we are using Mockito and it's Spring extension.
In tests I need to override some beans with mock/spy version.
With the #Mock/#Spy and #InjectMock annotations I have been able to use spy for beans using autowiring mechanism.
Now I have a third party component which create another Spring context and then merge the 2 contexts together. This third party component retrieve beans using a call to the context:
applicationContext.getBean(key);
In this case, the #Mock/#Spy and #InjectMock combination is not working.
The 'workaround' solution I have put in place is an XML file in which I declare my spied bean:
<mockito:spy beanName="beanToSpy"/>
To stay in the annotation world, I have tried springockito-annotations as mentioned in these similar questions:
Injecting Mockito mocks into a Spring bean
and its duplicate:
How to inject a Mock in a Spring Context
But the bean is not spied or mocked, I've probably a configuration error.
My current setup is:
A base class that is in charge of the Spring config for test:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#ContextConfiguration(loader= SpringockitoContextLoader.class, locations ={"/config.xml","/test-config.xml"})
public abstract class BaseTest {
//...
}
A test class that would like to use a mocked bean:
public class MyTest extends BaseTest {
#ReplaceWithMock #Autowired MyService myService;
#WrapWithSpy #Autowired OtherService otherService;
#Test public void someTest() {
doReturn(x).when(myService).call();
doReturn(y).when(otherService).process();
}
}
Unfortunately in MyTest, the beans are not replaced by their mock/spy versions, it is the plain old regular version.
EDIT:
The third party component that does the lookup is using its own spring parent context and add the current spring context into its own. The lookup to retrieve the component that I want to be mocked occurs after the context has been fully loaded.
What should I do to properly replace the bean in the context with a mock/spy version ?
What is wrong with the way I'm using #WrapWithSpy / #ReplaceWithMock ?

When does the call to
applicationContext.getBean(key);
happens? Is it possible that it retrieves the bean before the SpringockitoContextLoader has a chance to replace it with a spy?
One solution to stay in annotation world would be to override the bean in java config:
#Bean
public MyBeanType myBeanType() {
return spy(new MyBeanType(...))
}

The more conventional way to perform this is by simply mocking them in the test as required :
public class MyTest extends BaseTest {
#Test public void someTest() {
MyService myMockService = Mockito.mock(MyService.class);
// define when's
// perform verification
}
You can inject using SpringReflectionTestUtils, or if using setter injection just set normally.
If you use a global mocked bean in a test class with multiple tests, the results can get confusing.

Related

Spring why #MockBean can't autowire an interface using profile

I've an interface with two implementations. Which implementaton is to be used depends of the environment (production, development, test, ...). I therefore use Spring profiles. I'm using a configuration file to instantiate the correct implementation.
#Configuration
public class BeanConfiguration {
#Profile({"develop","test-unit"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_1(){
return new EmailSenderImpl_1();
}
#Profile({"prod"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_2(){
return new EmailSenderImpl_2();
}
}
When the Spring container starts (with a specific profile), the correct bean is autowired into the class, and all works fine.
#Component
public class CustomerEmailProcessor {
#Autowire
private CustomerEmailSender customerEmailSender;
...
}
I also have a test class in which I want to autowire the bean. I'm using #Mock for autowiring.
The profile is set to "test-unit" in the test class. So, I'm expecting the spring container to lookup in the config class for the correct bean to be instantiated. But this doesn't happen.
Instead, an Exception is thrown :
Caused by: java.lang.IllegalStateException: Unable to register mock bean .... expected a single matching bean to replace but found [customerEmailSender, emailSenderImpl_1, emailSenderImpl_2]
When using #Autowire annotation, all goes fine. But of course the bean is not mocked anymore and that's what I need to have.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {"test-unit"})
#Import(BeanConfiguration.class)
public class CustomerEmailResourceTest {
#MockBean
private CustomerEmailSender customerEmailSender;
}
I've put a breakpoint in the config class, and I can see that when using #Autowire in the test class, the correct bean is instantiated (breaks at the line "return new EmailSenderImpl_1();".
When using #Mock, no bean at all is instantiated. Spring doesn't break at the line "return new EmailSenderImpl_1();"
Why is it that Spring can find the correct bean when using the #Mock annotation.
The #Mock annotation must be the reason that Spring doesn't use the config class "BeanConfiguration.java". That makes sence after all.

Micronaut with MockitoTests

I have a requirement to test my service layer in Micronaut.
I am using bean validation in my service layer and so I need validator to be injected in the testing phase and I can't just mock it. However, I want to mock some other classes and so I am also using Mockito as well.
The problem is if I am putting #MicornautTest on my class all the beans marked with #Inject are Not-Null which means that #Inject is working fine. However, as soon as I add #ExtendWith(MockitoExtension.class) to the class and re-run the tests, all the means marked with #Inject are now null and all the beans marked with #Mock are Not-Null.
So it looks like either I can use #Inject in my tests or I can use #Mock but not both.
#MicronautTest
#ExtendWith(MockitoExtension.class)
class CashServiceTest {
#Inject
private Validator validator;
#Mock
private AvailableCashClient availableCashClient;
Has anyone faced similar issue earlier? Can you please guide me the correct configuration which will allow me to use both #Inject and #Mock in the same tests.
I was able to find the issue and was able to use both mocked beans and original injected beans.
The issue is we should not use #ExtendWith annotation and can achieve everything just by using #micronaut test.
I am pasting code for the set up which I did. In this case, BusinessClient is the bean which is being mocked and Validator is the bean which is injected without being mocked.
#MicronautTest
class CashServiceTest {
#Inject
AvailableCashClient availableCashClient;
#Inject
Validator validator;
#MockBean(AvailableCashClient)
public AvailableCashClient availableCashClient() {
return Mockito.mock(AvailableCashClient);
}

Inject JMockit mocks into Spring context

For some integration tests, we use Spring’s #ContextConfiguration to create a real Spring context during the test. Now, it’s not supposed to be a full integration test, so we need a whole bunch of the Spring beans as mocks. This is not too complicated using Mockito and Spring’s factory-method, and even easier with Springockito.
But, this is using Mockito, while we are just migrating to JMockit. I would much prefer to use JMockit here as well. Basically, I am looking for a replacement for Springockito that uses JMockit instead.
I can also do it by hand. However, Mockito and JMockit seem to differ in one very important way: While in Mockito, you create mocks imperatively using a call to a method, in JMockit you get mocks declaratively ‘injected’ into your test. That’s too late to populate the Spring context. So if anyone can answer that, I’m happy as well: How can you create a mock in JMockit in your code?
If you are using Spring Test to do all the injection, then you can just let it do the job of creating instances for all dependencies, while having them mocked through suitable mock fields/parameters declared with the #Mocked or #Capturing annotations. The latter one will mock any implementation class that Spring has chosen to instantiate, even though the type used in the mock declaration is an interface or base class.
Alternatively, you could just let JMockit itself resolve all dependencies, by using #Tested(fullyInitialized = true) for top-level tested objects, with mocked dependencies provided as #Injectable's.
A "dirty" trick we use with Spring and Integration Tests while we still need to mock something, is to replace - where required - the real configuration, e.g.
#Configuration
#Profile("!test")
public class MyConfig {
#Bean
public MyBean bean() {
/** Real bean **/
}
}
with a mock one
#Configuration
#Profile("test")
public class MyTestConfig {
#Bean
public MyBean bean() {
final MyBean bean = mock(MyBean.class);
when(bean.doSomething()).thenReturn(withReply());
return bean;
}
}
it works with a "real" Spring Integration Test context and Mockito, it should work with JMockit as well, as long as you are able to create your bean with JMockit version of your class: basically something equivalent to mock(MyBean.class).
Edit: Whilst I am not familiar with JMockit, it seems that an equivalent way could be
#Configuration
#Profile("test")
public class MyTestConfig {
#Injectable MyBean mockXyz;
#Bean
public MyBean bean() {
/** You can probably mock its behaviour **/
return mockXyz;
}
}

Injecting Mockito Mock objects using Spring JavaConfig and #Autowired

I'm trying to replace an #Autowired object with a Mockito mock object. The usual way of doing this was with xml using Springockito:
<mockito:mock id="SomeMock" class="com.package.MockInterface" />
Currently I'm trying to move over to using Spring's JavaConfig to do the job. All of a sudden the Java expressions are a whole lot more verbose than xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTestClass {
#Configuration
static class Config {
#Bean
public MockInterface somethingSpecial() {
return Mockito.mock(MockInterface.class);
}
}
#Autowired MockInterface mockObj;
// test code
}
I discovered a library called Springockito-annotations, which allows you to do the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=SpringockitoContextLoader.class)
public class MyTestClass {
#Autowired #ReplaceWithMock MockInterface mockObj;
// test code
}
Clearly, a whole lot prettier :) The only problem is that this context loader doesn't allow me to use #Configuration and JavaConfig for other beans (if I do, Spring complains that there are no candidates that match those autowired fields).
Do you guys know of a way to get Spring's JavaConfig and Springockito-annotations to play nice? Alternatively, is there another shorthand for creating mocks?
As a nice bonus, using Springockito and xml config, I was able to mock out concrete classes without providing autowiring candidates to its dependencies (if it had any). Is this not possible without xml?
Moving away from the now unmaintained (as of this writing) Spingockito-annotations and to Mockito, we have a way of doing this very simply:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#Mock MockInterface mockObj;
// test code
}
If you're using a real object, but would like to mock a dependency within it, for instance testing a service layer with DAO:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
// test code
}
Finally, this can also be applied to Spring-boot, but using annotation initialization within setUp() until multiple class runners are supported:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyMainSpringBootClass.class)
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
#Before
public final void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
}
// test code
}
Outdated and deprecated!
Read about mocking and spying in Spring Boot 1.4
Please read also #ethesx answer,
Springockito is unmaintaned
Old answer
This is possible now to mock Spring application without any XML file with Springockito-annotations.. This solution works also with Spring Boot.
import static org.mockito.BDDMockito.*;
import org.kubek2k.springockito.annotations.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
loader = SpringockitoAnnotatedContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MainControllerTest {
#Autowired
MainController mainController;
#Autowired
#ReplaceWithMock
FooService fooService;
#Test
public void shouldGetBar() {
//given
given(fooService.result("foo")).willReturn("bar");
//when
Bar bar build = fooService.getBar("foo");
//then
assertThat(bar).isNotNull();
}
}
Dependencies: org.kubek2k:springockito-annotations:1.0.9
It appears that SpringockitoContextLoader extends GenericXmlContextLoader which is described as:
Concrete implementation of AbstractGenericContextLoader that reads bean definitions from XML resources.
So you are limited to xml bean definitions at the moment.
You could write your own context loader, taking relevant parts from the SpringockitoContextLoader class. Take a look here to get started, perhaps you could extend AnnotationConfigContextLoader for example?

How to access Spring #Service object from jUnit test

Situation: I have service implementation class annotated with #Service with access to properties file.
#Service("myService")
public class MySystemServiceImpl implements SystemService{
#Resource
private Properties appProperties;
}
Properties object is configured through config-file. applicationContext.xml
<util:properties id="appProperties" location="classpath:application.properties"/>
I want to test some methods of this implementation.
Question:How to access MySystemServiceImpl-object from test class in such way that Properties appProperties will be initialized properly?
public class MySystemServiceImplTest {
//HOW TO INITIALIZE PROPERLY THROUGH SPRING?
MySystemServiceImpl testSubject;
#Test
public void methodToTest(){
Assert.assertNotNull(testSubject.methodToTest());
}
}
I can't simple create new MySystemServiceImpl - than methods that use appProperties throws NullPointerException. And I can't directly inject properties in the object - there is no appropriate setter-method.
Just put correct steps here (thanks to #NimChimpsky for answer):
I copied application.properties under test/resources dir.
I copied applicationContext.xml under test/resources dir. In application context I add new bean (definition of application properties is already here):
<bean id="testSubject" class="com.package.MySystemServiceImpl">
I modified test class in such way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext.xml"})
public class MySystemServiceImplTest {
#Autowired
MySystemServiceImpl testSubject;
}
And this make the trick - now in my test class fully functional object is available
Alternatively, to do an integration test I do this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
#Transactional
public class MyTest {
#Resource(name="myService")
public IMyService myService;
Then use the service as you would normally. Add the app context to your test/resources directory
Just use its constructor :
MySystemServiceImpl testSubject = new MySystemServiceImpl();
This is a unit test. A unit test tests a class in isolation from the other classes and from the infrastructure.
If your class has dependencies over other interfaces, mock those interfaces and create the object with these mocks as argument. That's the whole point of dependency injection : being able to inject other, mock implementations inside an object in order to test this object easily.
EDIT:
You should provide a setter for your properties object, in order to be able to inject the properties you want for each of the unit tests. The injected properties could contain nominal values, extreme values, or incorrect values depending on what you want to test. Field injection is practical, but doesn't fit well with unit-testing. Constructor or setter injection should be preferred when unit-testing is used, because the main goal of dependency injection is precisely to be able to inject mock or specific dependencies in unit tests.

Categories