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

使用Spring?Boot進(jìn)行單元測(cè)試詳情

 更新時(shí)間:2022年09月22日 15:40:00   作者:六七十三  
這篇文章主要介紹了使用Spring?Boot進(jìn)行單元測(cè)試詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言

本文給你提供在Spring Boot 應(yīng)用程序中編寫好的單元測(cè)試的機(jī)制,并且深入技術(shù)細(xì)節(jié)。

我們將帶你學(xué)習(xí)如何以可測(cè)試的方式創(chuàng)建Spring Bean實(shí)例,然后討論如何使用MockitoAssertJ,這兩個(gè)包在Spring Boot中都為了測(cè)試默認(rèn)引用了。

本文只討論單元測(cè)試。至于集成測(cè)試,測(cè)試web層和測(cè)試持久層將會(huì)在接下來(lái)的系列文章中進(jìn)行討論。

使用 Spring Boot 進(jìn)行測(cè)試系列文章

這個(gè)教程是一個(gè)系列:

  • 使用 Spring Boot 進(jìn)行單元測(cè)試(本文)
  • 使用 Spring Boot 和 @WebMvcTest 測(cè)試SpringMVC controller層
  • 使用 Spring Boot 和 @DataJpaTest 測(cè)試JPA持久層查詢
  • 通過(guò) @SpringBootTest 進(jìn)行集成測(cè)試

如果你喜歡看視頻教程,可以看看Philip的課程:測(cè)試Spring Boot應(yīng)用程序課程

依賴項(xiàng)

本文中,為了進(jìn)行單元測(cè)試,我們會(huì)使用JUnit Jupiter(Junit 5)MockitoAssertJ。此外,我們會(huì)引用Lombok來(lái)減少一些模板代碼:

dependencies{
  compileOnly('org.projectlombok:lombok')
  testCompile('org.springframework.boot:spring-boot-starter-test')
  testCompile 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
  testCompile('org.mockito:mockito-junit-jupiter:2.23.0')
}

MockitoAssertJ會(huì)在spring-boot-test依賴中自動(dòng)引用,但是我們需要自己引用Lombok

不要在單元測(cè)試中使用Spring

如果你以前使用Spring或者Spring Boot寫過(guò)單元測(cè)試,你可能會(huì)說(shuō)我們不要在寫單元測(cè)試的時(shí)候用Spring。但是為什么呢?

考慮下面的單元測(cè)試類,這個(gè)類測(cè)試了RegisterUseCase類的單個(gè)方法:

@ExtendWith(SpringExtension.class)
@SpringBootTest
class RegisterUseCaseTest {

  @Autowired
  private RegisterUseCase registerUseCase;

  @Test
  void savedUserHasRegistrationDate() {
    User user = new User("zaphod", "zaphod@mail.com");
    User savedUser = registerUseCase.registerUser(user);
    assertThat(savedUser.getRegistrationDate()).isNotNull();
  }

}

這個(gè)測(cè)試類在我的電腦上需要大概4.5秒來(lái)執(zhí)行一個(gè)空的Spring項(xiàng)目。

但是一個(gè)好的單元測(cè)試僅僅需要幾毫秒。否則就會(huì)阻礙TDD(測(cè)試驅(qū)動(dòng)開(kāi)發(fā))流程,這個(gè)流程倡導(dǎo)“測(cè)試/開(kāi)發(fā)/測(cè)試”。

但是就算我們不使用TDD,等待一個(gè)單元測(cè)試太久也會(huì)破壞我們的注意力。

執(zhí)行上述的測(cè)試方法事實(shí)上僅需要幾毫秒。剩下的4.5秒是因?yàn)?code>@SpringBootTest告訴了 Spring Boot 要啟動(dòng)整個(gè)Spring Boot 應(yīng)用程序上下文。

所以我們啟動(dòng)整個(gè)應(yīng)用程序僅僅是因?yàn)橐?code>RegisterUseCase實(shí)例注入到我們的測(cè)試類中。啟動(dòng)整個(gè)應(yīng)用程序可能耗時(shí)更久,假設(shè)應(yīng)用程序更大、Spring需要加載更多的實(shí)例到應(yīng)用程序上下文中。

所以,這就是為什么不要在單元測(cè)試中使用Spring。坦白說(shuō),大部分編寫單元測(cè)試的教程都沒(méi)有使用Spring Boot。

