Hi I am experimenting with Google Guice 5.x. I have my class defined as:
public class Foo<T> {
// some logic here
}
and it's being used in other classes like:
public class Bar {
private final Foo<Chocolate> provider;
public Bar(Foo<Chocolate> _choco) {
this.provider = _choco;
}
}
public abstract class BaseZoo { // in some other package in a different jar
private final Injector injector = Guice.createInjector(new MyAppModule());
private Foo<?> provider;
public String doSomething() {
if (provider == null)
this.provider = this.injector.getInstance(Foo.class);
// some other code logic.
}
}
Now, in my module file (MyAppModule) I have defined Foo as:
#Inject
#Provides
#Singleton
public Foo<Chocolate> getFoo(FooDependency fooDep) {
return new Foo<>(fooDep);
}
Now when I run my code, Google Guice is able to find an instance for Foo<Chocolate> but is not able to find an instance for Foo<?>.
Is there a way to configure Google Guice to resolve Foo<?> with an instance of Foo<Chocolate>?
Bind the type:
#Inject
#Provides
#Singleton
public Foo<?> getFoo(FooDependency fooDep) { ... }
If you still want to bind Foo<Chocolate>, use the #Provides method in the question, but also bind Foo<?> to it, you can do so in your configure method:
bind(new Key<Foo<?>>() {}).to(new Key<Foo<Chocolate>>() {});
or, with a provider method:
#Provides
Foo<?> provideWildcardFoo(Foo<Chocolate> chocolateFoo) {
return chocolateFoo;
}
Related
I'm new to guice, and using guice 4.1 I want to instantiate the following class:
public class GuiceMain<T> implements IGuiceMain<T> {
private ObjectWrapperFactory<T> factory;
#Inject
public GuiceMain(ObjectWrapperFactory<T> factory) {
this.factory = factory;
}
public void process(T t) {
ObjectWrapper wrapper = factory.create(t);
}
}
I dont have strong requirements how to create it, but I came to the solution to create interface GuiceMainFactory
public interface GuiceMainFactory {
<T> GuiceMain<T> create();
}
so my client code will look like this:
Injector injector = Guice.createInjector(new Configuration());
GuiceMain<File> instance = injector.getInstance(GuiceMainFactory.class).create();
File f = new File("text.txt");
instance.process(f);
and in my guice configuration class I write
install(new FactoryModuleBuilder().implement(IGuiceMain.class, GuiceMain.class).build(GuiceMainFactory.class))
But I get the error: 1) com.guice.GuiceMainFactory cannot be used as a key; It is not fully specified.
So I decided to write the implementation of GuiceMainFactory
public class GuiceMainFactoryImpl implements GuiceMainFactory {
private ObjectWrapperFactory objectWrapperFactory;
#Inject
public GuiceMainFactoryImpl(ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
}
#Override
public <T> GuiceMain<T> create() {
return new GuiceMain<T>(objectWrapperFactory);
}
}
but in this case, on the line return new GuiceMain(objectWrapperFactory); I get unchecked assignment warning, because I cant specify generic type of objectWrapperFactory
I think I've over complicated everything, but I dont know how to deal with it
I have class A which is taking a set as guice dependency. The set is singleton. Below is the code example:
class A
{
private Set<InetAddress> set;
private String pingUriPath;
#Inject
public A(Set<InetAddress> set, #Named("pingUri") String pingUriPath)
{
this.set = set;
this.pingUriPath = pingUriPath; // this is used somewhere
}
public void storeValue(String str)
{
if(str.equals("abc"))
{
set.add(str);
}
}
}
Here is the guice module that injects dependency:
private class GuiceModule extends AbstractModule {
#Override
public void configure() {
bindConstant().annotatedWith(Names.named("pingUri")).to("/ping");
}
#Provides
#Singleton
Set<InetAddress> healthyTargets(){
return Sets.newConcurrentHashSet();
}
}
I want to mock the method storeValue and for that i have to mock the set. I am not able to mock the set using guice.
If i mock like below, it gives assertion error(no interactions with this mock)
#Mock
Set<InetAddress> mockHealthyTargets;
private class MockClassesModule extends AbstractModule {
#Override
public void configure() {
bindConstant().annotatedWith(Names.named("pingUri")).to("/ping");
}
#Provides
#Singleton
Set<InetAddress> healthyTargets(){
return Sets.newConcurrentHashSet();
}
}
public test_storeValue()
{
Injector injector = Guice.createInjector(new MockClassesModule());
A a = injector.getInstance(A.class);
a.storeValue("abc");
verify(mockHealthyTargets).add("abc")
}
If you have the need to use guice in your unit tests, something is most likely going the wrong direction. One of the biggest benefits of dependency injection is that testing becomes easy, because you can pass dependencies that are controlled by you.
I assume you want to test the class A and specifically the method storeValue. For this you don't even need mocking
#Test
public void test() {
// prepare dependencies
Set<InetAddress> set = Sets.newConcurrentHashSet();
String pingUri = "example.com";
// prepare input
String input = "someValue";
// prepare class under test
A classUnderTest = new A(set, pingUri);
// call class under test
classUnderTest.storeValue(input);
// check that what you expected happened
// in this case you expect that the dependency set now contains the input
assertThat(set, contains(input));
}
I have found what the mistake was, I should return mock when providing to my unit test. It should look like this:
#Mock
Set<InetAddress> mockHealthyTargets;
private class MockClassesModule extends AbstractModule {
#Override
public void configure() {
bindConstant().annotatedWith(Names.named("pingUri")).to("/ping");
}
#Provides
#Singleton
Set<InetAddress> healthyTargets(){
return mockHealthyTargets;
}
}
For a dagger2 module
#Module
public class MyModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
}
We could have the test module as Test
public class TestModule extends MyModule {
#Override public MyPrinter provideMyPrinter() {
return Mockito.mock(MyPrinter.class);
}
#Override public RestService provideRestService() {
return Mockito.mock(RestService.class);
}
}
However if for a class as below that is not declared in the dagger module...
public class MainService {
#Inject MyPrinter myPrinter;
#Inject public MainService(RestService restService) {
this.restService = restService;
}
}
How do I create a mock of MainService as above.
Note, I'm not planning to perform test for MainService as per share in https://medium.com/#fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.9aky15kke, but instead, my MainService is used in another normal class that I wanted to test. e.g.
public class MyClassDoingSomething() {
#Inject MainService mainService;
public MyClassDoingSomething() {
//...
}
// ...
public void myPublicFunction() {
// This function uses mainService
}
}
This is definitely not answering your question, but in my honest opinion it is related, it's helpful and too big for a comment.
I'm often facing this question and I end always doing "Constructor dependency injection". What this means is that I no longer do field injection by annotating the field with #Inject but pass the dependencies in the constructor like so:
public class MyClassDoingSomething implements DoSomethig {
private final Service mainService;
#Inject
public MyClassDoingSomething(Service mainService) {
this.mainService = mainService;
}
}
Notice how the constructor now receives the parameter and sets the field to it and is also annotated with #Inject? I also like to make these classes implement an interface (also for MyService) - Amongst several other benefits I find it makes the dagger module easier to write:
#Module
public class DoSomethingModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
#Provides #Singleton public Service provideMyPrinter(MyService service) {
return service;
}
#Provides #Singleton public DoSomethig provideMyPrinter(MyClassDoingSomething something) {
return something;
}
}
(This assumes that MyService implements or extends Service)
By now it seems you already know that dagger is able to figure out the dependency graph by itself and build all the objects for you. So what about unit testing the class MyClassDoingSomething? I don't even use dagger here. I simply provide the dependencies manually:
public class MyClassDoingSomethingTest {
#Mock
Service service;
private MyClassDoingSomething something;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
something = new MyClassDoingSomething(service);
}
// ...
}
As you see, the dependency is passed through the constructor manually.
Obviously this doesn't work if you're coding something that doesn't have a constructor that can be invoked by you. Classical examples are android activities, fragments or views. There are ways to achieve that, but personally I still think you can somehow overcome this without dagger. If you are unit testing a view that has a field #Inject MyPresenter myPresenter, usually this field will have package access that works fine in the tests:
public class MyViewTest {
#Mock MyPresenter presenter;
private MyView view;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
view.myPresenter = presenter;
}
}
Note that this only works if both MyViewTest and MyView are in the same package (which often is the case in android projects).
At the end of the day if you still want to use dagger for the tests, you can always create "test" modules and components that can inject by declaring methods in the component like:
#Inject
public interface MyTestComponent {
void inject(MyClassDoingSomething something);
}
I find this approach ok-ish, but throughout my development years I prefer the first approach. This also has reported issues with Robolectric that some setup in the build.gradle file is required to actually make the dagger-compiler run for the tests so the classes are actually generated.
I have one Maven module where I define some utils shared across several other Maven modules.
In this module I want to create some singleton:
package org.me.util;
public interface Baz {
String myMethod(String s);
}
#Singleton
public class Foo implements Baz {
private Bar bar;
#Inject
Foo(Bar bar) {
this.bar = bar;
}
#Override
public String myMethod(String s) {
return s;
}
}
Then I bind my interface with:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(Baz.class).to(Foo.class);
}
}
Suppose I want to use this singleton from another Maven module (say, a web service), how do I achieve this?
The only way I found was to create a class in my util Maven module like:
package org.me.util;
public class Main {
private static Injector injector = Guice.createInjector(new MyModule());;
public static Injector getInjector() {
return injector;
}
Alternatively I could create the injector in a static main method as seen in the Guice tutorials, and save an instance somewhere.
Then from my web service do something like:
Baz baz = Main.getInjector().getInstance(Baz.class);
But this does not seem very elegant because I have to pass my injector around (in this case by providing it with a static getter).
Is there any other way? Thanks.
The more elegant way to pass the Baz object it's to inject it.
For example,
public class BusinessService {
private final Baz baz;
#Inject
public BusinessService(Baz baz) { this.baz = baz; }
}
And again if you want to use the BusinessService in a WS.
public class WebService {
private final BusinessService businessService;
#Inject
public WebService(BusinessService businessService) { this.businessService = businessService; }
}
By doing this, all the dependency are clearly exposed in the API signatures.
The remaining problem is that you have to create the Injector in the entry point of an application.
In a standalone application, the injector is usually created in the main method.
public static void main(String[] args) {
Injector injector = Guice.createInjector(new UtilModule(), new BusinessModule());
BusinessService businessService = injector.getInstance(BusinessService.class);
...
}
In a web application, the injector might be created in different way. It depends of the framework.
Servlets:
guice servlet module
Dropwizard:
dropwizard-guice
dropwizard-guicey
playframework
play-module
...
I want to make a binding using a method annotated with #Provides into an eager singleton. I've found bug 216, which suggests this isn't possible, but doesn't mention the #Provides annotation explicitly.
I currently have a class that requests the eager singletons in time by itself being a singleton, but it's not a very nice solution.
public class LogicModule extends AbstractModule {
#Override public void configure() {
bind(SomeDep.class);
bind(MyWorkaround.class).asEagerSingleton();
}
// cannot add eager requirement here
#Provides #Singleton Logic createLogic(SomeDep dep) {
return LogicCreator.create(dep);
}
private static class MyWorkaround {
#Inject Logic logic;
}
}
Can I change something near the comment that would make the workaround class obsolete?
Why not to use
bind(Logic.class).toInstance(LogicCreator.create(dep));
//ohh we missing dep
then we can do this
class LogicProvider implements Provider<Logic> {
private final SomeDep dep;
#Inject
public LogicProvider(SomeDep dep) {
this.dep = dep;
}
#Override
public Logic get() {
return LogicCreator.create(dep);
}
}
and then
bind(Logic.class).toProvider(LogicProvider.class).asEagerSingleton();
You can even pass SomeDep dep to your provider as Provider<SomeDep> and then call providerDep.get() in LogicCreator.create() that would be a bit more robust.