日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

怎样编写测试类测试分支_编写干净的测试–天堂中的麻烦

發布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 怎样编写测试类测试分支_编写干净的测试–天堂中的麻烦 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

怎樣編寫測試類測試分支

如果我們的代碼有明顯的錯誤,我們很有動力對其進行改進。 但是,在某些時候,我們認為我們的代碼“足夠好”并繼續前進。

通常,當我們認為改進現有代碼的好處小于所需的工作時,就會發生這種情況。 當然,如果我們低估了投資回報,我們可能會打錯電話,這會傷害我們。

這就是發生在我身上的事情,因此我決定寫這篇文章,以便您避免犯同樣的錯誤。

編寫“良好”單元測試

如果我們要編寫“好的”單元測試,則必須編寫以下單元測試:

  • 只測試一件事 。 好的單元測試只能因一個原因而失敗,并且只能斷言一件事。
  • 被正確命名 。 測試方法的名稱必須揭示測試失敗的原因。
  • 模擬外部依賴關系(和狀態) 。 如果單元測試失敗,我們將確切知道問題出在哪里。

補充閱讀:

  • 單元測試只能測試一件事情
  • 編寫干凈的測試:命名問題
  • 編寫干凈的測試:分而治之
  • 編寫干凈的測試:驗證或不驗證

如果我們編寫滿足這些條件的單元測試,我們將編寫好的單元測試。 對?

我曾經這樣認為。 現在我對此表示懷疑

善意鋪平地獄之路

我從未見過一個決定編寫糟糕的單元測試的軟件開發人員。 如果開發人員正在編寫單元測試,則他/她很有可能要編寫好的單元測試。 但是,這并不意味著該開發人員編寫的單元測試是好的。

我想編寫既易于閱讀又易于維護的單元測試。 我什至寫了一個教程,描述了如何編寫干凈的測試 。 問題在于,本教程中給出的建議還不夠好(尚未)。 它可以幫助我們入門,但是并沒有顯示出兔子洞的真正深度。

我的教程中描述的方法存在兩個主要問題:

命名標準FTW?

如果我們使用Roy Osherove引入的“命名標準” ,我們注意到很難描述被測狀態和預期行為。

當我們為簡單場景編寫測試時,此命名標準非常有效。 問題在于,真正的軟件并不簡單。 通常,我們最終使用以下兩個選項之一來命名測試方法:

首先 ,如果我們嘗試盡可能具體,那么我們的測試方法的方法名稱就顯得太過looooooooong。 最后,我們必須承認我們不能像我們想要的那樣具體,因為方法名稱會占用太多空間。

其次 ,如果我們嘗試使方法名稱盡可能短,則方法名稱將不會真正描述測試狀態和預期行為。

選擇哪個選項并不重要,因為無論如何我們都會遇到以下問題:

  • 如果測試失敗,則方法名稱不一定表示要出錯。 我們可以使用自定義斷言來解決此問題,但是它們不是免費的。
  • 很難對我們的測試涵蓋的場景進行簡要概述。

這是我們在“ 編寫干凈測試”教程期間編寫的測試方法的名稱:

  • registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldThrowException()
  • registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldNotSaveNewUserAccount()
  • registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldSaveNewUserAccountAndSetSignInProvider()
  • registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldReturnCreatedUserAccount()
  • registerNewUserAccount_SocialSignInAnUniqueEmail_ShouldNotCreateEncodedPasswordForUser()

這些方法的名稱不是很長,但是我們必須記住,編寫這些單元測試是為了測試一種簡單的注冊方法。 當我使用這種命名約定為現實生活中的軟件項目編寫自動化測試時,最長的方法名稱是我們最長的示例名稱的兩倍。

那不是很干凈或可讀。 我們可以做得更好

沒有通用配置

在本教程中,我們使單元測試變得更好了 。 然而,他們仍然遭受這樣的事實,即沒有“自然的”方式在不同的單元測試之間共享配置。

這意味著我們的單元測試包含許多重復的代碼,這些代碼配置了我們的模擬對象并創建了在我們的單元測試中使用的其他對象。

同樣,由于沒有“自然”的方式表明某些常量僅與特定的測試方法相關,因此我們必須將所有常量添加到測試類的開頭。

我們的測試類的源代碼如下(突出顯示有問題的代碼):