創(chuàng)建一個(gè)可測(cè)試的類實(shí)例

然后,為了讓Spring實(shí)例有更好的測(cè)試性,有幾件事是我們可以做的。

屬性注入是不好的

讓我們以一個(gè)反例開(kāi)始??紤]下述類:

@Service
public class RegisterUseCase {

  @Autowired
  private UserRepository userRepository;

  public User registerUser(User user) {
    return userRepository.save(user);
  }

}

這個(gè)類如果沒(méi)有Spring沒(méi)法進(jìn)行單元測(cè)試,因?yàn)樗鼪](méi)有提供方法傳遞UserRepository實(shí)例。因此我們只能用文章之前討論的方式-讓Spring創(chuàng)建UserRepository實(shí)例,并通過(guò)@Autowired注解注入進(jìn)去。

這里的教訓(xùn)是:不要用屬性注入。

提供一個(gè)構(gòu)造函數(shù)

實(shí)際上,我們根本不需要使用@Autowired注解:

@Service
public class RegisterUseCase {

  private final UserRepository userRepository;

  public RegisterUseCase(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public User registerUser(User user) {
    return userRepository.save(user);
  }

}

這個(gè)版本通過(guò)提供一個(gè)允許傳入UserRepository實(shí)例參數(shù)的構(gòu)造函數(shù)來(lái)允許構(gòu)造函數(shù)注入。在這個(gè)單元測(cè)試中,我們現(xiàn)在可以創(chuàng)建這樣一個(gè)實(shí)例(或者我們之后要討論的Mock實(shí)例)并通過(guò)構(gòu)造函數(shù)注入了。

當(dāng)創(chuàng)建生成應(yīng)用上下文的時(shí)候,Spring會(huì)自動(dòng)使用這個(gè)構(gòu)造函數(shù)來(lái)初始化RegisterUseCase對(duì)象。注意,在Spring 5 之前,我們需要在構(gòu)造函數(shù)上增加@Autowired注解,以便讓Spring找到這個(gè)構(gòu)造函數(shù)。

還要注意的是,現(xiàn)在UserRepository屬性是final修飾的。這很重要,因?yàn)檫@樣的話,應(yīng)用程序生命周期時(shí)間內(nèi)這個(gè)屬性內(nèi)容不會(huì)再變化。此外,它還可以幫我們避免變成錯(cuò)誤,因?yàn)槿绻覀兺洺跏蓟搶傩缘脑?,編譯器就報(bào)錯(cuò)。

減少模板代碼

通過(guò)使用Lombok@RequiredArgsConstructor注解,我們可以讓構(gòu)造函數(shù)自動(dòng)生成:

@Service
@RequiredArgsConstructor
public class RegisterUseCase {

  private final UserRepository userRepository;

  public User registerUser(User user) {
    user.setRegistrationDate(LocalDateTime.now());
    return userRepository.save(user);
  }

}

現(xiàn)在,我們有一個(gè)非常簡(jiǎn)潔的類,沒(méi)有樣板代碼,可以在普通的 java 測(cè)試用例中很容易被實(shí)例化:

class RegisterUseCaseTest {

  private UserRepository userRepository = ...;

  private RegisterUseCase registerUseCase;

  @BeforeEach
  void initUseCase() {
    registerUseCase = new RegisterUseCase(userRepository);
  }

  @Test
  void savedUserHasRegistrationDate() {
    User user = new User("zaphod", "zaphod@mail.com");
    User savedUser = registerUseCase.registerUser(user);
    assertThat(savedUser.getRegistrationDate()).isNotNull();
  }

}

還有部分確實(shí),就是如何模擬測(cè)試類所依賴的UserReposity實(shí)例,我們不想依賴真實(shí)的類,因?yàn)檫@個(gè)類需要一個(gè)數(shù)據(jù)庫(kù)連接。

使用Mockito來(lái)模擬依賴項(xiàng)

現(xiàn)在事實(shí)上的標(biāo)準(zhǔn)模擬庫(kù)是 Mockito。它提供至少兩種方式來(lái)創(chuàng)建一個(gè)模擬UserRepository實(shí)例,來(lái)填補(bǔ)前述代碼的空白。

使用普通Mockito來(lái)模擬依賴

第一種方式是使用Mockito編程:

private UserRepository userRepository = Mockito.mock(UserRepository.class);

