How to test with mocks when using the Service Locator pattern? - java

Preface: My default mode of operation is using an IoC container and constructor injection. This makes testing with mocked dependencies trivial.
I am starting to develop an IntelliJ plugin and I want to make use of inversion of control. Since this is a plugin there isn't really an option of a container (right?) so I suppose I need to use a Service Locator pattern.
How do I test using mocks with the Service Locator pattern?
The best way that I can think of is to use an interface for my locator, set it in the default constructor of each service using a static getter, and have a setter so that I can set a mocked locator. It would look something like this:
public class MyService {
private IServiceLocator locator;
public MyService() {
setLocator(ServiceLocator.locator());
}
public void setLocator(IServiceLocator locator) {
this.locator = locator;
}
}
Now I can mock the IServiceLocator and set that on MyService in my test. I can then expect a call like locator.dependency1() and make it return a mocked dependency.
My main issue with this approach is the locator setter that is only there to support testing. Is there a better way?

First off I would suggest reading the excellent "Working Effectively With Legacy Code" which has a bunch of patterns for dealing with stuff like this. Although you're writing new code, you're bound by some of the same restrictions as legacy code.
For this, an easier way might be to provide a second protected constructor that accepts your dependencies explicitly and have the no-arg constructor use the defaults you want. So something like the following:
public class MyService {
private Dependency1 dep1;
private Dependency2 dep2;
protected MyService(Dependency1 dep1, Dependency2 dep2) {
this.dep1 = dep1;
this.dep2 = dep2;
}
public MyService() {
this(new ConcreteDependency1(), new ConcreteDependency2());
}
}
You can also add an annotation to make it clear this is for testing, i.e. Guava's #VisibleForTesting

