Why Dagger inject is not working but component.getObject yes - java

I am trying to use Dagger 2 for instantiating a Retrofit interface. The CloudContactDataStore class injects the RestClient and calls its methods.
When I instantiate a CloudContactDataStore object, its RestClient attribute has null value.
public class CloudContactDataStore implements ContactDataStore {
#Inject RestClient restClient;
public CloudContactDataStore() {
this.initializeInjector();
}
private void initializeInjector() {
ApiComponent component = DaggerApiComponent.builder()
.apiModule(new ApiModule())
.build();
component.inject(this); // Nothing changes, restClient is null!
this.restClient = component.getRestClient(); // This works
}
}
Here is how I create the Dagger Module and Component:
#Singleton
#Component(modules = ApiModule.class)
public interface ApiComponent {
void inject(ContactDataStore contactDataStore);
RestClient getRestClient();
}
#Module
public class ApiModule {
#Provides public RestClient provideRestClient(ApiService apiService) {
return new RestClientImpl(apiService);
}
#Provides public ApiService provideApiService(RestAdapter restAdapter) {
return restAdapter.create(ApiService.class);
}
#Provides public RestAdapter provideRestAdapter() {
return RestApiAdapter.getInstance();
}
}
So, why the inject function does not work but calling component's getRestClient() yes?

