33

I am using Mockito for unit testing. I am wondering if its possible to send Parametrized input parameters with as in Junit testing
e.g

@InjectMocks
MockClass mockClass = new MockClass();

@Test
public void mockTestMethod()
    {
    mockClass.testMethod(stringInput); 
// here I want to pass a list of String inputs 
// this is possible in Junit through Parameterized.class.. 
// wondering if its can be done in Mockito
    } 
SeanC
  • 15,695
  • 5
  • 45
  • 66
spal
  • 731
  • 3
  • 9
  • 20

4 Answers4

34

In JUnit, Parameterized tests use a special runner that ensure that the test is instantiated multiple times, so each test method is called multiple times. Mockito is a tool for writing specific unit tests, so there is no built-in ability to run the same test multiple times with different Mockito expectations.

If you want your test conditions to change, your best bet is to do one of the following:

  • Parameterize your test using JUnit, with a parameter for the mock inputs you want;
  • Run a loop of different parameters in your test, which unfortunately avoids the "test one thing per method" philosophy
  • Extract a method that actually performs the test, and create a new @Test method for each mock you want.

Note that there's no prohibition on using mock objects as @Parameterized test parameters. If you're looking to parameterize based on mocks, you can do that, possibly creating the mock and setting the expectations in a static method on the test.


Note about runners: This Parameterized test runner conflicts with Mockito's MockitoJUnitRunner: Each test class can only have one runner. You'll want to switch to @Before and @After methods or a Mockito JUnit4 rule for your setup, if you use them both.

As an example, compressed from a different answer that explains more about Parameterized runners versus JUnit rules and lifting from the JUnit4 Parameterized Test doc page and MockitoRule doc page:

@RunWith(Parameterized.class)
public class YourComponentTest {
    @Rule public MockitoRule rule = MockitoJUnit.rule();
    @Mock YourDep mockYourDep;

    @Parameters public static Collection<Object[]> data() { /* Return the values */ }

    public YourComponentTest(Parameter parameter) { /* Save the parameter to a field */ }

    @Test public void test() { /* Use the field value in assertions */ }
}
Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • I'm sure this is the definitive answer. But would you care to comment on the possibility at (http://stackoverflow.com/questions/27745691/how-to-combine-runwith-with-runwithparameterized-class/27750897#comment68773212_27750897) of combining `@RunWith(Parameterized.class)` with another `@RunWith`. I tried to understand how to use this technique with `@RunWith(MockitoJUnitRunner.class)`, but it was beyond me (see my comments there). I have doubts in view of what you say, but I thought I'd ask... – mike rodent Nov 24 '16 at 07:37
  • @mike Yes, you could create a parameterized Mockito runner using that technique, but while that post may be particularly useful for classloader-manipulating runners or runners that use dynamic tests, it is purely overkill in almost all Mockito situations because a `@Rule` would achieve this in a more modern idiomatic way. (MockitoJUnitRunner predates JUnit Rules.) If you want to see about combining them, it'd go better in another question—it wouldn't fit in a comment box and is no good general answer to either question. – Jeff Bowman Nov 24 '16 at 17:11
  • Thanks. You mean start a new question to ask for an explicit example of using @Rule? I have now done this: http://stackoverflow.com/questions/40793464/parameterized-testing-with-mockito-by-using-junit-rule. Hope you have a moment... – mike rodent Nov 24 '16 at 19:33
13

If you are stuck with an older version of mockito where MockitoRule isn't available, the other possibility is to initialize the mocks explicitely with MockitoAnnotations.initMocks:

@RunWith(Parameterized.class)
public class YourComponentTest {        
    @Mock YourDep mockYourDep;

    @Parameter
    public Parameter parameter;

    @Parameters public static Collection<Object[]> data() { /* Return the values */ }

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

    @Test public void test() { /* Use the field value in assertions */ }
}
kavai77
  • 6,282
  • 7
  • 33
  • 48
7

You can use the JUnitParamsRunner. Here's how I do it:

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.util.Arrays;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;

@RunWith(value = JUnitParamsRunner.class)
public class ParameterizedMockitoTest {

    @InjectMocks
    private SomeService someService;
    @Mock
    private SomeOtherService someOtherService;

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

    @Test
    @Parameters(method = "getParameters")
    public void testWithParameters(Boolean parameter, Boolean expected) throws Exception {
        when(someOtherService.getSomething()).thenReturn(new Something());
        Boolean testObject = someService.getTestObject(parameter);
        assertThat(testObject, is(expected));
    }

    @Test
    public void testSomeBasicStuffWithoutParameters() {
        int i = 0;
        assertThat(i, is(0));
    }

    public Iterable getParameters() {
        return Arrays.asList(new Object[][]{
                {Boolean.TRUE, Boolean.TRUE},
                {Boolean.FALSE, Boolean.FALSE},
        });
    }
}
Jonas Bausch
  • 185
  • 3
  • 12
1

What solved it for me was:

  1. Class level annotation of @ExtendWith(MockitoExtension.class)
  2. Annotate each mock object with @Mock
  3. @InjectMocks on the test class. Or a setup method annotated with @BeforeEach where you initialise the class to be tested.
  4. if you need the @test annotation, make sure you import org.junit.jupiter.api.Test. org.junit.test will not work!

I'm using mockito version 4.