Mockito和Groovy单元测试说明

单元测试说明

目前单元测试使用spring-boot-starter-test自带的Junit+Mockito框架,使用PowerMock针对Mockito进行功能增强。

单元测试需要保证以下几点:

  1. 不依赖外部系统
  2. 单元测试足够“单元”,避免流程过长的测试逻辑
  3. 测试方法的数据操作不影响真实数据源的数据现状
  4. 每个测试方法都要有verify或者Assert的验证或断言的操作,否则都是无效的测试方式
  5. 一些依赖外部系统的调用,或不需要每次单元测试都执行的测试类或测试方法,及时使用@Ignore,避免mvn test时执行单元测试异常
Mockito的问题
  1. mock数据不友好,数据结果对比也不友好的问题。
  2. 不支持final/static的类或方法的mock操作(已使用PowerMock增强字节码方式解决)
    • 但PowerMockRunner会重新加载Spring Context,如果配合SpringRunner使用,在集成单元测试阶段,
      会有Context重复加载问题。例如xxljob的端口被占用,因为SpringRunner在集成测试中会使用context缓存,
      以减少每个测试类重复加载上下文的消耗。
  3. 后续会进行其他单元测试框架调研的工作,主要考虑mock数据的生成和结果对比的友好程度。
Mockito基本使用说明

Mockito主要针对mock实际对象的行为,进行bean之间的依赖解耦处理。主要通过以下几个注解和方法进行mock操作。

1. @InjectMocks

@InjectMocks标记一个对象需要进行mock对象的注入,mock对象指被 @Mock 或者 @Spy 注解修饰的bean(后面说明这两个注解)。

@InjectMocks 修饰的对象一定是非接口对象,否则无法确定bean在初始化时需要mock哪些属性或方法。

