In Java, how can I mock a service loaded using ServiceLoader? - java

I have a legacy Java application that has code something like this
ServiceLoader.load(SomeInterface.class)
and I want to provide a mock implementation of SomeInterface for this code to use. I use the mockito mocking framework.
Unfortunately I am unable to change the legacy code, and I do not wish to add anything statically (eg. adding things to META-INF).
Is there an easy way to do this from within the test, ie. at runtime of the test?

You can use PowerMockito along with Mockito to mock static methods:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ServiceLoader.class)
public class PowerMockingStaticTest
{
#Mock
private ServiceLoader mockServiceLoader;
#Before
public void setUp()
{
PowerMockito.mockStatic(ServiceLoader.class);
Mockito.when(ServiceLoader.load(Mockito.any(Class.class))).thenReturn(mockServiceLoader);
}
#Test
public void test()
{
Assert.assertEquals(mockServiceLoader, ServiceLoader.load(Object.class));
}
}

From the ServiceLoader.load documentation:
Creates a new service loader for the given service type, using the
current thread's context class loader.
So you could use a special context class loader during test runs that will dynamically generate provider-configuration files in META-INF/service. The context class loader will be used for searching for provider-configuration files due to this note in the ServiceLoader documentation:
If the class path of a class loader that is used for provider loading
includes remote network URLs then those URLs will be dereferenced in
the process of searching for provider-configuration files.
The context class loader needs to also load a mock implementation of the service class, which is then passed as the mock implementation.
Such a context class loader would need to do two things:
dynamically generating the provider configuration files on request
per getResource* methods
dynamically generate a class (for example
using ASM library) on request per loadClass methods, if it is the
class that was specified in the dynamically generated provider
configuration file
Using above approach, you don't need to change existing code.

Move the call into a protected method and override it in the test. This allows you to return anything during the tests.

Services can usually be replaced at runtime.
If you are using OSGi you can replace the service implementation in a set up method annotated with #BeforeClass and unregister the mocked implementation in an #AfterClass method:
private ServiceRegistration m_registration;
#BeforeClass
public void setUp() {
SomeInterface mockedService = Mockito.mock(SomeInterface.class);
m_registration = registerService(Activator.getDefault().getBundle(), Integer.MAX_VALUE, SomeInterface.class, mockedService);
}
#AfterClass
public void tearDown() {
if (m_registration != null) {
unregisterService(m_registration);
}
}
public static ServiceRegistration registerService(Bundle bundle, int ranking, Class<? extends IService> serviceInterface, Object service) {
Hashtable<String, Object> initParams = new Hashtable<String, Object>();
initParams.put(Constants.SERVICE_RANKING, ranking);
return bundle.getBundleContext().registerService(serviceInterface.getName(), service, initParams);
}
public static void unregisterService(ServiceRegistration registration) {
registration.unregister();
}

Related

Java private vs default method protection

