While testing my Spring classes with EasyMock, I came to this below scenario:
My Spring configuration is taking the original DAO object configured by Spring component-scan rather than my mock DAO object.
Please find my mock, AppContext and test class below:
ApplicationContxt-Test.xml
<context:annotation-config />
<context:component-scan base-package="com.test.testclasses"/>
<import resource="mockServices.xml" />
MockServices.xml
<bean class="org.easymock.EasyMock" factory-method="createMock"
id="codeDAO" primary="true" >
<constructor-arg value="com.test.testclasses.dao.MaintainCodeDAO" />
</bean>
JUnit class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:ApplicationContxt-Test.xml")
public class MaintainCodeServiceImplTest {
#Autowired
private MaintainCodeDAO codeDAO;
#Autowired
private MaintainCodeService maintainCodeService;
#Before
public void setUp() {
EasyMock.reset(codeDAO);
}
#After
public void tearDown() {
EasyMock.reset(codeDAO);
}
#Test
public void shouldAutowireDependencies() {
assertNotNull(codeDAO);
assertNotNull(maintainCodeService);
}
#Test
public void getProcedureByCode_success() throws Exception {
MaintainCodeVO maintainCodeVO = new MaintainCodeVO();
EasyMock.expect(codeDAO.searchProcedureCode(isA(String.class))).andReturn(maintainCodeVO);
EasyMock.replay(codeDAO);
MaintainCodeBO maintainCodeResult = maintainCodeService.getProcedureByCode("test");
EasyMock.verify(codeDAO);
codeDAO.searchProcedureCode("test");
assertNotNull(maintainCodeResult);
EasyMock.reset(codeDAO);
}
}
Here I am mocking codeDAO and while testing the service class, instead of the mock codeDAO, the original DAO object is getting autowired and EasyMock.verify() is throwing exception. Don't know whats the issue. Is there any problem with the above configuration?
I found the solution. The trick was so simple .. I killed one day finding the solution..
So here is the solution:
Just move the mockServices.xml (spring xml with only mock configurations) above context:component-scan...
<import resource="mockServices.xml" />
<context:annotation-config />
<context:component-scan base-package="com.wellpoint.icnotes"/>
Worked like a charm.
If I remember correctly, because of createMock is a generic method, the Spring doesn't work properly while inferring the bean type, it infers it as an Object, so it cannot wire. I suggest not to use the Spring in unit tests, use simple classes.
BTW, EasyMock has it's own DI, just use it: http://easymock.org/user-guide.html#mocking-annotations
It is possible to put mocks into context, e.g. as described here: Autowiring of beans generated by EasyMock factory-method? but for me it is not the best approach, looks clumsy.
If you are using Spring Framework 4.2 or higher, this should work. See the following related issues for details.
https://jira.spring.io/browse/SPR-9567
https://jira.spring.io/browse/SPR-9682
Is there any way to override a bean discovered by component scan?
Related
I want to unit test a java class with an autowired final class object, as well as another autowired class that has #PostConstruct method. While it is possible to test them individually, i am not able to combine them together.
This question is an extension to the question on injecting mockito mocks into spring bean
Code to be tested
public class A {
#Autowired
private FinalClass serviceClient;
#Autowired
private ClassWithPostConstructor resourceVerifier;
//no setters or constructors
public String useBothFinalClassAndClassWithPostConstructor() {
//logic to be tested
}
}
Working Test class
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClass.class)
public class ATest {
//#org.mockito.Mock //fails to mock final class
#org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
#org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
//#InjectMocks //fails since mocking final class
private A a;
#Before
public void init() {
a = new A();
//working snippet with setters created in A and without #Autowired here within the test
serviceClient = PowerMock.create(FinalClass.class);
a.setServiceClient(serviceClient);
resourceVerifier = PowerMock.create(ClassWithPostConstructor.class);
a.setClassWithPostConstructor(resourceVerifier);
}
#Test
public void testTheMethodUsingExpectAndVerify() {
//test the functionality here
EasyMock.expect(serviceClient.callService()).andReturn("someMock");
EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
PowerMock.replayAll();
A.useBothFinalClassAndClassWithPostConstructor();
PowerMock.verifyAll();
}
}
The above code works with the need for setters in file
Expected Test class
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:spring-configuration/unit-testing-config.xml"})
#PrepareForTest(FinalClass.class)
public class ATest {
#Autowired
private FinalClass serviceClient;
#Autowired
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
private A a;
#Before
public void init() {
a = new A();
}
#Test
public void testTheMethodUsingExpectAndVerify() {
//test the functions here
EasyMock.expect(serviceClient.callService()).andReturn("someMock");
EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
PowerMock.replayAll();
A.useBothFinalClassAndClassWithPostConstructor();
PowerMock.verifyAll();
}
}
//spring-configuration/unit-testing-config.xml
//same error even on customer factory
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
<constructor-arg type="java.lang.Class" value="com.company...resourceVerifier" />
</bean>
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
<constructor-arg type="java.lang.Class" value="com.company...serviceClient" />
</bean>
The above snippet mocks finalClass but calls #PostConstructor of ResourceVerifier.class - What should be done here to overcome this call.
Investigations
It is possible to test autowired files using #InjectMocks without the need for spring context configurations.
#InjectMock fails silently for static and final fields and when failing, it doesn't inject other mocks as well.
It is possible to mock final class using PowerMock's createMock and run the test with PowerMockRunner and #PrepareForTest. But this requires new unnecessary setters to inject the mocks for #Autowired fields.
MockitoAnnotations.#Mock doesn't work along well with PowerMock (especially when mocking final class objects) and can be solved via EasyMock.Annotations.#Mock
EasyMock and PowerMock doesn't have an #InjectMocks annotation to inject the mocks as possible by Mockito (Would have solved the problem in secs).
It is possible to inject autowired spring beans via SpringJUnit4Runner and a separate unit-testing #ContextConfiguration
It is possible to run the same test file with both PowerMockRunner and SpringJUnit4Runner with PowerMockRunnerDelegate
I know that #PostConstruct method will not be executed automatically if mocked in code than by using spring bean creation and injection.
If a wrapper factory bean class is written and used to create the mocks, it injects automatically, but calls the #PostConstruct method as well along with it.
It is not possible to depend on Springockito since it is unreliable at this stage.
But none of these worked since the usecase is a combination of all these.
Possible Solutions
Remove #Autowired fields and use Setter injections so that it is possible by mocking normally using PowerMock(Tested to work) - But it is a convention followed by external team packages - i should try my best to stick to it.
Or set #Autowired to the setters or to constructor
Alternatives?
I don't feel that the classes require reorganisation as they serve their purposes and are well designed as well.
Any other means that doesn't require to keep hands on the class under test - What if i didn't have the permissions to modify this class? i.e., a pure testing library dependent solution.
Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.
Hmm,
Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.
Seems to me you have a mess in your head and misunderstanding PowerMock/Mockito and EasyMock.
You should never use at same time PowerMockito and PowerMock, because these two classes are PowerMock friendly API for two different mocking frameworks: EasyMock and Mockito. And there is no reason to use they both.
And of course this want work
//#org.mockito.Mock //fails to mock final class
#org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
#org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
//#InjectMocks //fails since mocking final class
private A a;
Because, you decelerate and create mocks via EasyMock API, but tries to inject it with Mockito Annotation.
You have to choose only one Mocking Framework and use suitable API for it. For now, Mockito + PowerMockito (PowerMock API for Mockito) better fit your requirements.
You may full example how it works on PowerMock github
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClass.class)
public class SpringInjectFinalClassExampleTest {
#Mock
private FinalClass finalClass;
#InjectMocks
private MyBean myBean = new MyBean();;
#Test
public void testInjectFinalClass() {
final String value = "What's up?";
when(finalClass.sayHello()).thenReturn(value);
assertEquals(value, myBean.sayHello());
}
}
I am currently fronting issues mixing a CXF web service with Spring #Configurable annotation.
From one side I have my CXF web service fully working and configured like this :
<import resource="classpath:some-other-context.xml" />
<jaxws:server id="Init"
serviceClass="package.to.my.ServiceInterface"
address="/">
<jaxws:serviceBean>
<bean class="package.to.my.BADematInitImpl">
</bean>
</jaxws:serviceBean>
</jaxws:server>
<context:spring-configured />
And in my some-other-context.xml is my Spring configuration containing the following Bean's :
#Configurable(autowire = Autowire.BY_TYPE)
public class MyConfigurable {
#Autowired(required=true)
private A a;
#Autowired(required=true)
private B b;
#Autowired(required=true)
private C c;
...
}
But when I try to create a new instance of MyConfigurable bean into my service, I get a NullPointerException due to the null valued supposed-autowired A,B and C objects.
Any idea ?
#Configurable is a marker used by the AOP load-time-weaving stuff. I assume you are not using any AOP, because there is nothing mentioned about it in your question. Second thing: you don't have to use required=true in your #Autowired annotation as true is the default value of required. I would suggest you to change your code like this:
Add these to elements in your spring configuration file:
<context:component-scan base-package="your.pckg.toscan"/>
Documentation says:
Scans the classpath for annotated components that will be
auto-registered as Spring beans. By default, the Spring-provided
#Component, #Repository, #Service, and #Controller stereotypes will
be detected.
<context:annotation-config/>
Documentation says:
Activates various annotations to be detected in bean classes: Spring's
#Required and #Autowired, as well as JSR 250's #PostConstruct,
#PreDestroy and #Resource (if available), JAX-WS's #WebServiceRef (if
available), EJB3's #EJB (if available), and JPA's #PersistenceContext
and #PersistenceUnit (if available). Alternatively, you may choose to
activate the individual BeanPostProcessors for those annotations.
So changing your code to:
#Component
public class MyConfigurable {
#Autowired
private A a;
#Autowired
private B b;
#Autowired
private C c;
...
}
Everything should work just fine.
I finally found out the problem.
I needed to add this configuration into my third-part application XML files :
<context:load-time-weaver/>
... and add this argument to my jvm launch command line :
-javaagent:"path\to\my\spring-agent.jar"
And it just works like a charm.
I understand that this is because Spring needs at some point to have an entity managing its AOP part so that the dependencies are well injected. It would be interesting if someone had further explanations.
I have an AuthenticationManager.authenticate(username,password) method that gets called in someMethod of a SomeService under test. The AuthenticationManager is injected into SomeService:
#Component
public class SomeService {
#Inject
private AuthenticationManager authenticationManager;
public void someMethod() {
authenticationManager.authenticate(username, password);
// do more stuff that I want to test
}
}
Now for the unit test I need the authenticate method to just pretend it worked correctly, in my case do nothing, so I can test if the method itself does the expected work (Authentication is tested elsewhere according to the unit testing principles, however authenticate needs to be called inside that method) So I am thinking, I need SomeService to use a mocked AuthenticationManager that will just return and do nothing else when authenticate() gets called by someMethod().
How do I do that with PowerMock (or EasyMock / Mockito, which are part of PowerMock)?
With Mockito you could just do that with this piece of code (using JUnit) :
#RunWith(MockitoJUnitRunner.class)
class SomeServiceTest {
#Mock AuthenitcationManager authenticationManager;
#InjectMocks SomeService testedService;
#Test public void the_expected_behavior() {
// given
// nothing, mock is already injected and won't do anything anyway
// or maybe set the username
// when
testService.someMethod
// then
verify(authenticationManager).authenticate(eq("user"), anyString())
}
}
And voila. If you want to have specific behavior, just use the stubbing syntax; see the documentation there.
Also please note that I used BDD keywords, which is a neat way to work / design your test and code while practicing Test Driven Development.
Hope that helps.
I have a routes defined in CamelRoutes.xml and I would like to test them by using the wrapping technique described at the bottom of http://camel.apache.org/mock.html.
My CamelRoutes.xml
<route autoStartup="true" xmlns="http://camel.apache.org/schema/spring">
<from uri="direct:start"/>
<to uri="direct:end"/>
</route>
So I created CamelRoutesTest.xml containing:
<import resource="CamelRoutes.xml"/>
<bean id="mockAllEndpoints" class="org.apache.camel.impl.InterceptSendToMockEndpointStrategy"/>
but I am not sure how to create a test that both loads the spring xml AND provides access to the mock endpoints.
If I use..
#ContextConfiguration( locations=("/CamelRoutesTest"))
public class CamelTest extends AbstractJUnit38SpringContextTests
}
then I have no idea how to get the mock endpoints
If I use..
public class CamelTest extends CamelTestSupport
}
then I dont know how to load my camel context..
I can't seem to find an example test on the website that uses CamelTestSupport AND loads routes from spring xml.
Tom you already posted this on the Camel mailing list. I suggest that you write this when you post a Q here as well.
The answer is already posted here
http://camel.465427.n5.nabble.com/Problems-testing-Camel-with-Spring-config-tp4267754p4267754.html
Solved using:
public class CamelTest extends CamelSpringTestSupport {
#Override
protected AbstractXmlApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("/CamelRoutesTest.xml");
}
#Test
#DirtiesContext
public void discover() throws Exception {
getMockEndpoint("mock:my.route.out").expectedMessageCount(1);
template.sendBody("direct:my.route.in", FileUtils.slurpClassPathFile("/samples/sample.data.xml"));
assertMockEndpointsSatisfied();
}
}
Thanks for this solution, #Tom !
I also had a hard time getting Camel to work with my Spring-powered JUnit tests, problem is the suggested CamelSpringJUnit4ClassRunner has now moved from camel-spring.jar to a separate module, camel-spring-test.jar, which is not available in the standard camel-distribution as of 2.9.2.
So I cannot use #RunWith but had to resort to the manual method described in the solution...
Does anyone know how to run unit tests for Liferay portlets? I have found a lot of posts about it (e.g. http://agile-reflections.opnworks.com/2010/06/portlet-unit-testing-with-liferay-6.html) but none works nonetheless.
This may be overkill, but if you're looking for an Enterprise approach with continuous integration testing, this blog gives a very good example: Continuous integration on Liferay: running your Selenium 2 tests on the Tomcat 6 bundle
Unit testing Liferay portlets is quite complicated when ServiceBuilder is utilized.
The reason is that it generates quite heavy services that contain references not only to beans within Portlet, but even to the Portal beans generated by ServiceBuilder.
There are tools like InitUtil.init(); that lets you at least instantiate and use ServiceBuilder entities... not EntityServices though. For that you'd have to use SpringUtil.loadContext(); that requires
System.setProperty("external-properties", "testing.properties");
where testing.properties contains :
spring.configs=META-INF/ext-spring.xml,\
META-INF/base-spring.xml,\
META-INF/dynamic-data-source-spring.xml,\
META-INF/infrastructure-spring.xml,\
META-INF/shard-data-source-spring.xml,\
META-INF/hibernate-spring.xml,\
META-INF/portlet-spring.xml
These are spring definitions to be loaded for testing application context. It all would be OK, but beans from portlet-spring.xml are those heavy services containing references to Portal bean definitions like ResourceService, UserLocalService, CounterLocalService and you would have to load even META-INF/portal-spring.xml and trust me, it's not that easy cause then you'd have to load quite a lot of other stuff.
THE ANSWER:
The truth is, that you most likely won't have to unit test portlet SB services, never. They represent entities with persistence and service layer around. Something that is not to be tested. You just have to mock them and stub their methods, right ?
And the best way for junit and integration testing as to mocking is not using *LocalServiceUtil static classes in your application, because it is almost unmockable.
You just need to create a Spring FactoryBean :
public class PortalFactoryBean implements FactoryBean {
private Class type;
public void setType(final Class type) {
this.type = type;
}
#Override
public Object getObject() throws Exception {
return PortalBeanLocatorUtil.locate(type.getName());
}
#Override
public Class getObjectType() {
return type;
}
}
public class PortletFactoryBean implements FactoryBean {
private Class type;
public void setType(final Class type) {
this.type = type;
}
#Override
public Object getObject() throws Exception {
return PortletBeanLocatorUtil.locate(type.getName());
}
#Override
public Class getObjectType() {
return type;
}
}
<bean id="somePortalBean" class="example.spring.PortalFactoryBean" lazy-init="true">
<property name="type" value="com.liferay.some.util.SomeService"/>
</bean>
<bean id="somePortletBean" class="example.spring.PortletFactoryBean" lazy-init="true">
<property name="type" value="com.example.SomeService"/>
</bean>
#Autowired
private SomeService somePortalBean;
Writing unit/integration tests for this portlet would be quite easy, right ? You just create a spring context for testing and you mock these services :
Using Service Builder is worth it, but you must have some Spring knowledge and play with it for some time. Then it spares a lot of time because it is easy to maintain.
You need to have some third party libraries on classpath.
THe key point is having even portal-impl.jar and other portal dependencies on classpath and having InitUtil.initWithSpring(boolean); load up core spring xml configs that you specify in spring-ext.properties in spring.congigs property, only those services you need. You may need no portal services and only the portlet ones, but this is a problem because your portlet services generated by service builder use the portal services.
Using service builder just needs good knowledge of spring and classloading.
But you need to understand the infrastructure before doing that. There are quite a lot of hacks needed... Like
BeanLocator beanLocator = new BeanLocatorImpl(PortalClassLoaderUtil.getClassLoader(), ac);
PortletBeanLocatorUtil.setBeanLocator("portlet", beanLocator);