@InjectMocks 注入mock对象的方式有两种:

  1. 添加 MockitoAnnotations.initMocks(this); 表示改测试类需要初始化mock对象并注入到 @InjectMocks 对象中。
  2. 如果使用了PowerMockRunner,则PowerMock会自动进行mock对象的注入,无须额外添加注入。测试类继承 com.jdh.fuhsi.BasePowerMockRunner 即可。
    (实际就是给类添加 @RunWith(PowerMockRunner.class)

例子1:CiticbankPushCustomerServiceImplTest

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest
@Transactional
@Rollback
public abstract class BaseSpringJUnitRunner {
}

public class CiticbankPushCustomerServiceImplTest extends BaseSpringJUnitRunner {
    @InjectMocks
    private CiticbankPushCustomerServiceImpl citicbankPushCustomerService;
    @Mock
    private CiticbankHttpServerTemplate citicbankHttpServerTemplate;
    @Spy
    private CiticbankRegistNotifyServiceImpl citicbankRegistNotifyService;
    @Spy
    private JdhHttpServer jdhHttpServer;
    @Before
    public void setUp() {
        // 注入@Mock,@Spy对象到@InjectMocks中
        MockitoAnnotations.initMocks(this);
        request = JSONObject.parseObject(REQ_MESSAGE, RegisterApplyRequest.class);
        // 模拟回调成功
        Resp resp = RespUtil.success("mockSuccess");
        Mockito.doReturn(JSON.toJSONString(resp)).when(jdhHttpServer).simpleSend(anyString(), anyString(), any());
    }

    @Test
    public void testSuccessRequest() {
        // 具体的测试方法
        ....
    }

例子2:ChannelRequestRetryJobHandlerTest

// 基类标记了PowerMockRunner,使用PowerMock框架
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*"})
public abstract class BasePowerMockRunner {

}
@PrepareForTest(CommonUtil.class)
public class ChannelRequestRetryJobHandlerTest extends BasePowerMockRunner {
    // PowerMock会自动将下面的mock对象注入到channelRequestRetryJobHandler中
    @InjectMocks
    @Spy
    private ChannelRequestRetryJobHandler channelRequestRetryJobHandler = new ChannelRequestRetryJobHandler();
    /**
     * 模拟server
     */
    @Mock
    private DefaultHttpServerTemplate defaultHttpServerTemplate;
    @Mock
    private ChannelRequestLogService channelRequestLogService;
    @Mock
    private FileInfoNoticeService fileInfoNoticeService;
    ...
}
2. @Mock

@Mock会将对象的所有方法进行mock处理,一律默认返回null。可以对mock对象进行Mockito.when(…)设置模拟方法执行操作,
或Mcokito.verify(…)对mock对象的行为进行验证。

3. @Spy

@Spy标记的对象也是一个mock对象,不同的是,默认所有方法都返回原始对象的方法,只有被Mockito.when(…)进行stub之后的方法才会走mock方法。
同样也可执行Mcokito.verify(…)对mock对象的行为进行验证。

搭配@Autowired使用,可以将SpringBean注入到@InjectMocks中,同时还能对该对象进行Mockito.verify操作。

4. Mockito.when(…)

上述的注解是针对mock对象初始化,mock对象行为主要通过Mockito.when(…)方式进行,可以模拟方法返回,模拟异常等。模拟的行为称为stub。
以下用一个模拟返回方式,针对@Mock和@Spy的两种mock对象差异说明使用方式。

以下都是针对一个httpServer模拟返回成功对象的操作。

@Spy:

@Spy
private HttpServer httpServer;
...
Mockito.doReturn(JSON.toJSONString(resp)).when(httpServer).simpleSend(anyString(), anyString(), any());

@Mock:

@Mock
private CiticbankHttpServerTemplate citicbankHttpServerTemplate;
...
Response successResp = CommonUtil.buildSuccessResponse("mockSuccess");
Mockito.when(citicbankHttpServerTemplate.packageRequest(any(), any(), any(), anyBoolean())).thenReturn(successResp);

doReturn和thenReturn效果一致,区别在于前者不会真的执行mock对象对应的方法,而后者会执行mock对象的方法。
由于@Mock方式创建的对象所有方法都返回null,因此不会执行到真实方法;而@Spy如果没有进行stub操作,会执行真实方法。

stub的匹配规则:例子中出现的anyXXX(),表示这个mock方法在被何种入参场景调用时才会触发。

** null参数只能用any()识别,不能用anyString()

5. Mockito.verify(…)

每个单元测试的最后,都要针对一些数据进行Assert断言操作,例如
Assert.assertEquals("expected result", mockDate.getResult()); 来断言模拟数据的某些数据状态是否符合预期。
针对mock对象的行为验证,比如mock方法执行次数,是否抛出异常,等等,则可通过Mockito.verify(…)进行验证。

例如,以下验证mockObject这个对象的retry方法,匹配参数为any(),即任意对象,至少在前面的测试方法里至少被调用过一次:

@Mock
private T mockObject;
...测试方法逻辑...
Mockito.verify(mockObject, Mockito.atLeastOnce()).retry(any());
static/final方法的Mock方式

Mockito本身不支持static/final方法的mock,目前使用PowerMock增强Mockito的处理。PowerMock的缺点前面提到,因此使用时不建议搭配SpringRunner使用,否则集成单元测试执行会失败。

static/final方法的mock例子:

// 测试基类,也可以直接在对应测试类上使用下面的注解,效果相同
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*"})
public abstract class BasePowerMockRunner {

}

// 说明要mock的static/final方法所属的类
@PrepareForTest(CommonUtil.class)
public class ChannelRequestRetryJobHandlerTest extends BasePowerMockRunner {
    /**
     * 模拟server
     */
    @Mock
    private DefaultHttpServerTemplate defaultHttpServerTemplate;

    @Before
    public void setUp() {
        // 模拟返回静态方法,注入对应的Bean
        PowerMockito.mockStatic(CommonUtil.class);
        MockitoAnnotations.initMocks(this);
        // 基础数据初始化
        PowerMockito.when(CommonUtil.getBean(anyString())).thenReturn(defaultHttpServerTemplate);
    }
}

Groovy使用demo

def spyHandler = Spy(AutoEffectiveFactorQuotaHandler)
直接使用mock的service 然后测试里面的方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Groovy和Spock是一对非常强大的测试工具,经常被用于Java应用程序的单元测试和集成测试。Groovy是一种动态语言,它能够与Java无缝集成,提供了更简洁、灵活的语法和更强大的功能。Spock是一种基于Groovy的测试框架,它结合了传统的单元测试框架和行为驱动开发(BDD)的思想,提供了一种更易读、更易维护的测试编写方式。 使用Groovy和Spock进行测试非常简单。首先,你需要在项目中引入相应的依赖。对于Groovy,你可以在项目的构建工具(如Maven或Gradle)中添加Groovy的依赖。对于Spock,你需要添加spock-core和spock-spring(如果需要与Spring集成)这两个依赖。 在编写测试时,你可以使用Spock提供的各种注解和断言来编写测试逻辑。Spock的语法非常接近自然语言,能够更好地表达测试的意图。以下是一个简单的示例: ```groovy import spock.lang.Specification class MySpec extends Specification { def "test addition"() { given: def a = 2 def b = 3 when: def result = a + b then: result == 5 } } ``` 在这个示例中,我们定义了一个名为"test addition"的测试方法。在given块中,我们初始化了两个变量a和b。在when块中,我们执行了相加操作并将结果赋给result变量。在then块中,我们使用断言来验证结果是否等于5。 你可以使用任何GroovyJava的特性来编写测试逻辑,包括使用Mockito等库进行模拟和依赖注入。 希望这能帮助你入门Groovy和Spock测试!如果你有更多的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值