44

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.

axtavt
  • 239,438
  • 41
  • 511
  • 482
mike27
  • 965
  • 3
  • 12
  • 22

4 Answers4

83

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
}
earldouglas
  • 13,265
  • 5
  • 41
  • 50
  • 11
    With the new version of Mockito, I'd use the `@InjectMocks` annotation on the declaration of `A` and get rid of the reflection `setField(..)` – Snekse Dec 07 '11 at 23:31
  • But a is a bean, i.e. a proxy (created with AOP). This will not succeed. I have tried something similar and the error was that field b could not be found on A (because it is a proxy). – Mike Argyriou Sep 19 '14 at 09:02
  • i understand the wanting to test of isolation of class A but why would B absolutely have to be mocked? there's no way you can have A work if B is broken. – dtc May 30 '16 at 02:36
  • @dtc That's a good point. My views on mock objects have evolved since I answered this question, and these days I would instead find a way to decouple A from B in the first place. – earldouglas Jun 01 '16 at 02:15
18

Here's an example of how I got my tests working with Spring 3.1, JUnit 4.7, and Mockito 1.9:

FooService.java

public class FooService {
    @Autowired private FooDAO fooDAO;
    public Foo find(Long id) {
        return fooDAO.findById(id);
    }
}

FooDAO.java

public class FooDAO {
    public Foo findById(Long id) {
        /* implementation */
    }
}

FooServiceTest.java

@RunWith(MockitoJUnitRunner.class)
public class FooServiceTest {
    @Mock private FooDAO mockFooDAO;
    @InjectMocks private FooService fooService = new FooService();

    @Test public final void findAll() {
        Foo foo = new Foo(1L);
        when(mockFooDAO.findById(foo.getId()).thenReturn(foo);

        Foo found = fooService.findById(foo.getId());
        assertEquals(foo, found);
    }
}
Mike Partridge
  • 5,128
  • 8
  • 35
  • 47
  • 6
    If not using `MockitoJUnitRunner`, important to remember in `FooServiceTest`: `@Before public void initMocks() { MockitoAnnotations.initMocks(this); }` – TalkLittle Feb 12 '13 at 16:04
15

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
  • 56,312
  • 72
  • 233
  • 406
0

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.

Aaron
  • 999
  • 2
  • 9
  • 21
  • 1
    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 Sep 07 '10 at 20:22
  • 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). – Aaron Sep 07 '10 at 22:12