亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JUnit 5中擴(kuò)展模型的深入理解

 更新時(shí)間:2018年08月29日 08:38:11   作者:無明  
幾乎所有的Java 開發(fā)人員都會使用JUnit 來做測試,但其實(shí)很多自動化測試人員也會使用Junit 。下面這篇文章主要給大家介紹了關(guān)于JUnit 5中擴(kuò)展模型的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

什么是Junit5 ?

先看來個(gè)公式:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

這看上去比Junit4 復(fù)雜,實(shí)際上在導(dǎo)入包時(shí)也會復(fù)雜一些。

JUnit Platform是在JVM上啟動測試框架的基礎(chǔ)。

JUnit Jupiter是JUnit5擴(kuò)展的新的編程模型和擴(kuò)展模型,用來編寫測試用例。Jupiter子項(xiàng)目為在平臺上運(yùn)行Jupiter的測試提供了一個(gè)TestEngine (測試引擎)。

JUnit Vintage提供了一個(gè)在平臺上運(yùn)行JUnit 3和JUnit 4的TestEngine 。

關(guān)鍵要點(diǎn)

  • JUnit 5是一個(gè)模塊化和可擴(kuò)展的測試框架,支持Java 8及更高版本。
  • JUnit 5由三個(gè)部分組成——一個(gè)基礎(chǔ)平臺、一個(gè)新的編程和擴(kuò)展模型Jupiter,以及一個(gè)名為Vintage的向后兼容的測試引擎。
  • JUnit 5 Jupiter的擴(kuò)展模型可用于向JUnit中添加自定義功能。
  • 擴(kuò)展模型API測試生命周期提供了鉤子和注入自定義參數(shù)的方法(即依賴注入)。

JUnit是最受歡迎的基于JVM的測試框架,在第5個(gè)主要版本中進(jìn)行了徹底的改造。JUnit 5提供了豐富的功能——從改進(jìn)的注解、標(biāo)簽和過濾器到條件執(zhí)行和對斷言消息的惰性求值。這讓基于TDD編寫單元測試變得輕而易舉。新框架還帶來了一個(gè)強(qiáng)大的擴(kuò)展模型。擴(kuò)展開發(fā)人員可以使用這個(gè)新模型向JUnit 5中添加自定義功能。本文將指導(dǎo)你完成自定義擴(kuò)展的設(shè)計(jì)和實(shí)現(xiàn)。這種自定義擴(kuò)展機(jī)制為Java程序員提供了一種創(chuàng)建和執(zhí)行故事和行為(即BDD規(guī)范測試)的方法。

我們首先使用JUnit 5和我們的自定義擴(kuò)展(稱為“StoryExtension”)來編寫一個(gè)示例故事和行為(測試方法)。這個(gè)示例使用了兩個(gè)新的自定義注解“@Story”和“@Scenario”,以及“Scene”類,用以支持我們的自定義StoryExtension:

import org.junit.jupiter.api.extension.ExtendWith;
 
import ud.junit.bdd.ext.Scenario;
import ud.junit.bdd.ext.Scene;
import ud.junit.bdd.ext.Story;
import ud.junit.bdd.ext.StoryExtension; 
 
@ExtendWith(StoryExtension.class)
@Story(name=“Returns go back to the stockpile”, description=“...“)
public class StoreFrontTest {
 
 @Scenario(“Refunded items should be returned to the stockpile”)
 public void refundedItemsShouldBeRestocked(Scene scene) {
  scene
   .given(“customer bought a blue sweater”,
      () -> buySweater(scene, “blue”))
 
   .and(“I have three blue sweaters in stock”,
      () -> assertEquals(3, sweaterCount(scene, “blue”),
        “Store should carry 3 blue sweaters”))
 
   .when(“the customer returns the blue sweater for a refund”,
      () -> refund(scene, 1, “blue”))
 
   .then(“I should have four blue sweaters in stock”,
      () -> assertEquals(4, sweaterCount(scene, “blue”),
        “Store should carry 4 blue sweaters”))
   .run();
 }
}

從代碼片段中我們可以看到,Jupiter的擴(kuò)展模型非常強(qiáng)大。我們還可以看到,我們的自定義擴(kuò)展及其相應(yīng)的注解為測試用例編寫者提供了簡單而干凈的方法來編寫B(tài)DD規(guī)范。

作為額外的獎(jiǎng)勵(lì),當(dāng)使用我們的自定義擴(kuò)展程序執(zhí)行測試時(shí),會生成如下所示的文本報(bào)告:

STORY: Returns go back to the stockpile
 
As a store owner, in order to keep track of stock, I want to add items back to stock when they're returned.
 
SCENARIO: Refunded items should be returned to stock
   GIVEN that a customer previously bought a blue sweater from me
     AND I have three blue sweaters in stock
    WHEN the customer returns the blue sweater for a refund
    THEN I should have four blue sweaters in stock

這些報(bào)告可以作為應(yīng)用程序功能集的文檔。

自定義擴(kuò)展StoryExtension能夠借助以下核心概念來支持和執(zhí)行故事和行為:

  • 用于裝飾測試類和測試方法的注解
  • JUnit 5 Jupiter的生命周期回調(diào)
  • 動態(tài)參數(shù)解析

注解

示例中的“@ExtendWith”注解是由Jupiter提供的標(biāo)記接口。這是在測試類或方法上注冊自定義擴(kuò)展的方法,目的是讓Jupiter測試引擎調(diào)用給定類或方法的自定義擴(kuò)展?;蛘撸瑴y試用例編寫者可以通過編程的方式注冊自定義擴(kuò)展,或者通過服務(wù)加載器機(jī)制進(jìn)行自動注冊。

我們的自定義擴(kuò)展需要一種識別故事的方法。為此,我們定義了一個(gè)名為“Story”的自定義注解類,如下所示:

import org.junit.platform.commons.annotation.Testable;
 
@Testable
public @interface Story {...}

測試用例編寫者應(yīng)該使用這個(gè)自定義注解將測試類標(biāo)記為故事。請注意,這個(gè)注解本身使用了JUnit 5內(nèi)置的“@Testable”注解。這個(gè)注解為IDE和其他工具提供了一種識別可測試的類和方法的方式——也就是說,帶有這個(gè)注解的類或方法可以通過JUnit 5 Jupiter測試引擎來執(zhí)行。

我們的自定義擴(kuò)展還需要一種方法來識別故事中的行為或場景。為此,我們定義一個(gè)名為“Scenario”的自定義注解類,看起來像這樣:

import org.junit.jupiter.api.Test;
 
@Test
public @interface Scenario {...}

測試用例編寫者應(yīng)使用這個(gè)自定義注解將測試方法標(biāo)記為場景。這個(gè)注解本身使用了JUnit 5 Jupiter的內(nèi)置“@Test”注解。當(dāng)IDE和測試引擎掃描給定的一組測試類并在公共實(shí)例方法上找到@Scenario注解時(shí),就會將這些方法標(biāo)記為可執(zhí)行的測試方法。

請注意,與JUnit 4的@Test注解不同,Jupiter的@Test注解不支持可選的“預(yù)期”異常和“超時(shí)”參數(shù)。Jupiter的@Test注解是從頭開始設(shè)計(jì)的,并考慮到了可擴(kuò)展性。

生命周期

JUnit 5 Jupiter提供了擴(kuò)展回調(diào),可用于訪問測試生命周期事件。擴(kuò)展模型提供了幾個(gè)接口,用于在測試執(zhí)行生命周期的各個(gè)時(shí)間點(diǎn)對測試進(jìn)行擴(kuò)展:



擴(kuò)展開發(fā)者可以自由地實(shí)現(xiàn)所有或部分生命周期接口。

“BeforeAllCallback”接口提供了一種方法用于初始化擴(kuò)展并在調(diào)用JUnit測試容器中的測試用例之前添加自定義邏輯。我們的StoryExtension類將實(shí)現(xiàn)這個(gè)接口,以確保給定的測試類使用了“@Story”注解。

import org.junit.jupiter.api.extension.BeforeAllCallback;
 
public class StoryExtension implements BeforeAllCallback {
 @Override
 public void beforeAll(ExtensionContext context) throws Exception {
 
  if (!AnnotationSupport
    .isAnnotated(context.getRequiredTestClass(), Story.class)) {
   throw new Exception(“Use @Story annotation...“);
  }
 }
}

Jupiter引擎將提供一個(gè)用于運(yùn)行擴(kuò)展的執(zhí)行上下文。我們使用這個(gè)上下文來確定正在執(zhí)行的測試類是否使用了“@Story”注解。我們使用JUnit平臺提供的AnnotationSupport輔助類來檢查是否存在這個(gè)注解。

