SpringBoot - Mock stateful object created via "new" keyword in integration test - java

I have an SpringBoot application that consists of a Controller layer and a Service layer.
MyController has access to MyService via #Autowired, while MyService has a method that creates a new instance of MyClass, which is imported from an external dependency.
import externaldependency.MyClass;
#Service
public class MyService {
public void myMethod() {
MyClass c = new MyClass();
c.doStuff();
c.doOtherStuff();
c.doMoreStuff();
}
}
I use new to create the instance because MyClass holds state; it has several methods that change its state during the execution of myMethod until I get the desired result, therefore I shouldn't autowire it nor inject it in the constructor, since that would use a single instance of this class for every call to myMethod. I understand that "Prototype" beans exists, but as far as I know, even if I declare MyClass as a prototype bean and inject it to MyService via #Autowired, the service would still use the same instance of MyClass during execution, so ultimately I decided to just use new.
Recently I've been trying to do an integration test, calling my Controller layer, which in turn will call my Service layer, which in turn will create an instance of MyClass. The problem is that one of the many methods of MyClass internally calls an external service, which shouldn't be part of the test itself, so I would like to mock this class.
I understand that mocking is done via dependency injection, but in this case I can't do that. Is there an alternative way to mock MyClass, or is it simply not possible with this setup? If not, then how could I refactor my code to make mocking possible in this particular case?
Many thanks in advance.

I'll answer my own question.
Since MyClass holds state, it shouldn't be autowired to the service nor injected via its constructor, but rather new instances should be created as needed. However, whan can be autowired is a "factory" which creates these instances:
#Component
class MyClassFactory {
public MyClass getInstance() {
return new MyClass();
}
}
Therefore, the service becomes:
#Service
public class MyService {
#Autowired
private MyClassFactory myClassFactory;
public void myMethod() {
// MyClass c = new MyClass();
MyClass c = myClassFactory.getInstance();
c.doStuff();
c.doOtherStuff();
c.doMoreStuff();
}
}
In practice, using a factory is the same thing as just using new; I'm getting a new instance either way. The benefit comes during testing; now I can mock what the factory returns, since the factory is part of Spring's application context:
#SpringBootTest
public class MyTest {
#MockBean
private MyClass myClassMock;
#MockBean
private MyClassFactory myClassFactoryMock;
#Test
public void myTests() {
// Make a mock of MyClass, replacing the return
// values of its methods as needed.
given(
myClassMock.doStuff()
).willReturn(
"Something useful for testing"
);
// Then make a mock of the factory, so that it returns
// the mock of the class instead of a real instance.
given(
myClassFactoryMock.getInstance()
).willReturn(
myClassMock
);
// Do the tests as normal.
}
}
Probably not the most elegant solution, but at least solved my current problem.

Related

How does bean self-auto-wiring work in Spring?

One of the really cool and rather mind boggling features that I have recently discovered in Spring is self auto-wiring a bean.
What I am referring to is this:
class UserServiceImpl implements UserService {
#Autowired
UserService service;
// other service methods...
}
My questions are as follows:
How is this implemented ?
How does Spring manage this ? Does it assign the same object to self auto-wire reference ? Like so:
UserServiceImpl serviceImpl = new UserServiceImpl();
serviceImpl.setService(serviceImpl); // Obviously this would be done via Reflection rather than a setter.
or
does Spring make 2 seperate objects? Like so :
UserServiceImpl obj1 = new UserServiceImpl();
UserServiceImpl obj2 = new UserServiceImpl();
obj1.setService(obj2);
And just gives us obj1 when we request it in a RestController ?
How many copies of the object are there in the Spring Application Context ?
Related to the previous question, how many actual copies of the object are there ?
Its a very convenient feature for things like transactions across methods, but I want to know what is exactly going on behind the scenes here.
It doesn't inject the UserServiceImpl if you use some annotation like #Transactional,#Cachable etc. It actually injects the proxy.
But as the base it uses the same instance.
As of 4.3, #Autowired also considers self references for injection (that is, references back to the bean that is currently injected). Note that self injection is a fallback. Regular dependencies on other components always have precedence. In that sense, self references do not participate in regular candidate selection and are therefore in particular never primary. On the contrary, they always end up as lowest precedence. In practice, you should use self references as a last resort only (for example, for calling other methods on the same instance through the bean’s transactional proxy). Consider factoring out the affected methods to a separate delegate bean in such a scenario. Alternatively, you can use #Resource, which may obtain a proxy back to the current bean by its unique name.
There is only one copy and uses reflection
Lets look at this sample
#Service
public class SampleService {
#Autowired
private SampleService service;
public SampleService getService() {
return service;
}
}
#SpringBootApplication
public class RestServiceApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context
= SpringApplication.run(RestServiceApplication.class, args);
SampleService sampleService = context.getBean(SampleService.class);
//This will print true
System.out.println(sampleService == sampleService.getService());
}
}
As you can see sampleService == sampleService.getService() is true;

