5

I have an AuthenticationManager.authenticate(username,password) method that gets called in someMethod of a SomeService under test. The AuthenticationManager is injected into SomeService:

@Component
public class SomeService {
    @Inject
    private AuthenticationManager authenticationManager;

    public void someMethod() {
        authenticationManager.authenticate(username, password);
        // do more stuff that I want to test
    }
}

Now for the unit test I need the authenticate method to just pretend it worked correctly, in my case do nothing, so I can test if the method itself does the expected work (Authentication is tested elsewhere according to the unit testing principles, however authenticate needs to be called inside that method)

So I am thinking, I need SomeService to use a mocked AuthenticationManager that will just return and do nothing else when authenticate() gets called by someMethod(). How do I do that with PowerMock (or EasyMock / Mockito, which are part of PowerMock)?

Or is there another way to tell Spring to use some Mock instead of the real deal, when injeeting the AuthenticationManager into SomeService?

Or is my design wrong?

Thanks!


Edit - found a solution, would like to hear comments

So I found a nice way to inject a mock without having to use a setter or package level:

@RunWith(PowerMockRunner.class)
public class TestOrderService {

    @Mock
    private AuthenticationManager authenticationManager;

    @InjectMocks 
    private OrderService orderService = new OrderService();

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        Mockito.doNothing().when(this.authenticationManager).authenticate(null, null);
    }

    @Test
    public void testOrderService() {
        orderService.save(null, null, null);
    }
}

with OrderService like this:

public void save(String username, Order order) {
    authenticationManager.authenticate(username, password);
    // do something with the order...
}

Now the save method will call authenticate on the mock and the mock does nothing leaving me free to test the actual work inside my service method (which actually is calling a manager method but that was not the point ;) )

1 Answer 1

2

I personally don't use spring on the unit testing side (except maybe a single test to verify that the application context loads properly). Instead I use setter methods to manually wire the mocks (actually I use project lombok's @Setter annotation), OR if I'm using @Autowired I make the member variables package scoped so the unit test can directly set the values.

I don't see how you're going to get things like Mockito to work via dependency injection, because you'll always be new-ing your object inside the unit test and then creating mock behaviors for the methods. Also, you don't lose much by doing it this way, because you'll only need to setup direct dependencies of the class under test (because they're mocks), which is much easier than what spring DI does, which is recursive dependency creation and wiring on real objects.

4
  • Good point actually. Was wondering if I couldn't reduce the testing time by removing the spring context from the equation... How about using reflections to access the private beans? Or maybe there's a frameworks that wraps this behaviour? I don't feel comfortable creating a security risk by making the beans package scope and I don't like to add setters just so I can unit test a class when I don't need them otherwise...
    – Pete
    Commented Feb 14, 2012 at 19:46
  • That's why I like Lombok. You add an annotation and it generates the setter method. The setter method is still there but I don't have to look at it every time I edit the file so it's much less annoying
    – Kevin
    Commented Feb 15, 2012 at 3:04
  • Found a nice solution without a setter or spring context. Would like to hear your opinion.
    – Pete
    Commented Feb 15, 2012 at 8:37
  • 1
    Nice I hadn't seen @InjectMocks before. I'm going to start using that instead...
    – Kevin
    Commented Feb 16, 2012 at 5:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.