
junit5
最近,我為一個小型個人項目編寫了許多Jasmine測試。 我花了一些時間才終于感到正确地完成了測試。 此後,當我切換回JUnit測試時,我總是很難過。 由于某種原因,JUnit測試不再那麼好,我想知道是否有可能以類似于Jasmine的方式編寫JUnit測試。
Jasmine是受RSpec (Ruby BDD測試架構)啟發而流行JavaScript行為驅動開發測試架構。
一個簡單的茉莉花測試如下所示:
describe('AudioPlayer tests', function() {
var player;
beforeEach(function() {
player = new AudioPlayer();
});
it('should not play any track after initialization', function() {
expect(player.isPlaying()).toBeFalsy();
});
...
});
第一行中的describe()函數調用使用Description AudioPlayer tests建立一個新的測試套件。 在測試套件中,我們可以使用it()建立測試(在Jasmine中稱為specs)。 在這裡,我們檢查建立新的AudioPlayer的isPlaying()方法是否傳回false。AudioPlayer執行個體。
用JUnit編寫的相同測試如下所示:
public class AudioPlayerTest {
private AudioPlayer audioPlayer;
@Before
public void before() {
audioPlayer = new AudioPlayer();
}
@Test
void notPlayingAfterInitialization() {
assertFalse(audioPlayer.isPlaying());
}
...
}
我個人認為Jasmine測試與JUnit版本相比更具可讀性。 在Jasmine中,唯一對測試沒有任何影響的噪音是花括号和function關鍵字。 其他所有内容都包含一些有用的資訊。
在閱讀JUnit測試時,我們可以忽略諸如void,通路修飾符(私有,公共,..),注釋和不相關的方法名稱(如以@Before注釋的方法名稱)之類的關鍵字。 除此之外,以駝峰案例方法名稱編碼的測試描述不太好讀。
除了提高可讀性外,我真的很喜歡Jasmine嵌套測試套件的功能。
讓我們來看一個更長的示例:
describe('AudioPlayers tests', function() {
var player;
beforeEach(function() {
player = new AudioPlayer();
});
describe('when a track is played', function() {
var track;
beforeEach(function() {
track = new Track('foo/bar.mp3')
player.play(track);
});
it('is playing a track', function() {
expect(player.isPlaying()).toBeTruthy();
});
it('returns the track that is currently played', function() {
expect(player.getCurrentTrack()).toEqual(track);
});
});
...
});
在這裡,我們建立了一個子測試套件,負責測試AudioPlayer播放曲目時的行為。 内部的beforeEach()調用用于為子測試套件内的所有測試設定通用的前提條件。
相反,在JUnit中為多個(但不是全部)測試共享通用的前提條件有時會變得很麻煩。 當然,在測試中複制設定代碼是不好的,是以我們為此建立了額外的方法。 為了在設定方法和測試方法之間共享資料(例如上面示例中的track變量),我們必須使用成員變量(範圍要大得多)。
另外,我們應確定将具有類似前提條件的測試分組在一起,以避免需要閱讀整個測試類以找到特定情況下的所有相關測試。 或者我們可以将事情分成多個較小的類。 但是,然後我們可能必須在這些類之間共享設定代碼……
如果我們檢視Jasmine測試,就會發現該結構是通過調用全局函數(例如describe(),it(),…)并傳遞描述性字元串和匿名函數來定義的。
使用Java 8,我們得到了Lambdas,是以我們可以做同樣的事情嗎?
是的,我們可以在Java 8中編寫如下代碼:
public class AudioPlayerTest {
private AudioPlayer player;
public AudioPlayerTest() {
describe("AudioPlayer tests", () -> {
beforeEach(() -> {
player = new AudioPlayer();
});
it("should not play any track after initialization", () -> {
expect(player.isPlaying()).toBeFalsy();
});
});
}
}
如果我們暫時假設describe(),beforeEach(),it()和Expect()是采用适當參數的靜态導入方法,則至少可以編譯。 但是,我們應該如何進行這種測試?
出于興趣,我嘗試将其與JUnit內建,結果發現這實際上非常簡單(我将在以後進行介紹)。 到目前為止,結果是一個名為Oleaster的小型圖書館。
用Oleaster編寫的測試如下所示:
import static com.mscharhag.oleaster.runner.StaticRunnerSupport.*;
...
@RunWith(OleasterRunner.class)
public class AudioPlayerTest {
private AudioPlayer player;
{
describe("AudioPlayer tests", () -> {
beforeEach(() -> {
player = new AudioPlayer();
});
it("should not play any track after initialization", () -> {
assertFalse(player.isPlaying());
});
});
}
}
與前面的示例相比,隻有幾處發生了變化。 在這裡,測試類使用JUnit @RunWith注釋進行注釋。 這告訴JUnit在運作此測試類時使用Oleaster。 通過靜态導入StaticRunnerSupport。*,可以直接通路靜态Oleaster方法,例如describe()或it()。 還要注意,構造函數已由執行個體初始化程式替換,而Jasmine like matcher被标準JUnit斷言替換。
與原始的茉莉花測試相比,實際上有一件事情并不那麼出色。 實際上,在Java中,變量必須有效地最終确定才能在lambda表達式中使用。 這意味着以下代碼段不會編譯:
describe("AudioPlayer tests", () -> {
AudioPlayer player;
beforeEach(() -> {
player = new AudioPlayer();
});
...
});
在beforeEach()lambda表達式中對玩家的指派将不會編譯(因為玩家實際上不是最終的)。 在Java中,我們必須在這種情況下使用執行個體字段(如上例所示)。
萬一您擔心要報告:Oleaster僅負責收集和運作測試用例。 整個報告仍由JUnit完成。 是以,Oleaster應該不會對使用JUnit報表的工具和庫造成任何問題。
例如,以下螢幕截圖顯示了IntelliJ IDEA中Oleaster測試失敗的結果:
如果您想知道Oleaster測試在實踐中的外觀,您可以看看Oleaster的測試(用Oleaster本身編寫)。 您可以在此處找到GitHub測試目錄。
通過評論此文章或建立GitHub問題,随時添加任何類型的回報。
翻譯自: https://www.javacodegeeks.com/2014/07/an-alternative-approach-of-writing-junit-tests-the-jasmine-way.html
junit5