我正在开发一个项目,我将MyBatis注释用作持久性框架。因此,我必须为'mapper'创建一个接口,并在服务中组合映射器,如:
class XYZServiceImpl{
public XYZMapper getXYZMapper(){
return SessionUtil.getSqlSession().getMapper(XYZMapper.class)
}
}
现在,在使用Mockito对服务进行单元测试时,我正在尝试为mapper注入一个模拟器。但是因为我在一个XYZService实例中注入mock,如何模拟服务本身的方法,在这种情况下getXYZMapper()就是我想要存根的东西。虽然我有一个在服务中创建实例XYZMapper的解决方案,而不像上面的代码那样按需调用:
Class XYZServiceImpl{
XYZMapper mapper;
public void useXYZMapper(){
mapper = SessionUtil.getSqlSession().getMapper(XYZMapper.class);
}
}
但这会带来很多代码更改(当然我可以重构)但是有没有办法实现而无需进行代码更改?
在类中使用mapper实例的“纯粹主义”方法是,方法1在性能方面优于方法2吗?
编辑:这里XYZMapper是一个界面。类似的东西:
public interface XYZMapper{
@Select("SELECT * FROM someclass WHERE id = #{id}")
public SomeClass getSomeClass(int id);
}
编辑:我面临类似的情况,但有一个变化,我有一个服务,我想测试像XYZServiceImpl。现在它有一个方法getXYZDetails(),它在服务中处理了很多业务逻辑。现在,如果getXYZDetails如下所示:
public XYZDetails getXYZDetails(int id){
XYZDetails details = new XYZDetails();
details.set1Details(fetchSet1Details(id));
//Perform some business logic
details.set2Details(fetchSet2Details(id));
if(details.set2Details() != null){
for(int i = 0; i < details.set2Details().size(); i++){
flushTheseDetails(i);
}
}
.
.
}
请注意fetchSet1Details(),fetchSet2Details(),flushTheseDetails分别是公共服务,公共和私人服务。
我想知道一种方法,可以在测试getXYZDetails()时模拟/存根这些方法,从而使我能够
答案 0 :(得分:1)
您可以使用多种选项。
当方法只返回对象的外部依赖关系时,这仅适用于getXYZMapper
之类的简单方法。这可能需要创建新的XYZServiceImpl实例,例如,将mapper绑定到每个请求打开的连接。
获得类似结果的另一种方法是使用factory或service locator 像这样:
public class XYZServiceImpl {
public XYZServiceImpl(XYZMapperFactory mapperFactory) {
this.mapperFactory = mapperFactory;
}
public XYZMapper getXYZMapper() {
return mapperFactory.getMapper();
}
}
这将允许您使用返回模拟映射器的实现轻松替换test中的factory。
类似的方法可以用于将其移动到其他类或类的其他方法fetchSet1Details
,fetchSet2Details
,flushTheseDetails
。如果该方法包含复杂的(并且可能是松散相关的)逻辑,则将其移动到单独的类中是一个很好的候选者。想想这些方法的作用。通常,您可以将其中一些必要且不相关的部分移动到其他类或类中,这使得模拟它们变得更加容易。
这不是recommended,但在遗留代码中,有时作为临时解决方案非常有用。
在您的测试子类中,您需要测试并覆盖所需的方法:
@Test
public void someTest() {
XYZServiceImpl sut = new XYZServiceImpl() {
public XYZMapper getXYZMapper() {
return mapperMock;
}
public Whatever fetchSet1Details() {
return whateverYouNeedInTest;
}
}
sut.invokeMethodUnderTest();
}
您可能需要做的唯一事情是将私有方法的访问修饰符更改为package-private或protected,以便您可以覆盖它们。
此方法也在discouraged中,但您可以使用mockito间谍:
XYZServiceImpl realService = new XYZServiceImpl();
XYZServiceImpl spy = Mockito.spy(realService);
when(spy.fetchSet1Details()).thenReturn(whaeveryouneed);
when(spy.getXYZMapper()).thenReturn(mockMapper);
spy.methodUnderTest();
答案 1 :(得分:0)
我会建议&#34;纯粹主义者&#34;这样做的方法是在构造函数中接受XYZMapper
实例并将其存储在本地字段中。
在生产用途中,您可以通过例如SQLXYZMapper
,它将与您的数据库进行交互。在测试使用中,您可以传入一个可以验证与之交互的模拟对象。