My question is very similar to the issue raised in Injecting Mockito mocks into a Spring bean. In fact, I believe the accepted answer there might actually work for me. However, I've got one issue with the answer, and then some further explanation in case the answer there is not in fact my answer.
So I followed the link in the aforementioned post to the Springockito website. I altered my test-config.xml to include something similar to the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mockito.org/spring/mockito http://www.mockito.org/spring/mockito.xsd">
...
<mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
...
</beans>
There seems to be something wrong with the www.mockito.org redirect currently, so I found the XSD code at https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd and altered the final entry in xsi:schemaLocation to point to this bitbucket link.
Running mvn test then produced the following error (newlines added for readability):
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 43 in XML document from class path resource [spring/test-context.xml] is invalid;
nested exception is org.xml.sax.SAXParseException; lineNumber: 43; columnNumber: 91;
The prefix "mockito" for element "mockito:mock" is not bound.
So the question regarding Springockito is: Is it possible anymore to include this? What am I missing?
Now, on to the further explanation...
I have an interface whose implementation I'm trying to test:
public interface MobileService {
public Login login(Login login);
public User getUser(String accessCode, Date birthDate);
}
The implementation contains a DAO that Spring #Autowires in for me:
#Service
public class MobileServiceImpl implements MobileService {
private MobileDao mobileDao;
#Autowired
public void setMobileDao(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
}
I don't want to alter my interface to include a setMobileDao method, because that would be adding code just to support my unit testing. I'm trying to mock out the DAO since the actual SUT here is the ServiceImpl. How can I achieve this?
You don't want to test your interface: it contains no code at all. You want to test your implementation. So the setter is available. Just use it:
#Test
public void testLogin() {
MobileServiceImpl toTest = new MobileServiceImpl();
toTest.setMobileDao(mockMobileDao);
// TODO call the login method and check that it works as expected.
}
No need for a spring context. Just instanciate your POJO service, inject mock dependencies manually, and test the methods you want to test.
After struggling with the Springockito XSD issue, for a while, I found a much simpler solution. Let Spring inject the mock for you using a factory method, i.e. in applicationContext.xml put:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.gerrydevstory.mycoolbank.AccountsDAO"/>
</bean>
<bean class="com.gerrydevstory.mycoolbank.BankingService"/>
</beans>
where the AccountsDAO bean is injected into the BankingService class. The corresponding JUnit test case is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/BankingServiceTest.xml")
public class BankingServiceTest {
#Autowired private BankingService bankingService;
#Autowired private AccountsDAO mockAccountsDAO;
#Test
public void testTransfer() throws Exception {
// Setup 2 accounts
Account acc1 = new Account();
acc1.setBalance(800.00);
Account acc2 = new Account();
acc2.setBalance(200.00);
// Tell mock DAO to return above accounts when 1011 or 2041 is queried respectively
when(mockAccountsDAO.findById(1011)).thenReturn(acc1);
when(mockAccountsDAO.findById(2041)).thenReturn(acc2);
// Invoke the method to test
bankingService.transfer(1011, 2041, 500.00);
// Verify the money has been transferred
assertEquals(300.00, acc1.getBalance(), 0.001);
assertEquals(700.00, acc2.getBalance(), 0.001);
}
}
Personally I find this very elegant and easy to understand. For more details, see the original blog post.
You have three options to set your mock dao:
Test the implementation - which gives a seam for your mock via the setDao method. (as JB's answer)
Add the setDao method to the interface - not desired since you don't want to add code just to support your tests.
Add a constructor to the impl class to accept the dao - not desired for same reason as #2.
If you wanted to do #3, you'll need to add a constructor to the MobileService that accepts the MobileDao.
public MobileServiceImpl(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
Then your test will look like this:
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.*;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
public class MobileServiceImplTest {
private MobileService systemUnderTest;
private MobileDao mobileDao;
#Before
public void setup() {
mobileDao = mock(MobileDao.class);
systemUnderTest = new MobileServiceImpl(mobileDao);
}
#Test
public void testGetUser() {
//if you need to, configure mock behavior here.
//i.e. when(mobileDao.someMethod(someObject)).thenReturn(someResponse);
systemUnderTest.getUser("accessCode", new Date());
verify(mobileDao).getUser("JeffAtwood");
}
}
Please note that you have not provided us with the details of the MobileDao so I created a getUser method that accepts a String.
To make the test pass, your MobileServiceImpl would just need this:
mobileDao.getUser("JeffAtwood");
The problem looks like your classpath doesn't contain actual springockito jar - You don't have to change the URL's - these are only the tokens that are used internally by spring - they are not being resolved - all You need is a new enough Spring distribution and springockito on classpath.
Kuba (creator of aforementioned lib :) )
I had same problem, I wanted use springockito by according their wiki but validations xml throw errors. So when i tried go to locations where xsd supposed to be, that hasn't. So i read this and get to working with this:
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.mockito.org/spring/mockito
https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd">
But when i looking at this link, had bad feeling. It seems to me that's not permanent stable link (you must check if i wrong)
Related
I'm building a Quarkus app which handles http requests with resteasy and calls another api with restclient and I need to propagate a header and add another one on the fly so I added a class that implements ClientHeadersFactory.
Here's the code:
#ApplicationScoped
public abstract class MicroServicesHeaderHandler implements ClientHeadersFactory {
#Inject
MicroServicesConfig config;
#Override
public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders) {
// Will be merged with outgoing headers
return new MultivaluedHashMap<>() {{
put("Authorization", Collections.singletonList("Bearer " + config.getServices().get(getServiceName()).getAccessToken()));
put("passport", Collections.singletonList(incomingHeaders.getFirst("passport")));
}};
}
protected abstract String getServiceName();
My issue is that the injection of the config doesn't work. I tried both with #Inject and #Context, as mentioned in the javadoc of ClientHeadersFactory. I also tried to make the class non abstract but it doesn't change anything.
MicroServicesConfig is a #Startup bean because it needs to be initialized before Quarkus.run() is called, otherwise the hot reload doesn't work anymore, since it's required to handle requests.
Here's the code FYI:
#Getter
#Startup
#ApplicationScoped
public final class MicroServicesConfig {
private final Map<String, MicroService> services;
MicroServicesConfig(AKV akv, ABS abs) {
// some code to retrieve an encrypted file from a secure storage, decrypt it and initialize the map out of it
}
It appears to be an issue with ClientHeadersFactory because if I inject my bean in my main class (#QuarkusMain), it works. I'm then able to assign the map to a public static map that I can then access from my HeaderHandler with Application.myPublicStaticMap but that's ugly so I would really prefer to avoid that.
I've searched online and saw several people having the same issue but according to this blogpost, or this one, it should work as of Quarkus 1.3 and MicroProfile 3.3 (RestClient 1.4) and I'm using Quarkus 1.5.2.
Even the example in the second link doesn't work for me with the injection of UriInfo so the issue doesn't come from the bean I'm trying to inject.
I've been struggling with this for weeks and I'd really like to get rid of my workaround now.
I'm probably just missing something but it's driving me crazy.
Thanks in advance for your help.
This issue has finally been solved in Quarkus 1.8.
I am trying to instantiate a resolver from spring-cloud-aws-messaging, specifically the NotificationMessageArgumentResolver. The problem is that it takes a MessageConvertor as an argument. So, this is what I have so far:
private NotificationMessageArgumentResolver notificationMessageArgumentResolver() {
new NotificationMessageArgumentResolver(this.messageConvertor);
}
To get the messageConvertor, I have tried:
#Autowired
public MvcConfig(MessageConvertor messageConvertor) {}
#Autowired
public MvcConfig(MappingJackson2MessageConverter messageConvertor) {}
but I get the same error either ways no bean found. The documentation is simply asking to use the XML:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<ref bean="notificationResolver" />
</mvc:argument-resolvers>
<aws-messaging:notification-argument-resolver id="notificationResolver" />
Which, according to the doc
registers three argument resolvers: NotificationStatusHandlerMethodArgumentResolver, NotificationMessageHandlerMethodArgumentResolver, and NotificationSubjectHandlerMethodArgumentResolver.
So, following the answer from How to use argument-resolvers using annotation in Spring boot?, I am able to get 2 of the 3 beans added, as they don't need any beans I cannot access, however I am not able to instantiate NotificationMessageArgumentResolver due to the lack of a MessageConvertor. I am expecting all my messages to come purely in JSON, so I do know exactly which MessageConvertor to use, which is the default one for JSON that ships with Spring Boot.
EDIT
The entire file, if anyone is interested: http://pastebin.com/tM471AEv
I wonder if you really need the NotificationMessageArgumentResolver as that is intended to be used when using messaging. As you can see it implements the HandlerMethodArgumentResolver from the org.springframework.messaging package.
I suspect that you want to use the NotificationMessageHandlerMethodArgumentResolver instead. Which is the HandlerMethodArgumentResolver for use with the web instead of messaging. Which is also registered when using <aws-messaging:notification-argument-resolver id="notificationResolver" />
I would also suggest to use the NotificationHandlerMethodArgumentResolverFactoryBean instead of 3 individual beans as that is also the class that is used internally by the namespace and annotation driven configuration.
Your configuration would look something like this.
#Bean
public NotificationHandlerMethodArgumentResolverFactoryBean notificationHandlerMethodArgumentResolverFactoryBean() {
return new NotificationHandlerMethodArgumentResolverFactoryBean();
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(notificationHandlerMethodArgumentResolverFactoryBean.getObject());
}
In some cases, we need to write to database in a Spring -application within an ApplicationListener, so we need transactions within the listener using #Transactional-annotation. These listeners are extended from an abstract baseclass, so normal ScopedProxyMode.INTERFACES won't do, as Spring container complains about expecting a bean of the abstract class-type, not "[$Proxy123]". However, using Scope(proxyMode=ScopedProxyMode.TARGET_CLASS), the listener receives the same event twice. We are using Spring version 3.1.3.RELEASE. (Edit: Still occurring with version 3.2.4.RELEASE)
Digging into Spring source with debugger, I found out that org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners returns a LinkedList that contains the same listener twice (same instance: [com.example.TestEventListenerImpl#3aa6d0a4, com.example.TestEventListenerImpl#3aa6d0a4]), if the listener is a ScopedProxyMode.TARGET_CLASS.
Now, I can work around this by placing the code handling database write into a separate class and putting the #Transactional there, but my question is, is this a bug in Spring or expected behavior? Are there any other workarounds so we wouldn't need to create separate service-classes (ie. handle the transaction in the listener, but don't get the same event twice) for even the simplest cases?
Below is a smallish example showing the problem.
With #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) in TestEventListenerImpl, the output is as follows:
Event com.example.TestEvent[source=Main] created by Main
Got event com.example.TestEvent[source=Main]
Got event com.example.TestEvent[source=Main]
With #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) removed from TestEventListenerImpl, the output is:
Event com.example.TestEvent[source=Main] created by Main
Got event com.example.TestEvent[source=Main]
So it seems that TARGET_CLASS -scoped beans get inserted twice into the listener list.
Example:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.example/**"/>
</beans>
com.example.TestEvent
public class TestEvent extends ApplicationEvent
{
public TestEvent(Object source)
{
super(source);
System.out.println("Event " + this + " created by " + source);
}
}
com.example.TestEventListener
public interface TestEventListener extends ApplicationListener<TestEvent>
{
#Override
public void onApplicationEvent(TestEvent event);
}
com.example.TestEventListenerImpl
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) //If commented out, the event won't be received twice
public class TestEventListenerImpl implements TestEventListener
{
#Override
public void onApplicationEvent(TestEvent event)
{
System.out.println("Got event " + event);
}
}
com.example.ListenerTest
public class ListenerTest
{
public static void main(String[] args)
{
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
SimpleApplicationEventMulticaster eventMulticaster = appContext.getBean(SimpleApplicationEventMulticaster.class);
//This is also needed for the bug to reproduce
TestEventListener listener = appContext.getBean(TestEventListener.class);
eventMulticaster.multicastEvent(new TestEvent("Main"));
}
}
I can't speak to if this is a bug or expected behavior, but here's the dirty:
Declaring a bean like
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) //If commented out, the event won't be received twice
public class TestEventListenerImpl implements TestEventListener
{
Creates two BeanDefinition instances:
A RootBeanDefinition describing the Scoped bean.
A ScannedGenericBeanDefinition describing the actual object.
The ApplicationContext will use these bean definitions to create two beans:
A ScopedProxyFactoryBean bean. This is a FactoryBean that wraps the TestEventListenerImpl object in a proxy.
A TestEventListenerImpl bean. The actual TestEventListenerImpl object.
Part of the initialization process is to register beans that implement the ApplicationListener interface. The TestEventListenerImpl bean is created eagerly (right away) and registered as an ApplicationListener.
The ScopedProxyFactoryBean is lazy, the bean (proxy) it's supposed to create is only generated when requested. When that happens, it also gets registered as an ApplicationListener. You only see this when you explicitly request it
TestEventListener listener = appContext.getBean(TestEventListener.class);
Or implicitly by using #Autowired to inject it into another bean. Note that the actual target object is added, not the proxy.
I am trying to follow the tutorial at:
http://pfelitti87.blogspot.be/2012/07/rest-services-with-spring-3-xml-json.html
Which basically is a tutorial on how to automatically convert your spring controller response to JSON\XML. The latter uses xstream.
And my configuration is exactly the same as his (besides the packages to scan obviously).
And my JSON url looks like I'd expect, however the .xml url does not return what I'd expect.
It starts with:
<?xml version="1.0"?>
<org.springframework.validation.BeanPropertyBindingResult><nestedPath/>
<nestedPathStack serialization="custom"><unserializable-parents/><vector>
<default><capacityIncrement>0</capacityIncrement><elementCount>0</elementCount>
<elementData><null/><null/><null/><null/><null/><null/><null/><null/><null/><null/></elementData></default></vector></nestedPathStack><objectName>coursesTakenForDealer</objectName>-<messageCodesResolver class="org.springframework.validation.DefaultMessageCodesResolver"><prefix/><formatter class="org.springframework.validation.DefaultMessageCodesResolver$Format">PREFIX_ERROR_CODE</formatter></messageCodesResolver>
And only contains the actual returned object towards the end.
Additionally it seems to ignore the #XmlAttribute and #XmlElement annotations.
I have a name instance variable on my Person class marked as #XmlAttribute yet it is converted as:
<person>
<name>Jack</name>
</person>
Any advice on how to resovle this\references to a better tutorial (perhaps using jaxb) would be much appreciated.
Edit:
As requested, part of my output (it does not completely fit in here). It is invoked using : http://localhost:8080/restTestApp/interestingPeople.xml
edit2:
The controller:
#Controller
public class InterestingPeopleController {
#RequestMapping(value="interestingPeople", method = GET)
public InterestingPeople getCoursesTakenByDealers() {
InterestingPeople interestingPeople;
//some logic (service calls etc) to fill it in
return interestingPeople;
}
}
edit 3:
I was able to resolve this by adding:
<property name="supportedClasses">
<list>
<value>com.jackdans.model.InterestingPeople</value>
</list>
</property>
to my XStreamMarshaller bean configuration.
I wanted to instrument a large number of classes to use with Spring Insight and instead of adding the #InsightOperation manually to the methods, I wrote an aspect to annotate the methods using point cuts.
However, this is not working. While the manual annotation affects the Spring Insight trace logging, the AspectJ method does not work.
Is there anything I am doing wrong here? (I decompiled the classes after aspectizing and do find the annotation in the class methods)
This is the aspect code snippet:
declare #method :public * com.example.IExample.execute(..) : #InsightOperation;
Spring documentation says this:
Use of the #Insight* annotations are
optional. They make it easy for end
users to define custom operation
frames and end points without needing
to create a plug-in. Because end user
code modification is required to use
the annotations, they are an option
for users who cannot or do not wish to
write aspects.
http://static.springsource.com/projects/tc-server/2.5/devedition/htmlsingle/devedition.html
So looks like the only way is to write a custom plugin
http://static.springsource.com/projects/tc-server/2.5/devedition/htmlsingle/devedition.html#tutorial-plugin
It is possible that the Insight LTW does not pick up your introduced annotations. I'll have to dig deeper on that.
In the meantime, you can try a more low-level annotation:
com.springsource.insight.collection.method.MethodOperationsCollected
If you look at the spring-core plugin, you will see that it does something similar:
public aspect RepositoryMethodOperationCollectionAspect {
declare #type: #Repository * : #MethodOperationsCollected;
}
An easy work around is to call another method from within your aspect method to continue executing the join point. I only tried calling a static method in a static class. See below my code for adding the #InsightOperation to all my JSON serialization.
My aspect:
#Aspect
public class JSONSerializerAspect {
#Around("call(* *.JSONSerializer.serialize(..)) && args(target)")
public Object serialize(ProceedingJoinPoint joinPoint, Object target) throws Throwable {
return JSONSerializationWrapper.serialize(joinPoint, target);
}
}
The static class it is calling:
public class JSONSerializationWrapper {
#InsightOperation(label = "JSON_SERIALIZATION")
public static Object serialize(ProceedingJoinPoint joinPoint, Object target) throws Throwable {
return joinPoint.proceed(new Object[]{target});
}
}
I'm using this myself and tested that it works.