Java Question: I am working on a class (call it ProcessorA that only extends Object. It is also stateless). It will reside in a Spring Service on a Web Server. The class declares several public methods as the class' API.
I want to test this class with a simple JUnit test. I need to test some functionality that is a few method calls deep inside of 1 public method. However, between the API Method and the method to test there are several classes would be loaded at runtime by Spring in the Web Server.
I can completely by-pass this by declaring the method to be tested as a 'default' method and calling it directly from an instance (of ProcessorA) from the JUnit test.
I have been told that this is NOT a best practice.
However, I am at a loss as to exactly what is gained by further restricting access to the method to be tested.
So, what is it that can be gained by declaring a method as private over default (which is more restrictive than "protected" (which by inclusion is also verboten). \
public class ProcessorA {
public methodA(String input) throws ValidationException {
doSomeValidationStuff(input);
doStuffToTest(input);
}
private doSomeValidationStuff(String input) throws ValidationException {
//Libraries that are not loaded at execution and not available for the JUnit test
}
doStuffToTest(String input) {
//Code to be tested}
}
}
class MyJunitTest {
#Test
void doStuffToTestTest() {
ProcessorA processorA = new ProcessorA();
String testData = "test data String";
assertNotNull( processorA.doStuffToTest(testDate));
}
}
The answer that I am looking for isn't for how to get around this constraint, but what is gained by blindly following a blanket directive that has (seemingly) no payoff.
Default scope in Java is a package-private scope which means that all classes from the same package can use this method. If you restrict it to private, only methods from the same class can do it. This is what we gain. It is your decision, do you need to expose this method to other classes or it is enough to just keep it private but don't expose something only for testing purposes.

Creating a configurable JUnit library to test same features across several microservices

A set of tests should be run on every microservice. Current solution is to have an abstract class and extend in every service, providing the necessary properties in abstract getters.
public abstract class AbstractTest {
#LocalServerPort
protected int serverPort;
protected abstract String getPath();
#Test
void someTest() {}
#Test
void conditionalTest() {}
}
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
classes = {...})
#ActiveProfiles(...) // etc
public class MyTest extends AbstractTest {
// ... implement getPath()
// tests from parent will be executed
}
The goal:
Ditch inheritance and have the AbstractTest's logic get executed automatically with conditional #Test execution based on beans/properties etc.
The possible solution:
A concrete class with all the tests or some sort of Configuration/TestFactory to create the necessary tests. It should take into account available properties and beans to determine which tests to run.
The problem:
How can those tests (created in runtime) be discovered and registered for execution?
How to inject all the properties that are part of the current context of the #SpringBootTest?
Failed attempts:
TestInstanceFactory extension doesn't seem to be the solution as it requires an instance of the class which it annotates.
Using the Launcher API seems overkill, and also doesn't seem to work, since the library class won't be created with the Spring context configs.
using cglib and a base class Spring Contract-style is not a desirable solution
Ideally I don't want the client of this lib to implement/create anything, so abstract String getPath(); would be a test.lib.path property, and if it's present, a test from the library which uses it will run.
Any thoughts on this would be great, because right now this just seems impossible to me.
What is the reason to have the inheritance for tests?
In case you need to share some common logic within the tests you may try JUnit features (custom rules/extensions), for example
For junit < 5.x.x #Rule functionality https://junit.org/junit4/javadoc/4.12/org/junit/rules/TemporaryFolder.html https://stackoverflow.com/a/34608174/6916890
For junit >= 5.x.x (jupiter) there is an extension API
https://junit.org/junit5/docs/current/user-guide/#writing-tests-built-in-extensions-TempDirectory

Testing Jersey app, Injecting classes using Jersey Injection built-in framework (HK2)

I need to create tests for some class. This class in main project (src/main/java/..) is injected easily into another classes, since I have custom ResourceConfig class which declares which packages have to be scanned to seek for service classes.
Now I created test directories (in src/test/java/..) and created a class, something like:
public class TheMentionedClassIntegrationTest {
#Inject
private TheMentionedClass theMentionedClass ;
#Test
public void testProcessMethod() {
assertNotNull(theMentionedClass);
}
}
But the problem is that whatever I do the class is always null. In another tests in the project I was using JerseyTest class. So I tried to do the same here, extend TheMentionedClassIntegrationTest with JerseyTest, override configure method, create my private ResourceConfig class which registers Binder (default for whole project) and register TheMentionedClassIntegrationTest as well.
It didnt work. I did many different attempts but none of them were successfull. I think working with HK2 is extremly difficult, there is no good documentation or so..
Do you guys have an idea how to inject TheMentionedClass into the test class? Maybe my approach is wrong?
Thanks!
The easiest thing to do is to just create the ServiceLocator and use it to inject the test class, as see here. For example
public class TheMentionedClassIntegrationTest {
#Inject
private TheMentionedClass theMentionedClass;
#Before
public void setUp() {
ServiceLocator locator = ServiceLocatorUtilities.bind(new YourBinder());
locator.inject(this);
}
#Test
public void testProcessMethod() {
assertNotNull(theMentionedClass);
}
}
You could alternatively use (make) a JUnit runner, as seen here.
For some other ideas, you might want to check out the tests for the hk2-testing, and all of its containing projects for some use case examples.

How do I substitute Guice modules with fake test modules for unit tests?