You can do that in way you described. There are other options that comes into my mind:
You can use package-private access for service locator field
Now if you put your test class in the same package as service - it can directly manipulate fields inside tested class - so it is easy to mock service locator.
You can use features of testing/mocking library to inject service locator
For example Mockito (https://code.google.com/p/mockito/) can inject private fields int two ways:
Whitebox class - but this is not best idea since you have to know name of field that you want to mock (change of field name will break test), and you need to execute Withebox.setInternalState method manualy
#InjectMocks annotation - You create service locator field annotated with #Mock annotation, and service field annotated with #InjectMocks and mockito will use service locator field and injects it into service automatically.
You can use dependency injection mechanism that not depends on containers
For example check Google Guice library: https://code.google.com/p/google-guice/
You can use "manually" dependency injection
DI is pattern - DI containers/frameworks makes this pattern easy to implement, but you can always do it yourself with factory classes (but it takes more time). Here is nice talk about DI that covers "manual" DI: https://www.youtube.com/watch?v=RlfLCWKxHJ0
It is difficult to say which option is best for you. If you want to stick with service locator - I'd recomend Mockito. If you want DI - I'd recommend Google Guice. Personaly, I think that service locator is more like anti-pattern.

Related

How to inject mock for only one test case with Quarkus/RestAssured

I'm attempting to test a REST controller (using Quarkus) endpoint using rest assured. I want to mock a class that is injected into that controller (ideally with Mockio), but only for one of my tests. Or get different behaviour per test case without having to have separate classes for each test. I'm not sure how to do this?
I've seen doing it the way from the documentation:
#Mock
#ApplicationScoped
public class MockExternalService extends ExternalService {
#Override
public String service() {
return "mock";
}
}
But this would only allow me to use one mock for all tests and not mock certain behaviours based on tests as I would with Mockito. I think?
I've tried creating a mock and annotating it with #Mock
#Mock
public TableExtractorService tableExtractorServiceMock = Mockito.mock(TableExtractorService.class);;
but I still get my real implementation when I use it. I'm using a constructor annotated with #Inject in my Controller that takes the TableExtractorService.
For a bit more information my test using restassured looks like this:
InputPart filePart = Mockito.mock(InputPart.class);
Mockito.when(tableExtractorServiceMock.Extract(anyObject()))
.thenThrow(IOException.class);
final InputStream inputStream = filePart.getBody(InputStream.class, null);
given()
.multiPart("file", inputStream)
.when().post("/document")
.then()
.statusCode(500);
That endpoint calls the service class that I'm trying to mock, and I want that mock to return an exception.
It can't be done. Quarkus documentation explains the issue:-
Although this mechanism is fairly straightforward to use, it nonetheless suffers from a few problems:
A new class (or a new CDI producer method) needs to be used for each bean type that requires a mock. In a large application where a lot of mocks are needed, the amount of boilerplate code increases unacceptably.
There is no way for a mock to be used for certain tests only. This is due to the fact that beans that are annotated with #Mock are normal CDI beans (and are therefore used throughout the application). Depending on what needs to be tested, this can be very problematic.
There is a no out of the box integration with Mockito, which is the de-facto standard for mocking in Java applications. Users can certainly use Mockito (most commonly by using a CDI producer method), but there is boilerplate code involved.
Link for reference: https://quarkus.io/blog/mocking/
According Quarkus test documentation, you can do it usingo #QuarkusMock or #InjectMock.
As #Ankush said, a class annotated with the #Mock annotation is using the CDI #Alternative mechanism, and will be global. #QuarkusTestProfiles can be used to define CDI #Alternatives for groups of tests.
For example, instead of annotating the mock with #Mock, it could be referenced in a test profile as
default Set<Class<?>> getEnabledAlternatives() {
return Set.of(MyMockThing.class);
}
Any test annotatated with the
#TestProfile(MyMockyTestProfile.class)
profile would get those mocks, while others would use the original implementation.
What may be a simpler method is to just use #InjectMock. For example, in the test class, declaring a field like this:
#InjectMock
MyThing mock;
will ensure that mock is used by the classes under test, just for this test.
For rest clients, it will also be necessary to add a #RestClient annotation, and if the original implementation is a singleton, convertscopes can be used to coax the scopes into something mockable.
#RestClient
#InjectMock(convertScopes = true)
MyThing mock;
Behaviour can be added to the injected mock in #BeforeEach or #BeforeAll methods. For example
#BeforeEach
public void setup() {
when(mock.someMethod()).thenReturn("some value");
}

Using hystrix-javanica annotations on interfaces

I am integrating Hystrix into SilverWare microservices platform and I want to use hystrix-javanica annotations. These annotations are meant to be applied on the actual implementation of the methods that need to be executed using Hystrix. The problem is that I need to implement a generic solution where you only have a service interface. It has to be done this way in order to let developers use annotated references to other microservices (when they implement their own service) without any need to deal with the implementation of those services.
I came up with a solution where you annotate your microservice reference with something like this:
#Inject
#MicroserviceReference
#HystrixConfiguration(MyServiceHystrix.class)
private MyService myService;
And then you implement (or extend) the service interface and put Hystrix annotations on its methods:
public interface HystrixedMyService extends MyService {
#HystrixCommand
doSomething();
}
When there is #HystrixConfiguration annotation on a field in your microservice referencing another service, SilverWare will scan the class given as a parameter of this annotation and prepare a Hystrix command for every method of the service. The command will also receive a callable with an actual method invocation which will be executed in its run() method.
My question is: Is it possible to reuse some (internal) parts of hystrix-javanica so I do not need to scan all the annotations and create those Hystrix commands myself? I can see that most of the classes are designed to be used only with AOP.

Scalamock testing Java dependency injection

I use Scalamock for unit testing my Java project.
Project contains classes that use Java CDI style DI:
class Resource {
#Inject
private Service service;
.....
}
How can I inject mock as a Service instance?
With Mockito I'm able to use #InjectMocks, but I definitely want to use Scalamock.
Currently ScalaMock does not support Mockito's #InjectMocks equivalent.
In case of setter/constructor injection you can inject your mocks manually. In case of field injection you could access the private fields using reflection, but that would be very cumbersome.
Please create new ScalaMock feature request (https://github.com/paulbutcher/ScalaMock/issues) - if it gets upvoted we can implement it.

Does using annotations to inject dependencies remove the main benefit of dependency injection(external configuration)?

I am using Spring, here is a controller:
#Controller
public class PersonController {
#Resource(name="PersonService")
private PersonService personService;
#RequestMapping(value = "/Person", method = RequestMethod.GET)
public String getPersons(Model model) {
// Retrieve all persons by delegating the call to PersonService
List<Person> persons = personService.getAll();
// Attach persons to the Model
model.addAttribute("persons", persons);
//then return to view jsp
}
and here is a service :
#Service("personService")
#Transactional
public class PersonService {
public List<Person> getAll() {
//do whatever
}
}
However, to properly make use of DI I should change the controller to make use of an interface (?) like so:
#Controller
public class PersonController {
#Resource(name="personService")
private IPersonService personService; //Now an interface
}
This would then allow me, for example, to use two services one test and one live. Which I could alter by adding/removing the annotation on the services :
#Service("personService") // this line would be added/removed
#Transactional
public class LivePersonService implements IPersonService {
public List<Person> getAll() {
//do whatever
}
}
and
#Service("personService") //this line would be added/removed
#Transactional
public class TestPersonService implements IPersonService {
public List<Person> getAll() {
//do something else
}
}
However one of the main benefits is lost due to the fact that the code has to be recompiled ? Whereas if I used xml lookup I could alter the dependency on-the-fly ?
The configuration is still external, because it is outside where you define which implementation is going to be injected. Inside the class, you just hardcode the "name" of something the class depends on (which is ok, because this dependency is inherent to the class).
This said, you can use XML to override the annotations of your code for the tests execution (you would have a specific XML application context for your tests) and specify which implementation you will inject.
Therefore, you don't need to change your code to run the tests. Take a look to this answer.
Well that's correct. Annotations are configuration inside the source code. Mainly intended when you have one class for each service. If you have more than one implementation for a particular interface, then XML will be a better option. Also you can mix XML configutation with annotations.
The conventional way, that I heard last time from DI camp, is that in unit tests, you shouldn't use the DI framework. Rather, simply instantiate mock service yourself and set it to the host object
test()
PersonController contr = new PersonController();
contr.personService = new TestPersonService();
// testing contr
This was hailed as the first and major achievement of DI, much to the puzzlement of people (like me) who don't get it. See my previous criticisms: advantage of using applicationcontext.getbean vs #configurable
If the DI supporters in this thread reflect the new trend in DI camp, they no longer do unit tests that way; instead tests depend on DI too, with a test specific DI config. Then it's really no different from service locator pattern. If the major feature of DI is moot, then what's the point?
Your controller class perfectly illustrated that. It cannot be used outside Spring DI framework as a POJO. There's nothing POJO about it. And nobody cares, rightfully. Same thing if your class depends on a service locator framework.
There are other features provided by Spring beans framework, none of them depends on DI; they can be implemented in a service locator framework just as well. Many people when defending DI the design pattern, are actually defending Spring the entire stack. You can actually use Spring as a service locator framework; Spring will not advertise this now, it's a blow to its main hype point; but it will once the hype weakens and it must appeal to the doubters.

Mock object and Spring annotations

I am using Spring annotations in my code to do the DI. So lets say I have a class class1 that depends on another class class2, I define class1 as below:
#Component
public class class1 {
#Resource
private interface2 object2;
}
class2 is an implementation of interface2.
Now lets say I want to mock class2 and pass it to class1, I dont see any constructor or setter in class1. I think Spring uses reflection to inject object2. How can I mock it? Should I add a setter in class1? Or can I reuse the same way spring is doing it - I mean does spring itself have a mock object framework or something, I was planning to use EasyMock for the mocking.
Thanks
The ReflectionTestUtils class in Spring might be helpful.
It seems to do what you are looking for...at least the injection part :-)
Mockito has a really powerful way of handling mocks and DI:
#RunWith(MockitoJUnitRunner.class)
public class Class1Test {
#Mock
private Interface2 inteface2mock;
#InjectMocks
private Class1 class1;
#Test
public void someTest() {
when(interface2mock.doSomething("arg")).thenReturn("result");
String actual = class1.doSomeThatDelegatesToInterface2();
assertEquals("result", actual);
}
}
Read more about #InjectMocks in the Mockito javadoc or in a blog post that I wrote about the topic some time ago.
Available as of Mockito 1.8.3, enhanced in 1.9.0.
ReflectionTestUtils is the easiest to add the mock you want (we use JMock, but it does not really matter), drawback is that it is slightly brittle. If you rename the field, you must remember to change the test as well.
You can also use this: http://i-proving.com/2006/11/09/using-jmock-and-inject-object/
It describes how to use a mocked object in a spring context.
AFAIK, there is no mocking framework built into Spring, so you need to use something like EasyMock. The way I have done this in the past is to
Define the Spring config using XML instead of annotations*
The config for the main app is defined in appContext-main.xml and the test config (the mock objects) is defined in appContext-test.xml
A mock bean in appContext-test.xml must have the same ID as the corresponding bean in appContext-main.xml
When the app runs only appContext-main.xml is loaded
When the tests run both appContext-main.xml and appContext-test.xml are loaded. Make sure that they are loaded in this order so that the mocks 'override' any beans of the same name
* You don't need to need to convert all your Spring configuration to XML to use this approach. Only those beans that have mock implementations or have mock implementations injected into them need to be changed. The others bean can continue to be defined with annotations.
Injecting yourself via reflection is very easy, so you can avoid the setter method.
To do it yourself is like this:
for (Field field : injectable.getClass().getDeclaredFields()) {
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
if (annotation != null) {
field.setAccessible(true);
Object param = generateMockObject();
field.set(injectable, param);
}
}
There is a JUnit Rule that gives EasyMock similar annotation-driven injection capabilities, in the manner of Mockito. See EasyMockRule

Categories

Resources