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
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。