Java几种常用的断言风格你怎么选

 更新时间:2020年01月03日 09:23:13   作者:PageThinker  
这篇文章主要介绍了Java几种常用的断言风格你怎么选,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

日常工作中,不管你是写Unit Test,还是采用TDD的编程方式进行开发,都会遇到断言。而断言的风格常见的会有Assert、BDD风格,对于这些常见的断言风格你怎么选择呢?

01 Assert风格

JUnit中提供了这样的assert断言风格,例如:

  void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {
    EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED);

    String result = entranceMachine.execute(Action.INSERT_COIN);

    assertEquals("opened", result);
    assertEquals(EntranceMachineState, entranceMachineState.UNLOCKED);
  }

Hamcrest和AssertJ都提供了assertThat()这样风格的断言,例如:

AssertJ提供的assertThat()的断言语法

  void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {
    EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED);

    String result = entranceMachine.execute(Action.INSERT_COIN);

    assertThat(result).isEqualsTo("opened");
    assertThat(EntranceMachineState).isEqualsTo(entranceMachineState.UNLOCKED);
  }

Hamcrest提供的assertThat()断言语法

  void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {
    EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED);

    String result = entranceMachine.execute(Action.INSERT_COIN);

    assertThat(result, is("opened"));
    assertThat(EntranceMachineState, is(entranceMachineState.UNLOCKED));
  }

对比上面三种断言语法,因为场景简单,所以结果差异并不是很大。对于我个人更加偏向于使用AssertJ提供的断言风格。因为这种风格避免JUnit提供的断言中经常遇到的问题,expected在前还是actural在前的问题。相比于Hamcrest的断言风格,在日常工作中综合对比发现AssertJ的更加清晰,毕竟AssertJ中assertThat只需要接收一个参数,而不用关注括号是否对齐的问题。

日常工作中如果使用TDD,且场景适当(例如上面例子),那么Hamcreate和AssertJ的差别不是很大。JUnit5默认提供了Hamcreate的断言,不需要额外的再引入其他依赖。

02 BDD风格

代码的可读性越来越收到开发者的重视。测试代码的可读性同样重要,为了让测试代码结构清晰,便于业务逻辑变动时能快读读懂测试的上下文,很多开发团队约定了BDD的风格来组织测试代码。其中包含两部分的约定:测试方法名的约定,测试代码段落的约定。

例如前面的例子:

void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {
   ...
  }

虽然方法名很长,但是通过方法名我们能够快速知道测试类中有哪些测试,通过方法名我们能够清晰的当前测试的上下文,在测什么,期望的结果什么。通过方法名而不是通过比方法名长很多的代码段来获取测试在测什么的信息,毕竟阅读代码时间和修改代码时间可能是10:1,甚至20:1。所以团队约定BDD的风格组织在后续修改代码时,是受益良多的。

当需要也带具体的测试代码的时候,团队发现按照BDD这种三段式的风格来组织代码受益良多。例如:

  void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {
    EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED);

    String result = entranceMachine.execute(Action.INSERT_COIN);

    assertThat(result).isEqualsTo("opened");
    assertThat(EntranceMachineState).isEqualsTo(entranceMachineState.UNLOCKED);
  }

我们可以清晰的知道哪行代码在描述上下文,哪几行代码在描述测试意图,哪几行代码在描述测试结果验证。

BDD的风格能够帮助团队将测试代码维护的较为清晰。AssertJ提供了BDD风格的断言方式。使用then()语法。例如:

@Test
  void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {
    EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED);

    String result = entranceMachine.execute(Action.INSERT_COIN);

    then(result).isEqualsTo("opened");
    then(EntranceMachineState).isEqualsTo(entranceMachineState.UNLOCKED);
  }

断言变化不大。但是真正仔细读的时候,会发现使用then()还是简单那么一点点的。

我们常用的Mock工具Mockito,也提供了BDD风格的断言:then(), should(), and()。

import static org.mockito.BDDMockito.then;
import static org.assertj.core.api.BDDAssertions.and;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;

