5
votes

I am writing unit tests for the following class

Class to Be Tested:

public class RandomManager {
        @Autowired
        private ApplicationContext context;

        @Autowired
        private ClassA objectA;

        public void methodToBeTested() {
            objectA.methodToBeVerified(context.getBean(Random.class,"Yaswanth","Yaswanth"));
        }
    }

Below is the test class:

public class RandomManagerTest {

    @Mock
    private ClassA objectA;

    @Mock
    private ApplicationContext context;

    @InjectMocks
    private RandomManager randomManager;

    @BeforeTest
    public void before() {
        MockitoAnnotations.initMocks(this);
        doReturn(any(Random.class)).when(context)
            .getBean(any(Class.class), any(), any());
    }

    @Test
    public void methodToBeTestedTest() {
        Random randomObject = new RandomObject("Yaswanth", "Yaswanth");
        randomManager.methodToBeTested();
        verify(objectA).methodToBeVerified(randomObject);
    }
}

The above code fails in the before method when I am trying to stub the applicationContext mock. I get the following error.

You cannot use argument matchers outside of verification or stubbing. Examples of correct usage of argument matchers: when(mock.get(anyInt())).thenReturn(null); doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject()); verify(mock).someMethod(contains("foo"))

This message may appear after an NullPointerException if the last matcher is returning an object like any() but the stubbed method signature expect a primitive argument, in this case, use primitive alternatives. when(mock.get(any())); // bad use, will raise NPE when(mock.get(anyInt())); // correct usage use

Also, this error might show up because you use argument matchers with methods that cannot be mocked. Following methods cannot be stubbed/verified: final/private/equals()/hashCode(). Mocking methods declared on non-public parent classes is not supported.

Can anyone please help me understand what am I doing wrong in the above code?

Note: I am using TestNG and Mockito. I can extend AbstractTestNGSpringContextTests and use a spring-test.xml, declare my beans and autowire applicationContext. I feel that is an overkill for my use case. I need to just mock applicationContext's getBean method.

1
Apologies for the formatting. I haven't been able to submit the question for some weird reason. Hence the ugly format.yaswanth
I don't really understand why you would mock anything in Spring based test (except maybe the http layer using MockMVC). When you use @RunWith(SpringJUnit4ClassRunner.class) you can load all you spring beans, and they will be wired together, and you test the actual code, rather than stubs that return something defined in the test, and never actually execute the code inside dependent beans. Mocking is a great tool when used right, but I have never used it for Spring based test, and when I have seen others do it, it has always been a mess.Klaus Groenbaek
I think @KlausGroenbaek is correct. Instead of trying to mock the Spring ApplicationContext, use it. Create a test application context that returns the appropriate beans / mocks as needed for your test.Michael Peacock
@KlausGroenbaek @RunWith is not available with TestNG and AbstractTestNGSpringContextTests should be used instead.juherr
You are right, I forgot it was TestNG. I prefer JUnit with Spring, as it is just better integrated. Although there are some features like parameterized tests that are easier in TestNG. There is a paradigme difference between the two, because each JUnit test is run on a new instance of the Test class, where TestNG reuses the same instance for multiple tests, so you have to be careful if you use local variables.Klaus Groenbaek

1 Answers

5
votes

The issue comes from doReturn(any(Random.class)) where you are not allowed to use any(). Just replace it by a real instance.