Mockito WrongTypeOfReturnValue:findById()不能返回布尔值
我正在尝试使用Mockito通过JUnit测试测试以下方法:
@Override public List<Adoption> search(String username, Integer id) {
List<Adoption> emptySearchResult = new ArrayList<>();
if(id != null && !username.equals("") ) {
if(!this.petRepository.findById(id).isPresent()){
return emptySearchResult;
}
if(!this.appUserRepository.findByUsername(username).isPresent()){
return emptySearchResult;
}
Pet pet = this.petRepository.findById(id).orElseThrow( () -> new PetNotFoundException(id));
AppUser user = this.appUserRepository.findByUsername(username).orElseThrow( () -> new UsernameNotFoundException(username));
return this.adoptionRepository.findAllByUserAndPet(user, pet);
}
else if(id != null && username.equals("")){
if(!this.petRepository.findById(id).isPresent()){
return emptySearchResult;
}
Pet pet = this.petRepository.findById(id).orElseThrow( () -> new PetNotFoundException(id));
return this.adoptionRepository.findAllByPet(pet);
}
else if(id == null && !username.equals("")) {
if(!this.appUserRepository.findByUsername(username).isPresent()){
return emptySearchResult;
}
AppUser user = this.appUserRepository.findByUsername(username).orElseThrow( () -> new UsernameNotFoundException(username));
return this.adoptionRepository.findAllByUser(user);
}
else {
return this.adoptionRepository.findAll();
}
}
但是,我遇到了以下部分的问题:
if(!this.petRepository.findById(id).isPresent())
尽管我嘲笑了this.petRepository.findById(Id),但由于某种原因,isPresent()返回false。这是我对测试的初始化:
@Mock
private AdoptionRepository adoptionRepository;
@Mock
private PetRepository petRepository;
@Mock
private AppUserRepository appUserRepository;
private AdoptionServiceImpl service;
private Adoption adoption1;
private Adoption adoption2;
private Adoption adoption3;
private AppUser user;
private AppUser user2;
private Pet pet;
private Pet petAlteadyAdopted;
List<Adoption> allAdoptions = new ArrayList<>();
List<Adoption> userFilteredAdoptions = new ArrayList<>();
List<Adoption> petFilteredAdoptions = new ArrayList<>();
@Before
public void init() {
MockitoAnnotations.initMocks(this);
user = new AppUser("username","name","lastname","email@gmail.com","pass",ZonedDateTime.now(), Role.ROLE_USER, City.Skopje);
user2 = new AppUser("username1","name","lastname","email@gmail.com","pass",ZonedDateTime.now(), Role.ROLE_USER, City.Skopje);
Center center = new Center("a", City.Bitola,"url");
pet = new Pet("p", Type.DOG,"b", Gender.FEMALE,"d",center, ZonedDateTime.now(),"url",null,false,ZonedDateTime.now());
petAlteadyAdopted = new Pet("p", Type.DOG,"b", Gender.FEMALE,"d",center, ZonedDateTime.now(),"url",null,true,ZonedDateTime.now());
pet.setId(0);
petAlteadyAdopted.setId(1);
adoption1 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.ACTIVE,user,pet);
adoption2 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.CLOSED,user,pet);
adoption3 = new Adoption(ZonedDateTime.now(),ZonedDateTime.now(),Status.CLOSED,user2,new Pet());
allAdoptions.add(adoption1);
allAdoptions.add(adoption2);
allAdoptions.add(adoption3);
petFilteredAdoptions.add(adoption2);
petFilteredAdoptions.add(adoption1);
userFilteredAdoptions.add(adoption2);
userFilteredAdoptions.add(adoption1);
Mockito.when(this.adoptionRepository.findById(0)).thenReturn(java.util.Optional.of(adoption1));
Mockito.when(this.adoptionRepository.findById(1)).thenReturn(java.util.Optional.of(adoption2));
Mockito.when(this.petRepository.findById(0)).thenReturn(java.util.Optional.of(pet));
Mockito.when(this.petRepository.findById(1)).thenReturn(java.util.Optional.of(petAlteadyAdopted));
Mockito.when(this.appUserRepository.findByUsername("username")).thenReturn(java.util.Optional.of(user));
Mockito.when(this.appUserRepository.findByUsername("username1")).thenReturn(java.util.Optional.of(user2));
Mockito.when(this.adoptionRepository.findAll()).thenReturn(allAdoptions);
Mockito.when(this.adoptionRepository.findAllByPet(pet)).thenReturn(petFilteredAdoptions);
Mockito.when(this.adoptionRepository.findAllByUser(user)).thenReturn(userFilteredAdoptions);
Mockito.when(this.adoptionRepository.findAllByUserAndPet(user,pet)).thenReturn(userFilteredAdoptions);
Mockito.when(this.adoptionRepository.save(Mockito.any(Adoption.class))).thenReturn(adoption1);
this.service = Mockito.spy(new AdoptionServiceImpl(this.adoptionRepository, this.petRepository,this.appUserRepository));
}
因此,以下测试即使应该通过也会失败:
@Test
public void searchTest2() {
List<Adoption> adoptionList = this.service.search("",0);
Assert.assertEquals(petFilteredAdoptions,adoptionList);
}
为了解决此问题,我尝试模拟isPresent()方法:
Mockito.when(this.petRepository.findById(0).isPresent()).thenReturn(true);
但我收到以下异常:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: findById()不能返回布尔值。findById()应返回 可选* 如果您不确定为什么会出现上述错误,请继续阅读。由于 出现上述语法问题的原因可能是:
- 此异常可能在错误编写的多线程测试中发生。有关并发的限制,请参阅Mockito常见问题 正在测试。
- 间谍使用WHEN(spy.foo()).Then()语法进行存根。更安全的做法是铲除间谍-
- 使用doReturn|Throw()方法系列。有关Mockito.spy()方法的javadoc的更多信息。
我还尝试了以下变体:
Mockito.doReturn(true).when(this.petRepository.findById(0)).isPresent();
但随后我收到以下异常:
org.mockito.exceptions.misusing.UnfinishedStubbingException: 此处检测到未完成的存根: --mk.finki.ukim.milenichinja.ServiceTests.AdoptionServiceFilterTests.init(AdoptionServiceFilterTests.java:87)
的>;例如,可能缺少TenReturn()。正确插桩的示例: When(mock.isOk()).thenReturn(True); When(mock.isOk()).thenThrow(异常); doThrow(exception).when(mock).someVoidMethod();提示:
- 缺少thenReturn()
- 您正在尝试存根Final方法,该方法不受支持
- 在"thenReturn"指令完成之前,您正在截断内部另一个模拟的行为
有什么想法可以解决此问题吗?
解决方案
在init
方法中,您正在清除模拟实例this.petRepository
上的findById
以返回非模拟可选,这很好。在您的新测试中,您试图为isPresent
设置返回值,但您不能这样做,因为Optional不是模拟的。如果您想覆盖每个测试的行为,您将需要存根findById
以返回不同实例的可选。因此,这是正确的,尽管它与init
中显示的完全相同,因此它无法告诉您测试失败的原因。
Mockito.when(this.petRepository.findById(0))
.thenReturn(java.util.Optional.of(pet));
Mockito的工作方式是创建模拟对象,该对象子类化一个类并覆盖每个方法。被覆盖的方法是与静电(线程本地)基础设施交互的方法,允许您使用when
语法。这里重要的是when
忽略了它的参数,而是尝试模拟您用模拟进行的最后一次交互。您可以在SO问题How does mockito when() invocation work?和How do Mockito matchers work?中找到更多信息。
当您看到此调用时:
Mockito.when(this.petRepository.findById(0))
.thenReturn(java.util.Optional.of(pet));
然后它会按您的预期工作:
petRepository
是模拟的,findById
大概是可重写的方法,Mockito记录您用参数0
调用它的事实。findById
还没有任何行为存根,因此它使用默认值,返回null
。when
不在乎它刚刚收到null
,因为null
没有告诉它调用了什么方法来获取null
。相反,它会回顾其最新记录(findById(0)
),并返回一个具有您期望的thenVerb
方法的对象。- 您调用
thenReturn
,因此Mockito设置petRepository
返回您创建并传入的可选实例。
但当您尝试此呼叫时:
Mockito.when(this.petRepository.findById(0).isPresent()).thenReturn(true);
那么最近的交互不是isPresent
,而是findById
,所以Mockito假定您想要findById(0)
到thenReturn(true)
,并抛出WrongTypeOfReturnValue。Optional不是模拟的,所以与它交互不会让Mockito记录它的交互或重播您的行为。不管它有什么价值,我也不建议模仿它:Optional is a final class,虽然Mockito has recently added some support for mocking final types,但Optional足够简单和直接,只返回您想要的可选实例比试图模仿它更有意义。
话虽如此,您的代码看起来是正确的;只要PetRepository是一个接口,我就看不到任何关于您的方法的外观或您的模拟的外观会导致this.petRepository.findById(0)
返回缺少的可选的内容。事实上,我甚至不知道您会在什么地方创建为它返回的缺少的可选,所以我只能猜测您在测试中使用的真实对象比您想象的要多。
相关文章