Mockito does not recognize class

I tried to user weld-junit5 with mocking a class.
I mock the class because i want to know how often it will be called.
But each time i try to Mockito.verify() this mocked class it throws a "NotAMockException"
The Intellij Debugger is validating the field as: "Mock for MessageService, hashCode: XY"
I already tried to add my testclass into the WeldInitiator but it dont want to work.
"MessageService" is a real class, not an interface (a Interface wouldn't work either)
Docs
#EnableWeld
class GameServiceTest {
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
/** Some More **/,
GameServiceTest.class).build();
#Produces
#ApplicationScoped
public MessageService createMessageServiceMock() {
return mock(MessageService.class);
}
#Inject
private MessageService messageService;
#Inject
private GameService gameService;
#Test
void handleRunningGames() {
this.gameService.handleRunningGames(null, mock(Session.class));
// This will throw a org.mockito.exceptions.misusing.NotAMockException
Mockito.verify(messageService, Mockito.times(1)).writeMessage(any(), any());
}
}
I expect, that the Injected MessageService is a real mock, on which i can call every Mockito function, but it seems not so.
Do I anything wrong, or what is the proper way to do this?
I think I just resolved this problem:
private static final MessageService messageService = mock(MessageService.class);
#Produces
#ApplicationScoped
public MessageService createMessageServiceMock() {
return messageService;
}
To give some background, the way it works is that Weld lets Mockito create the desired object and then takes that as a contextual bean instance.
However, in CDI any bean that has normal scoped needs to have a proxy that is passed around instead of that instance. So what your producer actually does (because it is #ApplicationScoped) is create the object, store that in the context and then also create a proxy and pass around that proxy instead. The proxy is a different object (a delegate with no state) that "knows" how to get reference to the actual instance.
So what happens is that proxy gets injected into the field and you are checking the proxy object with Mockito.verify() call. Obviously, the proxy isn't the mock itself and so it fails. As user #second suggested, Weld offers an API to unwrap the proxy and get the contextual instance. I would argue that the API isn't "ugly", it is just a thing users shouldn't mostly care about but sometimes you cannot avoid it.
You could avoid having proxy by using some of the pseudo scopes which are #Dependent or CDI #Singleton. With that it should work as well and so long as it's for tests, replacing application scoped with singleton should work.
As for your workaround, I do no see how that solves anything - it is basically the same producer and because of the scope it will only be invoked once, hence the static field will make no difference (as there will be a singular call to mock creation). Have you changed something else than that?
As a user of JUnit 5 + Weld-JUnit myself, I am using the following pattern. The reasons are those explained in the answer by Siliarus.
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import ...
#EnableWeld
#ExtendWith(MockitoExtension.class) // (1)
class GameServiceTest {
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
/** Some More **/,
GameServiceTest.class).build();
#Produces
#ApplicationScoped // (2)
#Mock // (3)
private MessageService messageServiceMock;
// #Inject // (4)
// private MessageService messageService;
#Inject
private GameService gameService;
#Test
void handleRunningGames() {
this.gameService.handleRunningGames(null, mock(Session.class));
// (5)
Mockito.verify(messageServiceMock, Mockito.times(1)).writeMessage(any(), any());
}
}
Explanations:
I am using the Mockito extension for convenience
You do not really need to give it application scope
The #Mock annotation is handled by the MockitoExtension. Again, this is only for convenience, you could create the mock yourself in a producer method.
You do NOT need to inject the mock service; you have the messageServiceMock! The injected thing here will be the Weld proxy, as explained by Siliarus.
It is fun to describe this injection a bit more: If the bean is #ApplicationScoped, i.e. "normal-scoped", CDI has to inject a proxy. If you use this proxy instead of the actual mock, you will get the exception. If you follow my advice from (2) and leave out the #ApplicationScoped, the bean will be dependent-scoped and the mock is injected directly. In this case you could use the injected field, but why bother?
Use the mock directly.