import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.springframework.security.crypto.password.PasswordEncoder;import static com.googlecode.catchexception.CatchException.catchException; import static com.googlecode.catchexception.CatchException.caughtException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when;@RunWith(MockitoJUnitRunner.class) public class RepositoryUserServiceTest {private static final String REGISTRATION_EMAIL_ADDRESS = "john.smith@gmail.com";private static final String REGISTRATION_FIRST_NAME = "John";private static final String REGISTRATION_LAST_NAME = "Smith";private static final SocialMediaService SOCIAL_SIGN_IN_PROVIDER = SocialMediaService.TWITTER;private RepositoryUserService registrationService;@Mockprivate PasswordEncoder passwordEncoder;@Mockprivate UserRepository repository;@Beforepublic void setUp() {registrationService = new RepositoryUserService(passwordEncoder, repository);}@Testpublic void registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldThrowException() throws DuplicateEmailException {RegistrationForm registration = new RegistrationFormBuilder().email(REGISTRATION_EMAIL_ADDRESS).firstName(REGISTRATION_FIRST_NAME).lastName(REGISTRATION_LAST_NAME).isSocialSignInViaSignInProvider(SOCIAL_SIGN_IN_PROVIDER).build();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(new User());catchException(registrationService).registerNewUserAccount(registration);assertThat(caughtException()).isExactlyInstanceOf(DuplicateEmailException.class);}@Testpublic void registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldNotSaveNewUserAccount() throws DuplicateEmailException {RegistrationForm registration = new RegistrationFormBuilder().email(REGISTRATION_EMAIL_ADDRESS).firstName(REGISTRATION_FIRST_NAME).lastName(REGISTRATION_LAST_NAME).isSocialSignInViaSignInProvider(SOCIAL_SIGN_IN_PROVIDER).build();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(new User());catchException(registrationService).registerNewUserAccount(registration);verify(repository, never()).save(isA(User.class));}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ ShouldSaveNewUserAccountAndSetSignInProvider() throws DuplicateEmailException {RegistrationForm registration = new RegistrationFormBuilder().email(REGISTRATION_EMAIL_ADDRESS).firstName(REGISTRATION_FIRST_NAME).lastName(REGISTRATION_LAST_NAME).isSocialSignInViaSignInProvider(SOCIAL_SIGN_IN_PROVIDER).build();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);registrationService.registerNewUserAccount(registration);ArgumentCaptor<User> userAccountArgument = ArgumentCaptor.forClass(User.class);verify(repository, times(1)).save(userAccountArgument.capture());User createdUserAccount = userAccountArgument.getValue();assertThatUser(createdUserAccount).hasEmail(REGISTRATION_EMAIL_ADDRESS).hasFirstName(REGISTRATION_FIRST_NAME).hasLastName(REGISTRATION_LAST_NAME).isRegisteredUser().isRegisteredByUsingSignInProvider(SOCIAL_SIGN_IN_PROVIDER);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldReturnCreatedUserAccount() throws DuplicateEmailException {RegistrationForm registration = new RegistrationFormBuilder().email(REGISTRATION_EMAIL_ADDRESS).firstName(REGISTRATION_FIRST_NAME).lastName(REGISTRATION_LAST_NAME).isSocialSignInViaSignInProvider(SOCIAL_SIGN_IN_PROVIDER).build();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);when(repository.save(isA(User.class))).thenAnswer(new Answer<User>() {@Overridepublic User answer(InvocationOnMock invocation) throws Throwable {Object[] arguments = invocation.getArguments();return (User) arguments[0];}});User createdUserAccount = registrationService.registerNewUserAccount(registration);assertThatUser(createdUserAccount).hasEmail(REGISTRATION_EMAIL_ADDRESS).hasFirstName(REGISTRATION_FIRST_NAME).hasLastName(REGISTRATION_LAST_NAME).isRegisteredUser().isRegisteredByUsingSignInProvider(SOCIAL_SIGN_IN_PROVIDER);}@Testpublic void registerNewUserAccount_SocialSignInAnUniqueEmail_ShouldNotCreateEncodedPasswordForUser() throws DuplicateEmailException {RegistrationForm registration = new RegistrationFormBuilder().email(REGISTRATION_EMAIL_ADDRESS).firstName(REGISTRATION_FIRST_NAME).lastName(REGISTRATION_LAST_NAME).isSocialSignInViaSignInProvider(SOCIAL_SIGN_IN_PROVIDER).build();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);registrationService.registerNewUserAccount(registration);verifyZeroInteractions(passwordEncoder);} }

一些開發人員認為看起來像上面示例的單元測試足夠干凈。 我理解這種情緒,因為我曾經是其中之一。 但是,這些單元測試有三個問題:

  • 該案的實質并不盡如人意 。 因為每種測試方法在調用被測試方法并驗證預期結果之前都會進行自我配置,所以我們的測試方法變得比必要的更長。 這意味著我們不能只看一眼隨機測試方法并弄清楚它要測試什么。
  • 編寫新的單元測試很慢 。 因為每個單元測試都必須自行配置,所以向我們的測試套件中添加新的單元測試比它可能要慢得多。 另一個“意外”的缺點是,這種單元測試鼓勵人們練習復制和粘貼編程 。
  • 維持這些單元測試是一件痛苦的事 。 如果我們向注冊表單添加新的必填字段,或者更改registerNewUserAccount()方法的實現,則必須對每個單元測試進行更改。 這些單元測試太脆弱了。
  • 換句話說,這些單元測試很難閱讀,很難編寫和維護。 我們必須做得更好

    摘要

    這篇博客文章教會了我們四件事:

    • 即使我們認為我們正在編寫好的單元測試,也不一定是正確的。
    • 如果由于必須更改許多單元測試而導致更改現有功能的速度很慢,那么我們就不會編寫好的單元測試。
    • 如果添加新功能的速度很慢,因為我們必須向單元測試中添加大量重復的代碼,那么我們就不會編寫好的單元測試。
    • 如果我們看不到單元測試所涵蓋的情況,那么我們就沒有編寫好的單元測試。

    本教程的下一部分將回答這個非常相關的問題:

    如果我們現有的單元測試很爛,我們該如何解決呢?

    如果要編寫干凈的測試,則應閱讀我的“ 編寫干凈的測試”教程 。

    翻譯自: https://www.javacodegeeks.com/2015/03/writing-clean-tests-trouble-in-paradise.html

    怎樣編寫測試類測試分支

    總結

    以上是生活随笔為你收集整理的怎样编写测试类测试分支_编写干净的测试–天堂中的麻烦的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。