@SuppressWarnings("static-access")
@Test
public void bdd_assertions_with_bdd_mockito() {
 Person person = mock(Person.class)
 person.ride(bike);

 person.ride(bike);

 then(person).should(times(2)).ride(bike);
 and.then(person.hasBike()).isTrue();
}

所以日常开发中,我会首先选择then(),其次会选择assertThat()。

除了以上两种断言风格,流式断言让代码更清晰,断言重复内容更少

当我们需要为某个结果测试多个测试点时,如果为每个测试点都组织一次相同的上下文,那么重复代码太多。带来的价值就是那么一点点区别,所以在测试力度上我们可以根据经验来在开发工程中动态调整。

下面据一个例子,当我们需要验证有一个查询方法返回的List的结果时,不单单要验证List中元素的数量,还要验证元素是否时期望的顺序。那么流式写法会缩减一部分重复的断言代码。

then(users).hasSize(3)
      .containsExactlyInAnyOrder(
        firstUser,
        secondUser,
        thirdUser);

上面是日常工作中经常使用到的断言技巧,你的怎么选择的呢?那种风格无所谓能工作就行?

参考

Hamcrest

AssertJ

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 哈希表在算法题目中的实际应用详解(Java)

    哈希表在算法题目中的实际应用详解(Java)

    散列表(Hash table,也叫哈希表)是根据关键码值(Key value)而直接进行访问的数据结构,下面这篇文章主要给大家介绍了关于哈希表在算法题目中的实际应用,文中介绍的方法是Java,需要的朋友可以参考下
    2024-03-03
  • 详解Java设计模式编程中的依赖倒置原则

    详解Java设计模式编程中的依赖倒置原则

    这篇文章主要介绍了详解Java设计模式中的依赖倒置原则,针对面对对象编程中的抽象的运用,需要的朋友可以参考下
    2016-02-02
  • java并发高的情况下用ThreadLocalRandom来生成随机数

    java并发高的情况下用ThreadLocalRandom来生成随机数

    如果我们想要生成一个随机数,通常会使用Random类。但是在并发情况下Random生成随机数的性能并不是很理想,本文主要介绍了java并发高的情况下用ThreadLocalRandom来生成随机数,感兴趣的可以了解一下
    2022-05-05
  • 浅谈java的接口和C++虚类的相同和不同之处

    浅谈java的接口和C++虚类的相同和不同之处

    下面小编就为大家带来一篇浅谈java的接口和C++虚类的相同和不同之处。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,祝大家游戏愉快哦
    2016-12-12
  • Java数据结构之数组(动力节点之Java学院整理)

    Java数据结构之数组(动力节点之Java学院整理)

    这篇文章主要介绍了Java数据结构之数组(动力节点之Java学院整理)的相关资料,包括创建和内存分配,数组封装后的使用等,需要的朋友参考下吧
    2017-04-04
  • 一文带你了解Spring中Bean名称加载机制

    一文带你了解Spring中Bean名称加载机制

    这篇文章主要给大家介绍了Spring Framework如何从使用注解定义的Bean元数据中获取到Bean的名称,文中通过代码示例给大家介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2024-01-01
  • Java方法递归与输入输出深入探索

    Java方法递归与输入输出深入探索

    这篇文章主要介绍了Java方法递归与输入输出的相关资料,方法递归是一种在方法内部调用自身的技术,适用于具有递归结构的问题,输入输出是Java程序与外部世界交互的桥梁,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • MDC在多线程中的使用方式

    MDC在多线程中的使用方式

    这篇文章主要介绍了MDC在多线程中的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Spring中的@Conditional注解实现分析

    Spring中的@Conditional注解实现分析

    这篇文章主要介绍了Spring中的@Conditional注解实现分析,  @Conditional是Spring 4出现的注解,但是真正露出价值的是Spring Boot的扩展@ConditionalOnBean等,需要的朋友可以参考下
    2023-12-12
  • 解决IDEA和CMD中java命令提示错误: 找不到或无法加载主类的问题

    解决IDEA和CMD中java命令提示错误: 找不到或无法加载主类的问题

    这篇文章主要介绍了解决IDEA和CMD中java命令提示错误: 找不到或无法加载主类的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09

最新评论