I've just started using Guice and having trouble with understanding the guice way of injection. I'm very familiar with Spring, but this seems a bit different.
I have a DAO class:
public class SomeDAO {
#NotNull
private DB db = null;
public SomeDAO (String databaseName) throws Exception{
xxxxxxxxxxxxxxxxxxxxxxxx
}
}
I have a controller, say:
public class SomeController {
private SomeDAO someDAO;
}
How should i use guice here to inject someDAO object? Note that the databaseName in SomeDAO constructor should be provided from SomeController.
Thanks.
Ideally SomeController shouldn't have to know the name of the database. This would come from a configuration file or from your application context and you'd inject your DAO like this:
public class SomeController {
private final SomeDAO someDAO;
#Inject
SomeController(SomeDAO someDAO) {
this.someDAO = someDAO;
}
}
And then to inject the database name you could do something like this:
public class SomeDAO {
#NotNull
private DB db = null;
#Inject
public SomeDAO (#IndicatesDatabaseName String databaseName) throws Exception {
...
}
}
In this case Guice will provide databaseName (see https://code.google.com/p/google-guice/wiki/BindingAnnotations). If you want to give the controller knowledge of the database name then you could consider just newing the DAO from the controller (but still injecting the controller) or using assisted inject.
EDIT:
If the controller really needs to know about the database you could use assisted inject:
public class SomeController {
private final SomeDAO someDAO;
#Inject
SomeController(#Assisted String databaseName) {
this.someDAO = new SomeDAO(databaseName);
}
}
public interface ControllerFactory {
public SomeController create(String databaseName);
}
public static class MyModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder()
.implement(SomeController.class, SomeController.class)
.build(ControllerFactory.class));
}
}
And then inject ControllerFactory where SomeController is needed. You could apply the same assisted injection to SomeDAO if it ends up needing more injected dependencies, as well.
Related
I have a controller class:
public class Controller {
private final IProcessor processor;
public Controller (final ProcessorFactory factory) {
this.processor = factory.getInstance();
}
}
A Factory class to provide the different instances of IProcessor:
#Component
public class ProcessorFactory {
private final Dep1 dep1;
private final Dep2 dep2;
public ProcessorFactory (final Dep1 dep1,
final Dep2 dep2) {
this.dep1= dep1;
this.dep2= dep2;
}
public IProcessor getInstance() {
if (...) {
return new ProcessorA(dep1, dep2);
}
return new ProcessorB(dep1, dep2);
}
}
In my mockito test class where I use Junit5, I am not able to instantiate the IProcessor member and is null:
#WebMvcTest(Controller.class)
public class ControllerTest {
#MockBean
private ProcessorFactory processorFactory ;
#MockBean
private IProcessor processor;
#Autowired
private MockMvc mockMvc;
#Test
public void test1() throws Exception {
when(processor.process(any(Request.class), any(String.class)))
.thenReturn(new BlaBla("Test", "Test"));
String request = ...
this.mockMvc.perform(post("/test/test").contentType(MediaType.APPLICATION_JSON).content(request))
.andDo(print())
.andExpect(status().is2xxSuccessful());
}
}
I am not sure I am using MockBean correctly. Basically I want to mock both the Factory and the Processor.
Since you need to call a mocked method (getInstance()) during Spring context initialization (inside the Controller's constructor), you need to mock the said method in a different way. The mocked bean has to be not only provided as an existing object, but also it should have it's mocked behavior defined.
Addtionally, IProcessor implementations are not configured as Spring beans, so Spring will not inject them - ProcessorFactory calls new explicitly and creates the objects without Spring involvement.
I've created a simple project to reproduce your problem and provide a solution - you can find it here on GitHub if you want to check if the whole thing is working, but here's the most important test snippet (I've simplified the methods a bit):
#WebMvcTest(Controller.class)
class ControllerTest {
private static final IProcessor PROCESSOR = mock(IProcessor.class);
#TestConfiguration
static class InnerConfiguration {
#Bean
ProcessorFactory processorFactory() {
ProcessorFactory processorFactory = mock(ProcessorFactory.class);
when(processorFactory.getInstance())
.thenReturn(PROCESSOR);
return processorFactory;
}
}
#Autowired
private MockMvc mockMvc;
#Test
void test1() throws Exception {
String result = "this is a test";
when(PROCESSOR.process(any()))
.thenReturn(result);
mockMvc.perform(post("/test/test")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(result));
}
}
let's assume we've got the following snippet as part of an full Java EE application:
#Singleton
public class LoginService {
#Inject
private UserDAO userDAO;
protected boolean login(String username, String password){
// [...]
User user = userDAO.findByUsername(username);
// [...]
}
}
UserDAO is an interface and there's one specific class implementation of that interface called DatabaseUserDAO which is injected into the userDAO field.
Now I am going to write a test for the login method e. g. testLoginSuccessfulIfLoginDataCorrect(). But as I don't want to depend on the database I just want to stub it by using a class e.g. public class TestUserDAO implements UserDAOand inject this one instead of the default class which whould be injected. What are the possibilities to implement it? Let's also assume that there is no constructor injection or other ways to initialize the field.
Use arquillian (http://arquillian.org/) along with mockito (http://site.mockito.org/) or one of its derivates to:
create a mockup of UserDAO to inject
create a bundle from your class under test along with the mock
create a junit test which gets your LoginService along with the mock injected and can use it for tests:
Example (modified from actual code to somewhat match your class names):
(Attention: this kind of deployment generation only works for maven projects)
#RunWith(Arquillian.class)
public class LoginSeviceTest {
// a pattern I find quite neat: hold the mocks in a static local class, but they might be anywhere else
public static class LocalMocks {
#Produces public static UserDAO mockUser = Mockito.mock(UserDAO.class);
}
#Deployment
public static WebArchive createDeployment() {
PomEquippedResolveStage pom = Maven.resolver().loadPomFromFile("pom.xml");
BeansDescriptor beansXml = Descriptors.create(BeansDescriptor.class)
.addDefaultNamespaces().getOrCreateAlternatives()
.up();
WebArchive jar = ShrinkWrap.create(WebArchive.class)
.addAsLibraries(pom.resolve("org.mockito:mockito-core").withTransitivity().asFile())
.addClass(LoginService.class) // eventually further classes or packages you depend on
.addClass(LoginSeviceTest.LocalMocks.class)
.addAsWebInfResource(new StringAsset(beansXml.exportAsString()), "beans.xml");
return jar;
}
#Inject LoginService loginService;
#Test
public void testLogin() {
// use the injected loginService here for actual tests
}
}
Note, that you do not need to change the class under test this way to make testing possible.
Well, there are not secret for that.
You have four possibilities.
Create a constructor for all DAOs (EJBs).
#Singleton
public class LoginService {
#Inject
private UserDAO userDAO;
LoginService (UserDAO userDAO) {
this.userDAO = userDAO;
}
}
Create a set for each DAO.
#Singleton
public class LoginService {
#Inject
private UserDAO userDAO;
setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}
Set the variable directly with default access
#Singleton
public class LoginService {
#Inject
UserDAO userDAO;
}
And in your test:
loginService.userDAO = userDAOMocked.
So, you can mock the UserDAO and pass to the LoginService test as parameter using the constructor or by setter.
Another one is by reflection (without constructor or setter), but I dislike this approach...:
Reflection
public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) throws Exception {
Field setId = instanceFieldClass.getDeclaredField(fieldName);
setId.setAccessible(true);
setId.set(instance, fieldValue);
}
And to use:
setPrivateField(loginService, "userDAO", userDAOMocked);
Is possible to specify that all setter should be autowired with one annotation?
This is my class:
#Component
public class MyClass {
private static Bean1 bean1;
//...
private static BeanN beanN;
public static Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
MyClass.bean1 = bean1;
}
//...
public static BeanN getBeanN() {
return beanN;
}
#Autowired
public void setBeanN(BeanN beanN) {
MyClass.beanN = beanN;
}
}
No. There is no such built-in annotation. Also, Spring doesn't care that your method is to be interpreted as a bean mutator (a setter). Any method can be annotated with #Autowired and Spring will try to invoke it with the appropriate arguments.
Since the whole point of Spring is dependency injection, there's no reason for you to have static fields. Just inject the bean where you need it.
I have a service which I am trying to inject across various classes in my tests but I am getting its instance as null.
My config interface:
MyService.java
public interface MyService {
public String getHostUri();
}
My implementation class of this interface: MyServiceImpl.java
public class MyServiceImpl implements MyService {
private static final String BASE_HOST_URI_CONFIG = "localhost:4444";
#Override
public String getHostUri() {
return BASE_HOST_URI_CONFIG;
}
My Spring configuration class with the bean:
#Configuration
public class AutomationSpringConfig {
#Bean
public MyService getMyService(){
return new MyServiceImpl();
}
}
My testNG class:
#ContextConfiguration(classes=AutomationSpringConfig.class ,loader =AnnotationConfigContextLoader.class)
public class BasicAutomatedTest extends AbstractTestNGSpringContextTests {
private static final Logger LOGGER = Logger.getLogger(BasicAutomatedTest.class);
#Inject
private MyService myService;
#Test
public void basicTest {
Setup setup = new Setup();
LOGGER.info(myService.getHostUri());
LOGGER.info(setup.myService.getHostUri());
}
}
My helper class in which I am not able to get the injection:
public class Setup {
#Inject
public MyService myService;
}
So when I try to get the hostUri via the setup object in the BasicAutomatedTest's basicTest method I get a NullPointerException.
So I am not able to inject the MyService bean in the Setup class.
In order to use annotations you need to specify that behaviour in your beans XML configuration file. Something like this:
<context:component-scan base-package="your.base.package"/>
<context:annotation-config/>
Hope it helps!
I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.