alibaba / testable-mock

换种思路写Mock,让单元测试更简单
https://alibaba.github.io/testable-mock/
MIT License
1.83k stars 310 forks source link

跨模块interface引用问题 #182

Closed liangjiawei closed 2 years ago

liangjiawei commented 3 years ago

请教一个问题,单元测试类A,类A有一个私有成员spring bean 接口B, B的实现类C,但是C是在另一个模块下。A模块下写单元测试无法初始化接口B。这种情况怎么处理的?

linfan commented 3 years ago

参考一下文档的包路径映射功能

liangjiawei commented 3 years ago

参考一下文档的包路径映射功能

我读了文档,但是有点不理解,例如:被测类使用接口为DemoService,第三方包实现类为DemoServiceImpl,那我使用的DemoServiceImplMock需要implement DemoService吗?

linfan commented 3 years ago

不用,原理上来说,TestableMock只是需要将被测类、测试类和Mock容器类之间建立关联,然后就可以自动进行调用匹配和替换了。目前这个关联是通过名称约定来建立的,就是默认在同包下命名符合业务类Test.Mock或者业务类Mock的类型就会被识别为Mock容器类。

只是当由于某些情况导致无法用默认路径和名称定义Mock容器类是时候,就要用到一些额外的手段,包括使用@MockWith注解和配置文件(添加包路径映射)。

liangjiawei commented 3 years ago

那这种就带来了另外一个问题,被测类里面这个DemoService引用需要在使用前赋值, 如果这个引用赋值,引用本身是null, 那 demoService.doSomething() 后面的方法根本不会走

我使用的是这个

PrivateAccessor.set(被测类, "demoService", new DependServiceImplMock());

就会出现报错

java.lang.IllegalArgumentException : Can not set DemoService  field 被测类.demoService to DemoMock

这个被测类里面引用怎样才能实现赋值。

linfan commented 3 years ago

不用创建DependServiceImplMock类替换DemoService里的成员对象。Testable的Mock机制是直接替换调用,而不是替换对象,Mock类的作用仅仅是一个存放要替换的Mock方法集合的容器,所以称为是“Mock容器类”。

比如DemoService类里有一个dependService.something()调用,希望在单元测试的时候被替换掉。只需要创建DemoServiceMock类型(包含DemoService类所需的Mock方法集合),在里面定义一个名字和参数与something()相同的方法,加上@MockMethod(targetClass = DependService.class)注解,单元测试运行的时候就会自动把所有DemoService类里对DependService类型something()方法的调用统统替换掉。不需要考虑如何注入Mock对象的问题。

这种Mock方式和主流的Mock工具差别比较大,如果已经习惯了基于对象的Mock方式,可能刚开始会不太适应。

liangjiawei commented 3 years ago

@linfan 我的问题还没解决,按照上面的您的回答。我不对被测试类里面的引用做初始化,但是如果不做的话,那些引用都是null,直接报空指针错误,后面的demoService.doSomething() 根本不会走

linfan commented 3 years ago

可以用PrivateAccessor+OmniConstructor直接在测试用例创建被测对象以后,把其中的成员字段直接初始化赋值。

ypcGitHub commented 3 years ago

接口Mock我今天也碰到了Mock失败的问题。

我的问题是Mock的方法出入参和原接口不一致导致,修改一致后Mock成功。

被测试类中的私有变量(特指接口)是不需要在测试类中初始化的,直接在测试类中Mock该接口的方法就行。