I find it's very useful to look at the code that Dagger 2 generates as it's quite easy to follow and can often point you in the right direction.
Dagger 2 creates code much like you'd write so think about how you would implement ApiComponent.inject(ContactDataStore). Assuming within that method you had access to a RestClient how would you get it into the field? If you sat and wrote it you'd notice that you have to do something like this:
((CloudContactDataStore) contactDataStore).restClient = restClient;
Or in other words you'd need to cast it down to a specific implementation. Dagger 2 doesn't cast down, EVER (at least I've not seen it), because that is usually unsafe.
So, you have two choices. Change the inject(ContactDataStore) method to inject(CloudContactDataStore), or provide a method on ContactDataStore that allows the RestClient to be passed in.
Update: Its not possible to #Inject through abstract, i.e. interface, method.
If you want to inject it through the ContactDataStore API then you have a problem as there is currently a restriction in Dagger 2 (have raised a feature request to remove it) that you cannot mark an abstract method with #Inject. So, in the meantime (or forever if there is some reason why it cannot work in Dagger 2) you will need to do it manually, i.e. get the instance of RestClient from your component method and pass it to the appropriate method on the interface.

Related

Generically asserting aspect service calls to lower layer with Mockito

Looking for a generic way to assert that a call to an aspect services method makes a call to a lower level service call by the same name.
Our application uses OSGI and aspect service where there are several implementations of the same interface with each implementation calling through to the lower layers, and likely doing some post processing. For example...
public interface ConfigService {
public List<String> getConfigurations();
}
public class ConfigServiceImpl implements ConfigService {
private volatile ConfigDAO dao;
#Override
public List<String> getConfigurations() {
List<String> configs = dao.getConfigurations();
// do something with configs
return configs;
}
}
public class ConfigDAO implements ConfigService {
#Override
List<String> getConfigurations() {
// database calls
return configs;
}
}
I would like to come up with a generic method, something along the lines of verifyCallsDelegate(configService, mockConfigDao, "getConfigurations");, that would allow me to write a bunch of one liners testing call through behavior instead of
configService().getConfigurations();
verify(mockConfigDao).getConfigurations();
This is a contrived example and I would likely like to take things a step further and assert that the value returned from mockConfigDao.getConfigurations() was what was returned from configService.getConfigurations() but once I get past getting the simplest test to work I will add that kind of smarts.
Can anyone suggest an implementation or like technique for my ```verifyCallsDelegate```` method above.

Mockito fails on NoSuchMethodError due to method being in abstract class using generic method

I'm running through some service tests and I am testing a concrete class that extends from one that uses generics.
An example setup of the service layer is below:
public abstract class AbstractService <E extends AbstractEntity, IT extends AbstractItem> {
public void deleteAllItems(E entity) {
List<IT> items = new ArrayList<IT>(entity.getItems());
for(IT item : items) {
//Yada, yada
}
}
}
public class Service extends AbstractService<Entity, Item> {
}
public class OtherService() {
#Inject
private ServiceManager serviceManager;
public void deleteItems(Entity e) {
serviceManager.getService().deleteAllItems(e);
}
}
Then to test it I have the following:
public class Test {
private Service service;
private OtherService otherService;
private ServiceManager serviceManager;
#BeforeMethod
public void setup() {
serviceManager= mock(serviceManager.class);
service= mock(Service.class);
when(serviceManager.getService()).thenReturn(service);
otherService=injector.getInstance(OtherService.class);
}
#Test
public void test() {
Entity e = new Entity();
//Attach some items
otherService.deleteItems(e);
verify(service).deleteAllItems(e);
}
}
This should call the OtherService, which exists (We're using injection to get ahold of the object), and then call the method deleteItems(), which in turn should call deleteAllItems() on the Service. Before I had implemented the Java generics, this worked fine, but since I have implemented the Java generics, the Mockito test fails with the following exception:
java.lang.NoSuchMethodError:
Service.deleteAllItems(Entity;)V
at
Test.test(Test.java:XXX)
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at Test.test(Test.java:XXX)
Example of correct verification:
verify(mock).doSomething()
Also, this error might show up because you verify either of:
final/private/equals()/hashCode() methods. Those methods cannot be
stubbed/verified.
Which sounds like it can't find the method. Should I instead mock the abstract class of AbstractService or is there something else that I am missing?
EDIT
From what I've seen of the Mockito inner workings, it creates an instance of this:
public void AbstractService.deleteAllItems(Entity)
For the MockitoMethod object, so that would make sense that Service.deleteAllItems() "isn't called", it appears Mockito assumes only the baseclass was ever called. So it does appear that I need to mock the base class instead. I'm going to investigate further, but if anyone has any other ideas, I'm open to suggestions
I can suggest to localize the problem - either it is in mocking:
#Test
public void test() {
Entity e = new Entity();
service.deleteItems(e); // Note! 'service' itself, not an 'otherService'
verify(service).deleteAllItems(e);
}
or in injection (remove inheritance and generics):
public class Service /*extends AbstractService<Entity, Item>*/ {
public void deleteAllItems(Entity entity) {
//...
}
}
Split the problem iterativelly and you will find the cause.
When you create a non-generic subclass of a generic class, Java creates "bridge methods" for any methods that use the generic type. The bridge methods look like the inherited methods but use the the specific class specified for the generic parameters instead of generics.
Java creates these methods because the methods of the subclass are not generic, so they need to "look like" non-generic methods (i.e. not subject to erasure, reflection will work as expected, etc). See this answer for details.
The solution is to have Mockito mock the type returned by serviceManager.getService().
After further investigation, I found a way to force Mockito to call the correct class. As I mentioned briefly, we're using injection to get ahold of the object. During the setup we do run through a setup of the injector, which I hadn't felt was causing the issue. But it did present a solution. This was how we were calling it:
Injector injector = Guice.createInjector(new Module() {
#Override
public void configure(Binder binder) {
service = mock(Service.class);
binder.bind(Service.class).
toInstance(service);
}
To solve the issue, we just bound the AbstractService class to the mocked instance of the Service class, like so:
Injector injector = Guice.createInjector(new Module() {
#Override
public void configure(Binder binder) {
service = mock(Service.class);
binder.bind(Service.class).
toInstance(service);
binder.bind(AbstractService.class).
toInstance(service);
}
So now, when Mockito attempts to get an instance of the AbstractService, it calls the mocked Service and solves our issue.
If anyone has any feedback it there is an alternative solution, then feel free to post and I can test it out and check if there are better methods that what we are doing.

How to mock an object created via Class.newInstance(className)?

I'm trying to add unit tests to some legacy code that has a String class name passed to it and that creates an object implementing a particular handler interface using Class.newInstance(String className). I can control the class name I'm passing, I can get a pointer to the new handler object (via a getHandler() call), and I would like to observe calls to it using Mockito.
My current solution is:
Create a new test class TestHandler that implements the interface.
Have that test class contain a Mockito mock object that also implements the interface.
Manually pass through all the interface methods to the mock object.
Make the mock object accessible via a getMock() method.
Observe the object by making verify() calls to objectUnderTest.getHandler().getMock().
This works, but feels a little inelegant, especially having to manually write all the pass-thru methods.
Is there a better solution?
Fundamentally, you're running into the same problems as trying to test a newly-created instance using new; the Class.newInstance (probably properly Class.forName(foo).newInstance()) doesn't hurt you, but doesn't help you either.
As a side note, your TestHandler sounds like a general purpose delegate implementation, which sounds pretty useful anyway (particularly if you ever need to write a Handler wrapper). If it is, you might want to promote it to be adjacent to your Handler in your production code tree.
Though I recognize that you mention legacy code, this becomes very easy if you are allowed to refactor to include a testing seam. (Ignoring reflective exceptions here for ease of explanation.)
public ReturnType yourMethodUnderTest(String className) {
return yourMethodUnderTest(Class.newInstance(className));
}
/** Package private for testing. */
public ReturnType yourMethodUnderTest(Handler handler) {
return yourMethodUnderTest(Class.newInstance(className));
}
You could also extract the object creation and replace it in your test:
/** Instance field, package-private to replace in tests. */
Function<String, Handler> instanceCreator =
( x -> (Handler) Class.forName(x).newInstance());
public ReturnType yourMethodUnderTest(String className) {
Handler handler = instanceCreator.apply(className);
// ...
}
You could even just extract it to a method and replace it in your test:
public ReturnType yourMethodUnderTest(String className) {
Handler handler = createHandler(className);
// ...
}
/** Package private for testing. */
Handler createHandler(String className) {
return Class.forName(className).newInstance();
}
#Test public void yourTest() {
// Manually replace createHandler. You could also use a Mockito spy here.
ObjectUnderTest objectUnderTest = new ObjectUnderTest() {
#Override Handler createHandler(String className) {
return mock(Handler.class);
}
}
// ...
}
Side note: Even though Mockito creates a named dynamic type, you almost certainly will not be able to hack it in and allow your code to create it by name. This is because the call to mock registers the instance within Mockito's internal state.
// BAD: Unlikely to work
#Test public void yourTest() {
objectUnderTest.methodUnderTest(
mock(Handler.class).getClass().getName());
// ...
}
Create a public method where you will place the logic to fetch the newInstance of the class
ClassA objectClassA=createNewInstance(className);
likewise,and
public ClassA createInstance(String className){
return (ClassA) (Class.forName(className)).newInstance();
}
Now suppose we were creating an instance of classA inside of ClassB
then in TestClass of B, we can simply mock this createInstance method
doReturn(mockClassA).when(mockClassB).createInstance(className);

Guice: instantiating a singleton before creating the module

Is it possible to instantiate and assign a singleton to a reference with Guice before creating the Module and pass that instance to the Module constructor be bound during configuration?
Here is an example of what I mean:
I have a method that allows me to create objects depending on a custom implementation of an interface which is being passed in constructor as an Optional (if the user won't provide a custom implementation, we will use the default one), which is being done by binding the interface to that particular implementation in the Module class. :
public static MyClass createMyClassObject(Optional<SpecialInterface> customSpecialInterfaceObject) {
SpecialInterface specialInterfacebject;
if(customSpecialInterfaceObject.isPresent() {
specialInterfaceObject = customSpecialInterfaceObject.get()
} else {
/* here I would like to bind it to an instance of the DefaultSpecialInterfaceObject but can't really do something like:
Injector injector = Guice.createInjector(myClassModule);
DefaultSpecialInterface instance = injector.getInstance(DefaultSpecialInterface.class);
as the module is yet to be created */
}
MyClassModule myClassModule = new MyClassModule(specialInterfaceObject);
Injector injector = Guice.createInjector(myClassModule);
return injector.getInstance(MyClass.class);
}
I'm currently using classes instead of instances to solve this problem, such as in the example below, but I don't quite like this solution. Would be happy to see a better way of doing it:
private static Class resolveSpecialInterfaceObject(Optional<SpecialInterface> customSpecialInterfaceObject) {
Class specialInterfaceObjectClass;
if (customSpecialInterfaceObject.isPresent()) {
specialInterfaceObjectClass= customSpecialInterfaceObject.get().getClass();
} else {
specialInterfaceObjectClass = DefaultSpecialInterface.class;
}
return specialInterfaceObjectClass;
}
public abstract class MyClassModule extends AbstractModule {
private final Class<SpecialInterface> specialInterfaceObjectClass;
public MyClassModule(Class<SpecialInterface> specialInterfaceObjectClass) {
this.specialInterfaceObjectClass= specialIntefaceObjectClass;
}
#Override
protected void configure() {
bind(SpecialInterface.class).to(specialInterfaceObjectClass);
}
}
Edit, from a comment below:
one more thing- didn't want to make the question too long; actually, I also want to perform another operation on the resulting instance of SpecialInterface, but only if it is the instance of DefaultSpecialInterface and I don't think it should be done in the Module. I was thinking if I could just have this bean up and running before, such as in Spring, so I could just pass it to the Module, but also use it in another method call before?
Can you take the whole Optional and use bind(...).toInstance(...)?
public static MyClass createMyClassObject(
Optional<SpecialInterface> customSpecialInterfaceObject) {
MyClassModule myClassModule = new MyClassModule(customSpecialInterfaceObject);
Injector injector = Guice.createInjector(myClassModule);
MyClassFactory instance = injector.getInstance(MyClassFactory.class);
return instance.createMyClassObject();
}
class MyClassModule extends AbstractModule {
private final Optional<SpecialInterface> customObject;
MyClassModule(Optional<SpecialInterface> customObject) {
this.customObject = customObject;
}
#Override public void configure() {
if (customObject.isPresent()) {
// Singleton by necessity: Guice doesn't know how to create another one.
bind(SpecialInterface.class).toInstance(customObject.get());
} else {
// Default scoped. Add ".in(Singleton.class)" if necessary.
bind(SpecialInterface.class).toInstance(DefaultSpecialInterfaceClass.class);
}
}
}
If you want to perform additional initialization on DefaultSpecialInterface and nothing else, you have a number of options:
If some kind of initialization is important for all implementations and likely too heavy to put into a class constructor, add an initialize method on your SpecialInterface. Make the custom one a no-op, and implement it for DefaultSpecialInterface.
If the initialization is unique to DefaultSpecialInterface, I see no reason why it shouldn't be in the Module. Write a #Provides method or bind to a Provider<SpecialInterface> that creates and initializes DefaultSpecialInterface correctly.
If your real goal is to keep the business logic out of a Module, you can do so by extracting it into a free-standing Provider or DefaultSpecialInterfaceFactory that is responsible for that.
Remember, Guice is responsible for feeding fully-constructed objects into your object graph, and that means that injecting a SpecialInterface should get a ready-to-use implementor of the SpecialInterface general contract. If Guice needs to perform some initialization to make that happen, it's not unreasonable to have it do so, and a Module isn't a bad place to do it.

How to avoid actual method call while running through junit test case

I have the following scenario.
A class MyClass in which I have an API myAPI() whose contents are as follows:
class MyClass {
public void myAPI() {
...
MyOtherClass myOtherObj = new MyOtherClass();
String value = myOtherObj.decodeAndGetName();
...
}
}
Here we have MyOtherClass which contains an API decodeAndGetName() which does some operation. It is in a different package and I can't modify its code.
Requirement
I need to write a junit test for the above myAPI(). Now I want to somehow mock the object of MyOtherClass and mock the return value of decodeAndGetName().
I am not able to do this, as we have a new MyOtherClass() and as soon as the flow comes to this line, it creates a new instance and goes to the decodeAndGetName() API.
What I need is, some way to prevent the flow going to decodeAndGetName() and take a mock value instead when this call is encountered in the above code.
Please let me know a way to do this.
I've only used it with Android code, but I think you may be able to make use of something like Mockito to mock the MyOtherClass in your tests with code similar to:
MyOtherClass mockMyOtherClass = Mockito.mock(MyOtherClass.class);
when(mockMyOtherClass.decodeAndGetName()).thenReturn(new String("known return value");
I would also suggest using dependency injection and make use of something like Guice in order to accomplish this. I use the combination of Guice & Mockito on a daily basis with my Android projects to successfully accomplish exactly this sort of thing.
Brief Example
Here is what your code may look like after setting up dependency injection with Guice:
MyOtherClassWrapper.java
#Singleton
public class MyOtherClassWrapper {
private MyOtherClass myOtherClass = new MyOtherClass();
public String decodeAndGetName() {
return getMyOtherClass().decodeAndGetName();
}
...
private MyOtherClass getMyOtherClass() {
return myOtherClass;
}
}
MyClass.java
class MyClass {
...
#Inject private MyOtherClassWrapper myOtherClassWrapper;
...
public void myAPI() {
...
String value = getMyOtherClassWrapper().decodeAndGetName();
...
}
private MyOtherClass getMyOtherClassWrapper() {
return myOtherClassWrapper;
}
}
Please see the Guice User's Guide for info on how to get started setting up Guice. It's not too difficult.

Categories