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

日常工作中,不管你是写Unit Test,还是采用TDD的编程方式进行开发,都会遇到断言 。而断言的风格常见的会有Assert、BDD风格,对于这些常见的断言风格你怎么选择呢?
01 Assert风格JUnit中提供了这样的assert断言风格,例如:
[@Test](https://my.oschina.net/azibug)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()的断言语法
[@Test](https://my.oschina.net/azibug)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()断言语法
[@Test](https://my.oschina.net/azibug)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的风格来组织测试代码 。其中包含两部分的约定:测试方法名的约定,测试代码段落的约定 。
例如前面的例子:
[@Test](https://my.oschina.net/azibug)void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() {...}虽然方法名很长,但是通过方法名我们能够快速知道测试类中有哪些测试,通过方法名我们能够清晰的当前测试的上下文,在测什么,期望的结果什么 。通过方法名而不是通过比方法名长很多的代码段来获取测试在测什么的信息,毕竟阅读代码时间和修改代码时间可能是10:1,甚至20:1 。所以团队约定BDD的风格组织在后续修改代码时,是受益良多的 。
当需要也带具体的测试代码的时候,团队发现按照BDD这种三段式的风格来组织代码受益良多 。例如:
[@Test](https://my.oschina.net/azibug)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()语法 。例如:
@Testvoid 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")@Testpublic 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();}


推荐阅读