如何模拟一个bean的某些方法调用,但在同一测试类中的其他方法调用中使用已定义的bean?

时间:2019-04-09 13:42:45

标签: java spring unit-testing junit mockito

我正在尝试测试在TestConfiguration中具有指定实现的服务接口。服务接口将注入DAO接口,该接口在TestConfiguration中也具有指定的实现。服务的实现定义为将在生产中使用的实际服务,而测试DAO是该接口的新的自定义实现。

在95%的情况下,我想使用Test DAO实施。但是,在某些测试中,我想覆盖DAO的功能。由于我想重写DAO的实例很少,因此我只想在有条件的情况下使用Mockito进行模拟,而无需创建DAO接口的另一种实现(对于从DAO返回空列表等极端情况)。理想情况下,它们应该在专门用于测试服务的类中。

我尝试在Test类的DAO Bean上使用@Spy批注。我尝试在DAO bean上使用@MockBean。我尝试使用Mockito的doReturn以及何时使用功能覆盖DAO的默认实现,但是,我总是从TestConfiguration中定义的Test DAO实现中获取结果。

由于是公司代码,所以我确实更改了正在执行的操作的文本,但这正是我要尝试执行的操作。

我的TestConfiguration的定义如下

@TestConfiguration
public class TestAppConfiguration {

    @Bean
    public PersonService personService() {
        return new PersonServiceImpl(personDao());
    }  

    @Bean
    public PersonDao personDao() {
        return new TestPersonDaoImpl();
    }
}

我的服务实现如下

public class PersonServiceImpl implements PersonService {
    private Logger logger = LoggerFactory.getLogger(PersonServiceImpl.class);

    private PersonDao personDao;

    public PersonServiceImpl(PersonDao personDao){
        this.personDao = personDao;
    }

    @Override
    public List<PersonDto> getAllPersons() {
        return personDao.findAll().stream()
                .map(person -> PersonDtoFactory.getPersonDto(person))
                .collect(Collectors.toList());
    }

    @Override
    public PersonDto getPersonById(Long id) {
       return PersonDtoFactory.getPersonDto(personDao.findById(id));

    }
}

我的测试DAO实施如下

public class TestPersonDaoImpl implements PersonDao {

    @Override
    public List<PersonModel> findAll() {
        return getPersons();
    }

    @Override
    public List<PersonModel> findById(id) {
        return getPersons().stream()
                       .filter(person -> person.getId() == id)
                                   .collect(Collectors.toList());
    }

    private List<PersonModel> getPersons() {
        List<PersonModel> personList = new ArrayList<>();
        personList.add(new PersonModel(1L, "Susan");
        personList.add(new PersonModel(2L, "Jerry");
        personList.add(new PersonModel(3L, "Tom");
        return personList;
    }
}

然后是我的服务测试类

@RunWith(SpringRunner.class)
@Import(TestAppConfiguration.class)
public class ApuMonitoringServiceTests {
    //We won't test web socket functionality in this test class.
    @Autowired
    private PersonService personService;

    @MockBean //want to overwrite in the test only when specified in the test, otherwise, use default TestPersonDaoImpl bean.
    private PersonDao personDao;

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

    @Test
    public void getAllPersons() {
        assert(personService.getAllTests().size() > 0);
    }

    @Test
    public void getPersonById() {
        assert(personService.getPersonById(1L).getName().equals("Susan"));
    }

    @Test
    public void getAllPersons_NoPersons(){
        //Mock the DAO call since it will have all of the test data by default
        doReturn(new ArrayList<Apu>()).when(personDao).findAll();
        //when(personDao.findAll()).thenReturn(new ArrayList<>());  <-- this also doesn't work
        assert(personService.getAllPersons().size() == 0);
}

预期所有测试都会通过,并且在服务实现中调用DAO调用时将被覆盖。实际结果是前两个测试通过了,而第三个测试失败了,因为它没有覆盖dao调用。

1 个答案:

答案 0 :(得分:1)

使用@MockBean,您将获得一个注入的模拟实例。

使用@Spy,您的dao将不会被注入服务

您需要@SpyBean ..您将获得注入以及默认情况下实现的所有方法的调用。