這會(huì)從外界創(chuàng)建一個(gè)看起來(lái)像UserRepository的對(duì)象。默認(rèn)情況下,方法被調(diào)用時(shí)不會(huì)做任何事情,如果方法有返回值,會(huì)返回null。

因?yàn)?code>userRepository.save(user)返回null,現(xiàn)在我們的測(cè)試代碼assertThat(savedUser.getRegistrationDate()).isNotNull()會(huì)報(bào)空指針異常(NullPointerException)。

所以我們需要告訴Mockito,當(dāng)userRepository.save(user)調(diào)用的時(shí)候返回一些東西。我們可以用靜態(tài)的when方法實(shí)現(xiàn):

@Test
void savedUserHasRegistrationDate() {
  User user = new User("zaphod", "zaphod@mail.com");
  when(userRepository.save(any(User.class))).then(returnsFirstArg());
  User savedUser = registerUseCase.registerUser(user);
  assertThat(savedUser.getRegistrationDate()).isNotNull();
}

這會(huì)讓userRepository.save()返回和傳入對(duì)象相同的對(duì)象。

Mockito為了模擬對(duì)象、匹配參數(shù)以及驗(yàn)證方法調(diào)用,提供了非常多的特性。想看更多,文檔

通過(guò)Mockito@Mock注解模擬對(duì)象

創(chuàng)建一個(gè)模擬對(duì)象的第二種方式是使用Mockito@Mock注解結(jié)合 JUnit Jupiter的MockitoExtension一起使用:

@ExtendWith(MockitoExtension.class)
class RegisterUseCaseTest {

  @Mock
  private UserRepository userRepository;

  private RegisterUseCase registerUseCase;

  @BeforeEach
  void initUseCase() {
    registerUseCase = new RegisterUseCase(userRepository);
  }

  @Test
  void savedUserHasRegistrationDate() {
    // ...
  }

}

@Mock注解指明那些屬性需要Mockito注入模擬對(duì)象。由于JUnit不會(huì)自動(dòng)實(shí)現(xiàn),MockitoExtension則告訴Mockito來(lái)評(píng)估這些@Mock注解。

這個(gè)結(jié)果和調(diào)用Mockito.mock()方法一樣,憑個(gè)人品味選擇即可。但是請(qǐng)注意,通過(guò)使用 MockitoExtension,我們的測(cè)試用例被綁定到測(cè)試框架。

我們可以在RegisterUseCase屬性上使用@InjectMocks注解來(lái)注入實(shí)例,而不是手動(dòng)通過(guò)構(gòu)造函數(shù)構(gòu)造。Mockito會(huì)使用特定的算法來(lái)幫助我們創(chuàng)建相應(yīng)實(shí)例對(duì)象:

@ExtendWith(MockitoExtension.class)
class RegisterUseCaseTest {

  @Mock
  private UserRepository userRepository;

  @InjectMocks
  private RegisterUseCase registerUseCase;

  @Test
  void savedUserHasRegistrationDate() {
    // ...
  }

}

使用AssertJ創(chuàng)建可讀斷言

Spring Boot 測(cè)試包自動(dòng)附帶的另一個(gè)庫(kù)是AssertJ。我們?cè)谏厦娴拇a中已經(jīng)用到它進(jìn)行斷言:

assertThat(savedUser.getRegistrationDate()).isNotNull();

然而,有沒(méi)有可能讓斷言可讀性更強(qiáng)呢?像這樣,例子:

assertThat(savedUser).hasRegistrationDate();

有很多測(cè)試用例,只需要像這樣進(jìn)行很小的改動(dòng)就能大大提高可理解性。所以,讓我們?cè)趖est/sources中創(chuàng)建我們自定義的斷言吧:

class UserAssert extends AbstractAssert<UserAssert, User> {

  UserAssert(User user) {
    super(user, UserAssert.class);
  }

  static UserAssert assertThat(User actual) {
    return new UserAssert(actual);
  }

  UserAssert hasRegistrationDate() {
    isNotNull();
    if (actual.getRegistrationDate() == null) {
      failWithMessage(
        "Expected user to have a registration date, but it was null"
      );
    }
    return this;
  }
}

現(xiàn)在,如果我們不是從AssertJ庫(kù)直接導(dǎo)入,而是從我們自定義斷言類UserAssert引入assertThat方法的話,我們就可以使用新的、更可讀的斷言。

