Search code examples
javaunit-testingjunitmockitopowermockito

@Spy not working when annotated on two lists


This is my PersonSeviceImpl class. Here before saving any person I want to run some validators which implements PersonValidator. And also I autowired PersonStatusChangeValidator to use in a different method.

public class PersonServiceImpl implements PersonService {

    @Autowired
    PersonDao personDao;

    @Autowired
    List<PersonStatusChangeValidator> statusChangeValidators;

    @Autowired
    List<PersonValidator> personValidators;

    @Override
    public Person save(Person person) {
        for(PersonValidator validator: personValidators){
            validator.validate(person);
        }
        personDao.save(person);
        return person;
    }
}

Here I am writing test to verify whether validators are called or not.

@RunWith(PowerMockRunner.class)
public class PersonServiceImplTest {

    @Mock
    private PersonDao personDao;

    @Mock
    private PersonValidator personValidator;

    @Mock
    private PersonStatusChangeValidator statusChangeValidator;

    @Spy
    private List<PersonStatusChangeValidator> statusChangeValidators = new ArrayList<>();

    @Spy
    private List<PersonValidator> personValidators = new ArrayList<>();

    @InjectMocks
    private PersonServiceImpl personService;

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

        personValidators.add(personValidator);
        statusChangeValidators.add(statusChangeValidator);
    }

    @Test
    public void testCreatePerson() throws Exception {
        personService.save(new Person());
        verify(personValidator, times(1)).validate(any(Person.class));
    }
}

The problem is personValidators, statusChangeValidators are null by the time I'm running test. And when there is a single @Spy annotation in the test, it is working fine. Need some help to know where I'm doing wrong.


Solution

  • Your test doesn't show any mocking of statics or finals so I'm wondering why you are using @RunWith(PowerMockRunner.class)?

    I have taken your code and successfully run it with just one change: replacing @RunWith(PowerMockRunner.class) with @RunWith(MockitoJUnitRunner.class).

    So, unless there is some other aspect of your tests which require PowerMock then I'd suggest running your test case with MockitoJUnitRunner. If there is some aspect of your tests which require PowerMock then I'd suggest injecting the statusChangeValidators and personValidators instances with explicit constructor injection (i.e. what @JB Nizet suggested in his comment above) so that you do not have to rely on @InjectMocks.

    FWIW, there's an open issue against PowerMock for this inability to apply @InjectMocks for fields with the @Spy annotation.