views:

350

answers:

3

My code:

@Component
public class A {
    @Autowired
    private B b;

    public void method() {}
}

public interface X {...}

@Component
public class B implements X {
    ...
}

I want to test in isolation class A. Do I have to mock class B? If yes, how? Because it is autowired and there is no setter where i could send the mocked object.

+4  A: 

You can inject the field via reflection using Spring's ReflectionTestUtils.setField (or the junit extension PrivateAccessor) or you can create a mock application context and load that. Though for a simple unit (non-integration) test, I favor using reflection for simplicity.

Jeff Storey
A: 

This forum discussion makes sense to me. You can declare your private member b as a type of InterfaceB which is implemented by the class B (ie: service-oriented) then declare a MockB class would also implement the same interface. In your test environment application context, you declare MockB class and your production application context you declare the normal B class and in either case, the code for class A does not need to be changed since it will be auto-wired.

CitizenForAnAwesomeTomorrow
With @Autowire you don't declare your beans in application context, they're just here in the classpath. So, this solution doesn't work.
Damien
This solution works if you wire the bean byType (in 2.5) or declare a qualifier (3.0). In Spring 2.5 you can autowire to a bean declared in your XML Application Context configuration as can be read in documentation http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-autowire. In Spring 3.0 they still allow you to do this with the @Qualifier annotation (documented at http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-autowired-annotation).
CitizenForAnAwesomeTomorrow
+2  A: 

I want to test in isolation class A.

You should absolutely mock B, rather than instantiate and inject an instance of B. The point is to test A whether or not B works, so you should not allow a potentially broken B interfere with the testing of A.

That said, I highly recommend Mockito. As mocking frameworks go, it is extremely easy to use. You would write something like the following:

@Test
public void testA() {
    A a = new A();
    B b = Mockito.mock(B.class); // create a mock of B
    Mockito.when(b.getMeaningOfLife()).thenReturn(42); // define mocked behavior of b
    ReflectionTestUtils.setField(a, "b", b); // inject b into the B attribute of A

    a.method();

    // call whatever asserts you need here
}
James Earl Douglas