回想一下,我們的自定義擴(kuò)展在執(zhí)行測試后會生成BDD報(bào)告。這些報(bào)告的某些部分是從“@Store”注解的元素中提取的。我們使用beforeAll回調(diào)來保存這些字符串。稍后,在執(zhí)行生命周期結(jié)束時(shí),再基于這些字符串生成報(bào)告。我們使用了一個(gè)簡單的POJO。我們將這個(gè)類命名為“StoryDe​​tails”。以下代碼片段演示了創(chuàng)建這個(gè)類實(shí)例的過程,并將注解元素保存到實(shí)例中:

public class StoryExtension implements BeforeAllCallback {
 @Override
 public void beforeAll(ExtensionContext context) throws Exception {
 
  Class<?> clazz = context.getRequiredTestClass();
  Story story = clazz.getAnnotation(Story.class);
 
  StoryDetails storyDetails = new StoryDetails()
    .setName(story.name())
    .setDescription(story.description())
    .setClassName(clazz.getName());
 
  context.getStore(NAMESPACE).put(clazz.getName(), storyDetails);
 }
}

我們需要解釋一下方法的最后一個(gè)語句。我們實(shí)際上是從執(zhí)行上下文中獲取一個(gè)帶有名字的存儲,并將新創(chuàng)建的“StoryDe​​tails”實(shí)例保存到這個(gè)存儲中。

自定義擴(kuò)展可以使用存儲來保存和獲取任意數(shù)據(jù)——基本上就是一個(gè)存在于內(nèi)存中的map。為了避免多個(gè)擴(kuò)展之間出現(xiàn)意外的key沖突,JUnit引入了命名空間的概念。命名空間是一種對不同擴(kuò)展保存的數(shù)據(jù)進(jìn)行隔離的方法。用于隔離擴(kuò)展數(shù)據(jù)的一種常用方法是使用自定義擴(kuò)展類名:

private static final Namespace NAMESPACE = Namespace
   .create(StoryExtension.class);

我們的擴(kuò)展需要用到的另一個(gè)自定義注解是“@Scenario”注解。這個(gè)注解用于將測試方法標(biāo)記為故事中的場景或行為。我們的擴(kuò)展將解析這些場景,以便將它們作為JUnit測試用例來執(zhí)行并生成報(bào)告?;叵胍幌挛覀冎翱吹降纳芷趫D中的“BeforeEachCallback”接口,在調(diào)用每個(gè)測試方法之前,我們將使用回調(diào)來添加附加邏輯:

import org.junit.jupiter.api.extension.BeforeEachCallback;
 
public class StoryExtension implements BeforeEachCallback {
 @Override
 public void beforeEach(ExtensionContext context) throws Exception {
  if (!AnnotationSupport.
   isAnnotated(context.getRequiredTestMethod(), Scenario.class)) {
    throw new Exception(“Use @Scenario annotation...“);
  }
 }
}

如前所述,Jupiter引擎將提供一個(gè)用于運(yùn)行擴(kuò)展的執(zhí)行上下文。我們使用上下文來確定正在執(zhí)行的測試方法是否使用了“@Scenario”注解。

回到本文的開頭,我們提供了一個(gè)故事的示例代碼,我們的自定義擴(kuò)展負(fù)責(zé)將“Scene”類的實(shí)例注入到每個(gè)測試方法中。Scene類讓測試用例編寫者能夠使用“given”、“then”和“when”等步驟來定義場景(行為)。Scene類是我們自定義擴(kuò)展的中心單元,它包含了特定于測試方法的狀態(tài)信息。狀態(tài)信息可以在場景的各個(gè)步驟之間傳遞。我們使用“BeforeEachCallback”接口在調(diào)用測試方法之前準(zhǔn)備一個(gè)Scene實(shí)例:如前所述,Jupiter引擎將提供一個(gè)用于運(yùn)行擴(kuò)展執(zhí)行上下文。我們使用上下文來確定正在執(zhí)行的測試方法是否使用了“@Scenario”注解。

public class StoryExtension implements BeforeEachCallback {
 @Override
 public void beforeEach(ExtensionContext context) throws Exception {
  Scene scene = new Scene()
    .setDescription(getValue(context, Scenario.class));
 
  Class<?> clazz = context.getRequiredTestClass();
 
  StoryDetails details = context.getStore(NAMESPACE)
    .get(clazz.getName(), StoryDetails.class);
 
  details.put(scene.getMethodName(), scene);
 }
}

上面的代碼與我們在“BeforeAllCallback”接口方法中所做的非常相似。

動態(tài)參數(shù)解析