Spring #Autowired on a class new instance

I'm not so familiar with Spring and I have the following situation:
A repository class:
#Repository
public class MyRepository {
// ...
}
A class that uses the repository class:
public class MyClass extends AbstractClass {
#Autowired
private MyRepository myRepository;
//...
}
I know that if I annotate my MyClass with #Component and use it with an #Autowired, then the #Autowired MyRepository is resolved just fine.
Problem is I am in a situation that I need to create new instances of MyClass with reflection. So MyRepository is never resolved and is null all the time.
Is there a way to use #Autowired in this situation?
Explaining better my situation:
I have some implementations of AbstractClass.
In a setup phase of my application I create a HashMap of these implementations. Basically:
{"MyClass", MyClass.class}
//...
Then I have a generic Controller that maps to the url /{class}?options=...
Using the {class} #PathVariable, the HashMap above and reflection I am able to create a instance of a class based on the given options (this part is important). Do you guys think there's a better way of doing this?
Thanks in advance
Spring itself offers some functionality for doing auto-wiring in your objects
which you created by new or newInstance() or whatever.
To use it you need an AutowireCapableBeanFactory
which you get by Spring's normal dependency injection with #Autowired.
#Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
Then you use its autowireBean(Object) method
to inject the #Autowired properties into your bean.
Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);
Design note:
Think well if you really need the approach above.
The javadoc of AutowireCapableBeanFactory advises against using this interface for most use-cases:
This subinterface of BeanFactory is not meant to be used in normal application code: stick to BeanFactory or ListableBeanFactory for typical use cases.
Integration code for other frameworks can leverage this interface to wire and populate existing bean instances that Spring does not control the lifecycle of. This is particularly useful for WebWork Actions and Tapestry Page objects, for example.
You can use Factory Design Pattern over here.
This might seem a little complicated in start but I am sure you will love it after you have implemented it.
Steps:
Add #Component on all implementations of AbstractClass.
Create a factory class as:
#Component
public class MyFactory {
private final Map<String, AbstractClass> impletationMap = new HashMap<>();
#Autowired
ApplicationContext context;
#PostConstruct
public void initialize() {
populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
}
private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
while (classIterator.hasNext()) {
AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
}
}
}
When the Bean of this MyFactory class is initialized, then it will lookup for all beans of type AbstractClass and put them in the HashMap(implementationMap).
Now from this factory you can get the HashMap and then get the implementations as and when you require. It will be very easy when you add new implementation of AbstractClass as factory will take care of it.
One work around is instead of binding the MyClass to the Hashmap to bind a Factory class. MyClassFactory. This way you will delegate the construction to a concrete factory that will do the job to instantiate the correct class and initialize the correct repository.
Here is an example:
{"MyClass", MyClassFactory.class}
The factory can be Component as well, then you need to bind the hashmap to the factory instance instead of the factory class. But lets say it is not a component:
//#Component this is optional
public MyClassFactory {
//#Autowired optional
ApplicationContext ctx;
public MyClass createInstance() {
MyRepository repo = ctx.getBean("")
MyClass myclass = new MyClass(repo)
return myclass;
}
}
If you mark it as component you can well also use ApplicationContextAware interface if you are going to autowire the ApplicationContext.
One approach is to declare #Component on top of MyClass.
Then, in the setup phase, you can pass the instance of MyClass instead of MyClass.class itself, in the HashMap. There won't be any need to create instances via reflection.
Note: You can fetch the instance of MyClass from your ApplicationContext in the setup phase.
Try this
#Component
public class SomeClass extends AbstractClass {
private static ApplicationContext applicationContext;
public MyClass getMyClass(){
// Now #Autowired MyRepository will work
return applicationContext.getBean(MyClass.class);
}
}
Yes, you can annotate all your AbstractClass implementation beans with #Component and use the next declaration
#Autowired
private List<AbstractClass> beans;
You can then convert that to a Map in a #PostConstruct method.
Spring won't complain about duplicate definitions if you autowire Lists.

