我们可以在测试服务本身的同时模拟服务中的方法吗?



我正在做一个项目,我使用MyBatis注释作为持久性框架。因此,我必须为"映射器"创建一个接口,并在服务中组合映射器,如:

class XYZServiceImpl{
    public XYZMapper getXYZMapper(){
        return SessionUtil.getSqlSession().getMapper(XYZMapper.class)
    }
}

现在,当使用Mockito对服务进行单元测试时,我试图为映射器注入一个mock。但是,由于我在XYZService的实例中注入mock,如何模拟服务本身的方法,在这种情况下,getXYZMapper()是我试图存根的。虽然我已经得到了在服务中创建实例XYZMapper的解决方案,而不是像上面的代码那样按需调用,但它做的事情如下:

  Class XYZServiceImpl{
        XYZMapper mapper;
        public void useXYZMapper(){
            mapper = SessionUtil.getSqlSession().getMapper(XYZMapper.class);
        }
    }

但是这会带来大量的代码更改(当然我可以重构),但是有没有一种方法可以在不进行代码更改的情况下实现?

还有,在类中拥有映射器实例的"纯粹主义"方式是方法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(),从而使我能够

您可以使用以下几个选项。

<标题>注入依赖h1> 只适用于简单的方法,如getXYZMapper,当方法只返回对象的外部依赖时。这可能需要创建新的XYZServiceImpl实例,例如,如果mapper绑定到每个请求打开的连接。

在对象

中封装方法行为实现类似结果的另一种方法是使用工厂或服务定位器这样的:
public class XYZServiceImpl {
    public XYZServiceImpl(XYZMapperFactory mapperFactory) {
        this.mapperFactory = mapperFactory;
    }
    public XYZMapper getXYZMapper() {
        return mapperFactory.getMapper();
    }
}

这将允许您轻松地将测试中的工厂替换为返回模拟映射器的实现。

类似的方法可以用于其他方法fetchSet1Details, fetchSet2Details, flushTheseDetails,将它们移动到其他类或类。如果方法包含复杂的(并且可能是松散相关的)逻辑,则将其移动到单独的类中是一个很好的选择。想想这些方法的作用。通常,您可以将它们的一些重要且不相关的部分移到其他类中,这使得嘲弄它们变得更加容易。

<标题> 子类

不建议这样做,但在遗留代码中,有时作为临时解决方案非常有用。

在您的测试子类中,您将在测试下分类并覆盖您需要的方法:

@Test
public void someTest() {
   XYZServiceImpl sut = new XYZServiceImpl() {
       public XYZMapper getXYZMapper() {
           return mapperMock;
       }
       public Whatever fetchSet1Details() {
           return whateverYouNeedInTest;
       }
   }
   sut.invokeMethodUnderTest();
}

你唯一需要做的就是将private方法的访问修饰符更改为package-private或protected,这样你就可以覆盖它们了。

<标题> 从事间谍活动

这个方法也不鼓励使用,但是你可以使用mockito间谍:

XYZServiceImpl realService = new XYZServiceImpl();
XYZServiceImpl spy = Mockito.spy(realService);
when(spy.fetchSet1Details()).thenReturn(whaeveryouneed);
when(spy.getXYZMapper()).thenReturn(mockMapper);
spy.methodUnderTest();
 

我建议这样做的"纯粹"方式是在构造函数中接受XYZMapper实例并将其存储在本地字段中。

在生产使用中,你可以传递一个例如SQLXYZMapper,它将与你的数据库交互。在测试使用中,您可以传入一个可以验证与之交互的模拟对象。

相关内容

  • 没有找到相关文章

最新更新