現(xiàn)在我們還缺少一個(gè)東西,即如何將場景實(shí)例注入到測試方法中。Jupiter的擴(kuò)展模型為我們提供了一個(gè)“ParameterResolver”接口。這個(gè)接口為測試引擎提供了一種方法,用于識別希望在測試執(zhí)行期間動態(tài)注入?yún)?shù)的擴(kuò)展。我們需要實(shí)現(xiàn)這個(gè)接口的兩個(gè)方法,以便注入我們的場景實(shí)例:

import org.junit.jupiter.api.extension.ParameterResolver;
 
public class StoryExtension implements ParameterResolver {
 @Override
 public boolean supportsParameter(ParameterContext parameterContext,
          ExtensionContext extensionContext) {
  Parameter parameter = parameterContext.getParameter();
 
  return Scene.class.equals(parameter.getType());
 }
 
 @Override
 public Object resolveParameter(ParameterContext parameterContext,
         ExtensionContext extensionContext) {
  Class<?> clazz = extensionContext.getRequiredTestClass();
 
  StoryDetails details = extensionContext.getStore(NAMESPACE)
    .get(clazz.getName(), StoryDetails.class);
 
  return details.get(extensionContext
       .getRequiredTestMethod().getName());
 }
}

上面的第一個(gè)方法告訴Jupiter我們的自定義擴(kuò)展是否可以注入測試方法所需的參數(shù)。

在第二個(gè)方法“resolveParameter()”中,我們從執(zhí)行上下文的存儲中獲取StoryDe​​tails實(shí)例,然后從StoryDetails實(shí)例中獲取先前為給定測試方法創(chuàng)建的場景實(shí)例,并將其傳給測試引擎。測試引擎將這個(gè)場景實(shí)例注入到測試方法中并執(zhí)行測試。請注意,僅當(dāng)“supportsParameter()”方法返回true值時(shí)才會調(diào)用“resolveParameter()”方法。

最后,為了在執(zhí)行完所有故事和場景后生成報(bào)告,自定義擴(kuò)展實(shí)現(xiàn)了“AfterAllCallback”接口:

import org.junit.jupiter.api.extension.AfterAllCallback;
 
public class StoryExtension implements AfterAllCallback { 
 @Override
 public void afterAll(ExtensionContext context) throws Exception {
 
  new StoryWriter(getStoryDetails(context)).write();
 }
}

“StoryWriter”是一個(gè)自定義類,可生成報(bào)告并將其保存到JSON或文本文件中。

現(xiàn)在,讓我們看看如何使用這個(gè)自定義擴(kuò)展來編寫B(tài)DD風(fēng)格的測試用例。Gradle 4.6及更高版本支持使用JUnit 5運(yùn)行單元測試。你可以使用build.gradle文件來配置JUnit 5。

dependencies {
 testCompile group: “ud.junit.bdd”, name: “bdd-junit”,
    version: “0.0.1-SNAPSHOT”
 
 testCompile group: “org.junit.jupiter”, name: “junit-jupiter-api”,
    version: “5.2.0"
 testRuntime group: “org.junit.jupiter”, name: “junit-jupiter-engine”,
    version: “5.2.0”
}
 
test {
 useJUnitPlatform()
}

如你所見,我們通過“useJUnitPlatform()”方法要求gradle使用JUnit 5。然后我們就可以使用StoryExtension類來編寫測試用例。這是本文開頭給出的示例:

import org.junit.jupiter.api.extension.ExtendWith;
 
import ud.junit.bdd.ext.Scenario;
import ud.junit.bdd.ext.Story;
import ud.junit.bdd.ext.StoryExtension; 
 
@ExtendWith(StoryExtension.class)
@Story(name=“Returns go back to the stockpile”, description=“...“)
public class StoreFrontTest {
 
 @Scenario(“Refunded items should be returned to the stockpile”)
 public void refundedItemsShouldBeRestocked(Scene scene) {
  scene
   .given(“customer bought a blue sweater”,
      () -> buySweater(scene, “blue”))
 
   .and(“I have three blue sweaters in stock”,
      () -> assertEquals(3, sweaterCount(scene, “blue”),
        “Store should carry 3 blue sweaters”))
 
   .when(“the customer returns the blue sweater for a refund”,
      () -> refund(scene, 1, “blue”))
 
   .then(“I should have four blue sweaters in stock”,
      () -> assertEquals(4, sweaterCount(scene, “blue”),
        “Store should carry 4 blue sweaters”))
   .run();
 }
}