How to autowire object to bean if class does not have setter

I have so controller
#Controller
public class MyController {
#Autowire
MyClass myClass;
//myClass doesn't have setter and getter
....
#RequestMapping("/path")
public String underTest(){
myClass.makeSomething();
return "html.jsp"
}
I want make mock test using Mockito and mock myClass.
In test class I want get myClass so:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/BeanConfig.xml");
myClass = context.getBean("myClass ", MyClass .class);
But I need autowire this bean to Controller for testing controller's method(I think test code should not affect to normal code).
There are exist way to make it without writing of set method?
I want to check that myClass.makeSomething() invokes once in method underTest.
As long as your test for MyController resides in the same package as MyController itself (as it's usually done - same packages in different source folders), you can simply assign it:
MyController controller = new MyController();
controller.myClass = mockMyClass;
That's the reason not to put #Inject/#Autowired on private fields.
I'm not sure I agree with you that test code should not affect normal code. I think an entirely valid reason to refactor / rewrite production code is to make it more testable (this is probably achieved by making it more modular, which is generally a good thing anyway).
This is precisely why annotations like
#VisibleForTesting
exist. Then you can create a package-local setter for MyClass, add the above annotation (for information to other programmers and possibly code inspection tools) and set the field in your test (which should reside in the same package).
Alternatively, since you are using Mockito, you could simply annotate the MyController instance with #InjectMocks, eg
#Test
public class MyControllerTest {
#Mock
private MyClass mockMyClass;
#InjectMocks
private MyController myController;
#BeforeMethod
public void before() {
MockitoAnnotations.initMocks(this);
}
// do tests...
}
Note that #InjectMocks does not depend on any annotations on the target field (i.e. #Autowired, #Resource, #Inject etc). It just works. (Presumably you will still need those annotations for Spring injection, so don't remove them! The point is you can also use it for fields that aren't annotated).
Note also that, depending on which version of Mockito you are using, you may need to instantiate the MyController in the before() method before calling MockitoAnnotations.initMocks()
Try testing the controller directly with context.getBean(). MyClass will be autowired into it.
I agree with #axtavt's answer, however if you absolutely want to go your way with injecting the mock in an integration test, you can do this:
define a overriding bean configuration file, say bean-test-config.xml, with content along these lines:
<import resource="classpath:spring/BeanConfig.xml"/>
<bean name="myClass" factory-method="mock" class="org.mockito.Mockito">
<constructor-arg value="MyClass"></constructor-arg>
</bean>
This should correctly inject in a mock in your controller. You will have to get hold of this mock in your test and inject in any behavior that you are expecting from this mock though.

Autowiring vs instantiating in Spring

I've started to use Spring recently. And I'm making spring mvc project. So my question is if it's preferred to make interfaces and autowire it with particular implementation by spring or just use class instances in case when I have only one implementation of that interface?
For example:
#Controller
public class MyController {
#Autowired
MyService myService;
#RequestMap("/")
public String mainPage() {
...
}
}
or
#Controller
public class MyController {
#RequestMap("/")
public String mainPage() {
MyService myService = new MyServiceImpl();
...
}
}
if there is only one implementation of MyService interface?
In most cases you should go with injection because:
It eases unit testing (you can inject mock or different implementation)
Spring can inject some dependencies into MyServiceImpl as well because it manages this object
You are not coupling your controller with particular implementation
Even if your service does not have an interface, because of the second reason you should consider injection.
The only case when you might want to skip Spring is when the class does not have any dependencies and is stateless. But most likely such a class is a utility that does not need any an instance at all because it has only static members.
It will depend on whether MyService is a bean that holds a state or not. If MyService does not hold state, then you don't need to create new instances and you can let Spring to inject it having the advantages above described

Categories