Here's how we are using Guice in a new application:
public class ObjectFactory {
private static final ObjectFactory instance = new ObjectFactory();
private final Injector injector;
private ObjectFactory() throws RuntimeException {
this.injector = Guice.createInjector(new Module1());
}
public static final ObjectFactory getInstance() {
return instance;
}
public TaskExecutor getTaskExecutor() {
return injector.getInstance(TaskExecutor.class);
}
}
Module1 defines how the TaskExecutor needs to be constructed.
In the code we use ObjectFactory.getInstance().getTaskExecutor() to obtain and the instance of TaskExecutor.
In unit tests we want to be able to replace this with a FakeTaskExecutor essentially we want to get an instance of FakeTaskExecutor when ObjectFactory.getInstance().getTaskExecutor() is called.
I was thinking of implementing a FakeModule which would be used by the injector instead of the Module1.
In Spring, we would just use the #Autowired annotation and then define separate beans for Test and Production code and run our tests with the Spring4JunitRunner; we're trying to do something similar with Guice.
Okay, first things first: You don't appear to be using Guice the way it is intended. Generally speaking, you want to use Guice.createInjector() to start up your entire application, and let it create all the constructor arguments for you without ever calling new.
A typical use case might be something like this:
public class Foo {
private final TaskExecutor executor;
#Inject
public Foo(TaskExecutor executor) {
this.executor = executor;
}
}
This works because the instances of Foo are themselves injected, all the way up the Object Graph. See: Getting started
With dependency injection, objects accept dependencies in their constructors. To construct an object, you first build its dependencies. But to build each dependency, you need its dependencies, and so on. So when you build an object, you really need to build an object graph.
Building object graphs by hand is labour intensive, error prone, and makes testing difficult. Instead, Guice can build the object graph for you. But first, Guice needs to be configured to build the graph exactly as you want it.
So, typically, you don't create a Singleton pattern and put the injector into it, because you should rarely call Guice.createInstance outside of your main class; let the injector do all the work for you.
All that being said, to solve the problem you're actually asking about, you want to use Jukito.
The combined power of JUnit, Guice and Mockito. Plus it sounds like a cool martial art.
Let's go back to the use case I've described above. In Jukito, you would write FooTest like this:
#RunWith(JukitoRunner.class)
public class FooTest {
public static class Module extends JukitoModule {
#Override
protected void configureTest() {
bindMock(TaskExecutor.class).in(TestSingleton.class);
}
}
#Test
public void testSomething(Foo foo, TaskExecutor executor) {
foo.doSomething();
verify(executor, times(2)).someMethod(eq("Hello World"));
}
}
This will verify that your Mock object, generated by Mockito via Jukito has had the method someMethod called on it exactly two times with the String "Hello World" both times.
This is why you don't want to be generating objects with ObjectFactory in the way you describe; Jukito creates the Injector for you in its unit tests, and it would be very difficult to inject a Mock instead and you'd have to write a lot of boilerplate.

Using Guice without a main method

I'm creating a library that will be included as a jar, so it won't contain a main method. I'm wondering what is the best practice for bootstrapping Guice in this case. I have one top level singleton.
public class TestManager
{
private TestManager()
{
}
public static TestManager getInstance()
{
// construct and return singleton
}
public void createSomeObjects()
{
}
}
Where should I bootstrap Guice? I was thinking that in the constructor that I could call Guice.createInjector(new Module()); but it wouldn't inject any of the objects created in createSomeObjects().
Is there a common way to do this when you don't have a main method()?
Cheers.
Much like logging configurations, if this is a true library then your options are pretty much this:
Tell the library user that they are responsible for bootstrapping Guice themselves.
Provide a library initialization method that takes care of bootstrapping Guice if they want to use your library
Trying to make the library super-smart to do self-configuration often ends up with somewhat inflexible, hard to test class hierarchies.
If you're just using Guice in the scope of your library and not for the whole application then you could use a static block in the TestManager class.
This strategy assumes that the application is going to call TestManager.getInstance() at some point and that it is the only entry point into your API.
#Singleton
class TestManager {
private static final TestManager INSTANCE;
static {
INSTANCE = Guice.createInjector(new Module()).getInstance(TestManager.class);
}
private TestManager() {
}
public static TestManager getInstance() {
return INSTANCE;
}
#Inject
public void createSomeObjects(YourDependencies ...) {
// Do something with dependencies
}
}
This is not what you should do, but what you could do:
Let key classes in your library extend or otherwise reference a class that initializes everything in a static block. This is a very dirty hack, but if you make sure your api can't be accessed without the classloader loading your initializer class you should be on the safe side.
You could make it a bit cleaner if you used AspectJ and injected a private Member of the initializer type into all classes in your library (without touching the java code), but it would still be a hack.

Categories