創(chuàng)建一個(gè)這樣自定義的斷言類看起來(lái)很費(fèi)時(shí)間,但是其實(shí)幾分鐘就完成了。我相信,將這些時(shí)間投入到創(chuàng)建可讀性強(qiáng)的測(cè)試代碼中是值得的,即使之后它的可讀性只有一點(diǎn)點(diǎn)提高。我們編寫測(cè)試代碼就一次,但是之后,很多其他人(包括未來(lái)的我)在軟件生命周期中,需要閱讀、理解然后操作這些代碼很多次。

如果你還是覺(jué)得很費(fèi)事,可以看看斷言生成器

結(jié)論

盡管在測(cè)試中啟動(dòng)Spring應(yīng)用程序也有些理由,但是對(duì)于一般的單元測(cè)試,它不必要。有時(shí)甚至有害,因?yàn)楦L(zhǎng)的周轉(zhuǎn)時(shí)間。換言之,我們應(yīng)該使用更容易支持編寫普通單元測(cè)試的方式構(gòu)建Spring實(shí)例。

Spring Boot Test Starter附帶MockitoAssertJ作為測(cè)試庫(kù)。讓我們利用這些測(cè)試庫(kù)來(lái)創(chuàng)建富有表現(xiàn)力的單元測(cè)試!

到此這篇關(guān)于使用Spring Boot進(jìn)行單元測(cè)試詳情的文章就介紹到這了,更多相關(guān)Spring Boot單元測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之在線蛋糕銷售商城的實(shí)現(xiàn)

    Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之在線蛋糕銷售商城的實(shí)現(xiàn)

    這是一個(gè)使用了java+JSP+Springboot+maven+mysql+ThymeLeaf+FTP開(kāi)發(fā)的在線蛋糕銷售商城,是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有線上蛋糕商城該有的所有功能,感興趣的朋友快來(lái)看看吧
    2022-01-01
  • IDEA設(shè)置JVM運(yùn)行參數(shù)的方法步驟

    IDEA設(shè)置JVM運(yùn)行參數(shù)的方法步驟

    這篇文章主要介紹了IDEA設(shè)置JVM運(yùn)行參數(shù)的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • JAVAlogback日志管理詳解

    JAVAlogback日志管理詳解

    本篇文章主要介紹了在SpringBoot中使用Logback管理記錄日志,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-09-09
  • Java多線程中斷機(jī)制三種方法及示例

    Java多線程中斷機(jī)制三種方法及示例

    這篇文章主要介紹了Java多線程中斷機(jī)制三種方法及示例,向大家分享了這三種方法的介紹幾代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • 基于java構(gòu)造方法Vevtor添加元素源碼分析

    基于java構(gòu)造方法Vevtor添加元素源碼分析

    這篇文章主要介紹了基于java構(gòu)造方法中對(duì)Vevtor添加元素的源碼分析,有需要的朋友可以借鑒參考下,希望可以對(duì)大家有所幫助,祝大家早日升職加薪
    2021-09-09
  • 關(guān)于JDK源碼中的@author unascribed注釋閑談

    關(guān)于JDK源碼中的@author unascribed注釋閑談

    這篇文章主要介紹了關(guān)于JDK源碼中的@author unascribed注釋閑談,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • JAVA的LIST接口的REMOVE重載方法調(diào)用原理解析

    JAVA的LIST接口的REMOVE重載方法調(diào)用原理解析

    這篇文章主要介紹了JAVA的LIST接口的REMOVE重載方法調(diào)用原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • java解壓zip文件示例

    java解壓zip文件示例

    這篇文章主要介紹了java解壓zip文件示例,在獲得一個(gè)以Zip格式壓縮的文件之后,需要將其進(jìn)行解壓縮,還原成壓縮前的文件,下面是代碼示例
    2014-03-03
  • Java Eureka探究細(xì)枝末節(jié)

    Java Eureka探究細(xì)枝末節(jié)

    Eureka是Netflix開(kāi)發(fā)的服務(wù)發(fā)現(xiàn)框架,本身是一個(gè)基于REST的服務(wù),主要用于定位運(yùn)行在AWS域中的中間層服務(wù),以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的
    2022-09-09
  • Java 工具類總結(jié)目錄(分享)

    Java 工具類總結(jié)目錄(分享)

    下面小編就為大家?guī)?lái)一篇Java 工具類總結(jié)目錄(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05

最新評(píng)論