我們可以通過“gradle testClasses”來運(yùn)行測試,或者使用其他支持JUnit 5的IDE。除了常規(guī)的測試報(bào)告外,自定義擴(kuò)展還為所有測試類生成BDD文檔。

結(jié)論

我們描述了JUnit 5擴(kuò)展模型以及如何利用它來創(chuàng)建自定義擴(kuò)展。我們設(shè)計(jì)并實(shí)現(xiàn)了一個(gè)自定義擴(kuò)展,測試用例編寫者可以使用它來創(chuàng)建和執(zhí)行故事。讀者可以從GitHub上獲取代碼,并研究如何使用Jupiter擴(kuò)展模型及其API來實(shí)現(xiàn)自定義擴(kuò)展。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Spring  ApplicationContextAware 接口的作用及使用方式

    Spring  ApplicationContextAware 接口的作用及使用方式

    Spring提供了許多回調(diào)接口,用于Bean生命周期中執(zhí)行特定的操作,通過實(shí)現(xiàn)ApplicationContextAware接口,Spring提供了一種便捷的方式讓 Bean獲取對Spring容器的引用,本文介紹ApplicationContextAware接口的作用、使用方式,以及在實(shí)際應(yīng)用中的常見場景,感興趣的朋友一起看看吧
    2024-01-01
  • Mybatis 一對多和多對一關(guān)聯(lián)查詢問題

    Mybatis 一對多和多對一關(guān)聯(lián)查詢問題

    這篇文章主要介紹了Mybatis 一對多和多對一關(guān)聯(lián)查詢問題,需要的朋友可以參考下
    2017-04-04
  • Java HashTable的原理與實(shí)現(xiàn)

    Java HashTable的原理與實(shí)現(xiàn)

    Java中的HashTable是一種線程安全的哈希表實(shí)現(xiàn),它可以高效地存儲和快速查找數(shù)據(jù),本文將介紹Java中的HashTable的實(shí)現(xiàn)原理、常用方法和測試用例,需要的小伙伴可以參考一下
    2023-09-09
  • Java實(shí)現(xiàn)調(diào)用接口API并返回?cái)?shù)據(jù)

    Java實(shí)現(xiàn)調(diào)用接口API并返回?cái)?shù)據(jù)

    這篇文章主要介紹了Java實(shí)現(xiàn)調(diào)用接口API并返回?cái)?shù)據(jù)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 使用Java實(shí)現(xiàn)文件流轉(zhuǎn)base64

    使用Java實(shí)現(xiàn)文件流轉(zhuǎn)base64

    這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)文件流轉(zhuǎn)base64效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Java三種循環(huán)求和方法

    Java三種循環(huán)求和方法

    本篇文章給大家介紹了Java三種循環(huán)求和的方法,大家在學(xué)程序的時(shí)候如果能用的到,參考下吧。
    2018-02-02
  • 詳解Spring bean的注解注入之@Autowired的原理及使用

    詳解Spring bean的注解注入之@Autowired的原理及使用

    之前講過bean注入是什么,也使用了xml的配置文件進(jìn)行bean注入,這也是Spring的最原始的注入方式(xml注入).本文主要講解的注解有以下幾個(gè):@Autowired、 @Service、@Repository、@Controller 、@Component、@Bean、@Configuration、@Resource ,需要的朋友可以參考下
    2021-06-06
  • SpringBoot輕松整合MongoDB的全過程記錄

    SpringBoot輕松整合MongoDB的全過程記錄

    這篇文章主要給大家介紹了關(guān)于SpringBoot輕松整合MongoDB的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • linux部署出現(xiàn)java文件操作報(bào)錯(cuò):java.io.FileNotFoundException解決辦法

    linux部署出現(xiàn)java文件操作報(bào)錯(cuò):java.io.FileNotFoundException解決辦法

    這篇文章主要g介紹了linux部署出現(xiàn)java文件操作報(bào)錯(cuò):java.io.FileNotFoundException解決的相關(guān)資料,這個(gè)錯(cuò)誤通常表示你的Spring Boot應(yīng)用程序無法找到指定的文本文件,需要的朋友可以參考下
    2023-12-12
  • Mac Book中Java環(huán)境變量設(shè)置的方法

    Mac Book中Java環(huán)境變量設(shè)置的方法

    本文給大家介紹mac book 中設(shè)置java環(huán)境變量的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下
    2017-04-04

最新評論