I'm using Google Guice, to bind a class with a mock class for some unit test.
This is my current code:
#Override
protected void configure() {
bind(ProductCaller.class).to(MockProductCaller.class);
}
In my test:
Injector injector = Guice.createInjector(new JUnitMockBootstrapBinder());
#Before
public void init() {
injector.getBinding(ProductCaller.class);
injector.getInstance(ProductCaller.class);
}
But in my mock class I have an attribute that I would like to set before the mock is instantiated.
public class MockProductCaller extends ProductCaller {
private String jsonValue; // <---This value
}
Any idea how to accomplish this?
You can inject maybe inject a #Named property :
public class MockProductCaller extends ProductCaller {
#Named("jsonValue") #Inject
private String jsonValue; // <---This value
}
and then :
Injector injector = Guice.createInjector(new JUnitMockBootstrapBinder(),
new AbstractModule() {
public void configure() {
bind(String.class).annotatedWith(Names.named("jsonValue"))
.toInstance("someValue");
}
});
Related
I just started looking at Guice for a new project. I have something like this
the ConfigImpl class ans Config interface
interface Config{...}
class ConfigImpl implements Config {
private static final Map<> propMap;
public ConfigImpl(Map<> propMap) {
this.propMap = someProps;
}
}
Guice injection I came up with
public class MyInjector extends AbstractModule {
protected void configure() {
bind(Config.class).to(ConfigImpl.class)
}
}
and finally
public SomeClass {
Config someConfig;
Injector injector = Guice.createInjector(new MyInjector());
someConfig = injector.getInstance(Config.class);
}
Now I am very confused as I can't find a way to pass propMap into ConfigImpl class. I'd like to know the proper way of doing it in Guice. Thanks!
You should inject propMaps from your module:
public class MyInjector extends AbstractModule {
private final Map<String,String> mapProps;
public MyInjector(Map<String,String> mapProps) {
this.mapProps = mapProps;
}
protected void configure() {
bind(Config.class).to(ConfigImpl.class).in(Scope.SINGLETON); // You most than likely want this
bind(new TypeLiteral<Map<String,String>>() {}).toInstance(mapProps); // binding for the map.
}
}
And use it like this:
public class SomeClass {
void doSomething() {
Map<String,String> mapProps = ... ;
Injector injector = Guice.createInjector(new MyInjector(mapProps));
Config someConfig = injector.getInstance(Config.class);
}
}
Also, you should fix your ConfigImpl class:
class ConfigImpl implements Config {
private final Map<String,String> propMap;
#Inject // mandatory since you use a non-default constructor
public ConfigImpl(Map<String,String> propMap) { // add the generic type of the map
this.propMap = propMap;
}
}
I have a standard CDI bean named NamedService;
#Named
public class NamedService {
#com.google.inject.Inject
private HazelcastService hazelcastService;
public void writeSomething() {
if (hazelcastService != null) {
System.out.println("injected properly.");
} else {
System.out.println("hazelcastService null");
}
}
}
which injects HazelcastService;
#Named
public class HazelcastService {
#Inject
#HazelcastMap(name = CacheConstants.CONFIGURATION_MAP)
protected IMap<String, String> configurationMap;
}
They are binded in the specified Module class:
public class NamedServiceModule extends AbstractModule {
#Override
public void configure() {
bind(NamedService.class);
bind(HazelcastService.class);
}
}
When I try to see whether hazelcastMap injected properly, It throws an exception;
public class TestGuice {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new NamedServiceModule());
NamedService namedService = injector.getInstance(NamedService.class);
namedService.writeSomething();
}
}
Exception in thread "main" com.google.inject.CreationException: Unable
to create injector, see the following errors:
1) No implementation for com.hazelcast.core.IMap was bound. while locating
com.hazelcast.core.IMap
for field at com.sqills.s3_ticket.service.util.HazelcastService.configurationMap(HazelcastService.java:11)
at
com.sqills.s3_ticket.service.util.NamedServiceModule.configure(NamedServiceModule.java:10)
When I remove the hazelcastMap injection then the injection is successful. How can I properly inject that hazelcast map by using Guice?
I have a controller like this
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AccountController implements CRUDController<Long, Account> {
private AccountDao accountDao;
private AccountService accountService;
#Inject
public AccountController(AccountDao accountDao, AccountService accountService) {
this.accountDao = accountDao;
this.accountService = accountService;
}
...
I'm injecting AccountDao and AccountService using
ResourceConfig config = new ResourceConfig()
.packages("controller", "exception")
.register(new MyDIBinder());
Where MyDIBinder is contains all the bindings (e.g
AccountDaoImpl accountDaoImpl = new AccountDaoImpl();
bind(accountDaoImpl).to(AccountDao.class);
)
Now I want to write a unit test for this controller, is it possible to inject the whole AccountController instance with all of it's transitive dependencies into the test?
Something like
#Inject
AccountController accountController;
You can use the main IoC container, and just explicitly inject the test class. Jersey uses HK2 as its DI framework, and its IoC container is the ServiceLocator, which has a method inject(anyObject) that can inject any objects with dependencies that are in its registry.
For example you could do something like
public class InjectionTest {
#Inject
private TestController controller;
#Before
public void setUp() {
final Binder b = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(TestController.class);
}
};
final ServiceLocator locator = ServiceLocatorUtilities.bind(new TestBinder(), b);
locator.inject(this);
}
#Test
public void doTest() {
assertNotNull(controller);
String response = controller.get();
assertEquals("Hello Tests", response);
}
}
The ServiceLocatorUtilities class is a helper class that allows us to easily create the ServiceLocator, and then we just call inject(this) to inject the InjectionTest.
If it seems repetitive to do this for all your controller tests, you may want to create an abstract base test class. Maybe something like
public abstract class AbstractControllerTest {
protected ServiceLocator locator;
private final Class<?> controllerClass;
protected AbstractControllerTest(Class<?> controllerClass) {
this.controllerClass = controllerClass;
}
#Before
public void setUp() {
final AbstractBinder binder = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(controllerClass);
}
};
locator = ServiceLocatorUtilities.bind(new TestBinder(), binder);
locator.inject(this);
}
#After
public void tearDown() {
if (locator != null) {
locator.shutdown();
}
}
}
Then in your concrete class
public class TestControllerTest extends AbstractControllerTest {
public TestControllerTest() {
super(TestController.class);
}
#Inject
private TestController controller;
#Test
public void doTest() {
assertNotNull(controller);
assertEquals("Hello Tests", controller.get());
}
}
If you spent some more time, I'm sure you could come up with a better abstract test class design. It was the first thing that came to mind for me.
Note: For anything request scoped, you mayb need to just mock it. When running the unit tests, there is no request context, so the test will fail.
See Also:
Using Jersey's Dependency Injection in a Standalone application
HK2 documentation
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
#BeforeClass
public static void doTest() {
ServiceLocator serviceLocator = ServiceLocatorUtilities.bind(new AbstractBinder() {
#Override
protected void configure() {
bindAsContract(YourClass1.class);
bindAsContract(YourClass2.class);
bindAsContract(YourClass3.class);
}
});
YourClass1 yourClass1 = serviceLocator.getService(YourClass1.class);
...
I want to create a global state (data object, not a service object).
I have created class MyDataObject.
I want to avoid regular global state,
but prefer using Guice dependency injection.
However all the tutorials show how to set a DI for service object with registration to interface.
How can I use Guice injection for my need?
Edit
I have tried:
public class AppInjector extends AbstractModule {
#Override
protected void configure() {
bind(E2eResult.class).toInstance(new E2eResult());
}
}
with:
#Test
public void sendSearchRequest() throws Exception {
...
e2eResult = injector.getInstance(E2eResult.class);
timerUtils.setTimeOut(criticalBlockTimeOutMilli);
timerUtils.startStopWatch();
...
long timeElapsed = timerUtils.stopStopWatch();
e2eResult.runTime = timeElapsed;
...
}
and:
public static void main(String... args) throws ClassNotFoundException, IOException {
Injector injector = Guice.createInjector(new AppInjector());
Result result = runTest(classAndMethod);
E2eResult e2eResult = injector.getInstance(E2eResult.class);
}
and yet I saw the in the main was without the new long value.
To inject GlobalState class you should first a create an instance of it(set it as you like) and then bind class to instance:
bind(GlobalState.class)
.toInstance(globalState);
GlobalState can be created and configured in your "module", you can read about it more here:
https://github.com/google/guice/wiki/GettingStarted
So you have a plain old java object GlobalState:
public class GlobalState {
// whatever...
}
You can use the singleton mechanism provided by guice:
bind(GlobalState.class).in(Singleton.class);
Or use the instance binding:
bind(GlobalState.class).toInstance(new GlobalState());
In this way, you will be able to inject an unique instance of GlobalState in your application.
I eventually create an "old" bad singleton
No need for special binding
because i didn't have any pre-loaded object.
#Override
protected void configure() {
}
just to carry a one and only guice injector
public class InjectorSingleton {
public Injector guiceInjector;
private static InjectorSingleton singleton;
private InjectorSingleton() {
guiceInjector = Guice.createInjector(new AppInjector());
}
public static InjectorSingleton getInstance() {
if (singleton == null) {
singleton = new InjectorSingleton();
}
return singleton;
}
}
and I call this from my main class and from my test class
InjectorSingleton.getInstance().guiceInjector.getInstance(MyDataObject.class);
Fashionably late to the party. I just wanted to share this pattern.
package modules;
public class MetricsModule extends AbstractModule {
#Override
protected void configure() {
// The initializer is an eager singleton
bind(modules.MetricsModule.MeterRegistryInitializer.class).asEagerSingleton();
}
private static class MeterRegistryInitializer {
#Inject
// When initialized, Guice will handle the injection as usual
public MeterRegistryInitializer(Config config, MeterRegistry registry) {
var instance = config.getString("instance.id");
registry.config().commonTags(List.of(
Tag.of("instance", instance)
));
// This is global state
Metrics.addRegistry(registry);
}
}
#Provides
#Singleton
MeterRegistry provideMeterRegistry(
#MetricsDriver String driver,
PrometheusMeterRegistry prometheusRegistry
) {
MeterRegistry registry;
switch (driver) {
case "none":
registry = new CompositeMeterRegistry();
break;
case "prometheus":
registry = prometheusRegistry;
break;
default:
throw new UnsupportedOperationException();
}
return registry;
}
}
I'm trying to get field injecting to work in google-gin, but when I call TestClass.test() the injected field is null. What am I doing wrong? According to the docs field injection should be really straight forward. Is there anything I'm missing in the ContextModule class?
public class MainEntry implements EntryPoint {
private final ContextInjector injector = GWT.create(ContextInjector.class);
#Override
public void onModuleLoad() {
injector.getAppMain();
}
}
#GinModules(ContextModule.class)
public interface ContextInjector extends Ginjector {
AppMain getAppMain();
}
public class MyLogger {
}
public class ContextModule extends AbstractGinModule {
#Override
protected void configure() {
bind(MyLogger.class).in(Singleton.class);
}
}
public class AppMain {
#Inject
AppMain(MyLogger logger) {
// logger is injected properly here
new TestClass().test();
}
}
public class TestClass {
#Inject
private MyLogger logger;
public void test() {
// logger is null here!
}
}
Your TestClass is not managed by GIN, so GIN won't inject anything in it. You have to either let GIN instantiate TestClass (e.g. change MyLogger to TestClass in your AppMain constructor, and call test() on the given instance), or ask GIN to inject an existing TestClass instance's members (add a method to your Ginjector that takes a TestClass as argument, when called, it'll inject fields and methods of the passed-in instance).