3

Here's the problem: I have a rather complex class B with lots of @Inject defined in it (among them a class C). An instance of this class is injected into another class A, which I want to be tested. The idea is that I want to inject a mock of class B into A - and I want it to be injected by spring to have the init-method be executed after the instance is created (so no @InjectMock here to have an alternative injection).

Here's an example that is boiled down to three classes Bla, Blub and Blublub. What I want to do is have a mock of Blub and inject this instance into BlubBlub - and I want to ignore the existence of Bla.

*Edited*

The main point is that I want a context to consists of a mock of class Blub and an instance of class BlubBlub.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MockInjectionTest.TestApp.class)
public class MockInjectionTest {
@Inject
public Blub blub;

@Inject
public BlubBlub blubblub;

@Configuration
public static class TestApp {
    @Bean
    Blub getBlub() {
        return mock(Blub.class);
    }

    @Bean
    BlubBlub getBlubBlub() {
        return new BlubBlub();
    }
}

@Test
public void testBlub() {
    Assert.assertNotNull(blub);
}

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

// the classes
public static class Bla {
}

public static class Blub {
    @Inject
    public Bla bla;
}

private static class BlubBlub {
    @Inject
    public Blub blub;
}
}

Problem: when I define a mock of Blub either by using @Mock or by calling mock(Blub) explicitly in a @Bean method I get the following error when the ApplicationContext is instantiated (no matter whether I use xml-config or annotation-based bean definitions).

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'getBlub': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: public Bla Blub.bla;

so apparently Spring still want to instantiate the original class instead of just taking my provided instance. This seams to be necessary to create the context (if I create the context by hand and pull the bean with ctx.getBean() it dumps already in the context construction).

Coming from Guice I would simple bind my mocked instance into the module and everything would be fine.

Help is much appreciated - sounds like a standard problem but I couldn't find a simple solution.

thanks and regards, fricke

fricke
  • 1,330
  • 1
  • 11
  • 21
  • It's a shame this one wasn't answered, I have the same problem – john16384 Jan 15 '15 at 07:29
  • have a look at https://stackoverflow.com/questions/41880210/how-to-disable-spring-autowiring-for-a-certain-bean , this helped me with a similar problem – fsch Feb 12 '21 at 10:59

2 Answers2

0

Change your configuration to

@Configuration
public static class TestApp {

@Bean
Blub getBlub() {
    return mock(Blub.class);
}

@Bean
BlubBlub getBlubBlub() {
    return new BlubBlub();
}

@Bean
Bla getBla() {
   return mock(Bla.class)
}

}

What Spring is complaining about is the fact that no Bla implementation is present in the application context and there for no injection can be performed

geoand
  • 60,071
  • 24
  • 172
  • 190
  • Hi @geoand, thanks for the quick reply; I had it like that, but assume I have ten such **Bla**-classes that are supposed to be injected, then I end up with an unwieldy amount of "pure" boilerplate that is only needed because Spring pulls things into the context I don't want to have in there. And they aren't even injected after all. I want a context to consists of a mock of class Blub and an instance of class BlubBlub. Perhaps this is not possible with Spring and it is one of the points where it is good to stick with Guice. – fricke Mar 12 '14 at 10:22
  • That is a good question. If that is the case you should checkout the Springockito project that will pretty much work like the @InjectMock you are used to. http://stackoverflow.com/q/19354473/2504224 – geoand Mar 12 '14 at 10:32
  • Hi @geoand, unfortunately Springockito does help getting around the problem that Spring while building the context checks for all Classes that are "@Inject-ed. And there it breaks. – fricke Mar 14 '14 at 19:04
  • Hi @geoand, like when I declare "@ReplaceWithMock "@Inject private MyBean innerBean; Springockito scans the class MyBean and if MyBean has "@Inject-ed classes that are not declared in the context (like above) then the construction of the context aborts. – fricke Mar 15 '14 at 17:14
0

You can return a null bean like this :

@Configuration
public static class TestApp {
    @Bean
    Blub getBlub() {
        return mock(Blub.class);
    }
    @Bean
    Bla getBla() {
        return null;
    }

    @Bean
    BlubBlub getBlubBlub() {
        return new BlubBlub();
    }
}

This will avoid injection on Bla instances, since there is none. This is not ideal when you have many @Injects, but this works.

Another solution wich is not ideal also, is to have your bean implement an interface, and mock the interface instead of the implementation class.

I tried springockito, this would work for your case, but in one of my test case wich was about testing a custom BeanDefinitionRegistryPostProcessor, it was messing to much the normal spring behavior in think.

Mirak
  • 59
  • 9