日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
三個要點,掌握Spring Boot單元測試

單元測試是軟件開發(fā)中不可或缺的重要環(huán)節(jié),它用于驗證軟件中最小可測試單元的準確性。結(jié)合運用Spring Boot、JUnit、Mockito和分層架構(gòu),開發(fā)人員可以更便捷地編寫可靠、可測試且高質(zhì)量的單元測試代碼,確保軟件的正確性和質(zhì)量。

一、介紹

本文將從與單元測試相關(guān)的技術(shù)主題開始,在技術(shù)部分之后,介紹使用Spring Boot、JUnit和Mockito進行單元測試的實踐。

二、測試的關(guān)鍵要素

1.單元

單元測試中的單元一詞指的是軟件中可以單獨測試和處理的最小功能部分,通常是指函數(shù)、方法、類或模塊等獨立的代碼片段。

2.用例

用例描述了系統(tǒng)使用特定功能或特性的方式,用于理解、設(shè)計和測試軟件系統(tǒng)的需求。通常包括用戶如何與系統(tǒng)進行交互、對系統(tǒng)的期望以及應(yīng)該實現(xiàn)的結(jié)果等詳細信息。

3.邊界情況

邊界情況指的是軟件必須處理的特定場景,這些場景包括意外或邊界條件,與典型情況有所不同或被認為是罕見的情況。邊界情況可以包括意外用戶登錄、測試限制、異常輸入或其他可能導(dǎo)致系統(tǒng)錯誤或異常行為的情況。在測試過程中,考慮和測試邊界情況是非常重要的,因為它們可以幫助開發(fā)人員發(fā)現(xiàn)潛在的問題并確保系統(tǒng)的魯棒性和穩(wěn)定性。

三、單元測試

單元測試涵蓋了我們可以考慮并編寫的所有可能性。每個單元必須至少有一個測試方法。測試不是為一個方法編寫的,而是為一個單元編寫的。

可以按照以下順序編寫單元測試:正常路徑/用例、邊界情況和異常情況。

這些步驟是必不可少的,這樣做可以確保單元以正確的方式處理輸入,并生成預(yù)期的輸出,展現(xiàn)出預(yù)期的行為。單元測試是及早發(fā)現(xiàn)風(fēng)險和修復(fù)錯誤的最佳方式。通過單元測試,我們可以預(yù)防潛在的意外情況,應(yīng)對生產(chǎn)代碼的變更,確保生產(chǎn)代碼能夠處理各種情況。簡而言之,單元測試確保了生產(chǎn)代碼的安全性。

關(guān)于單元測試的另一個重要事項是要測試業(yè)務(wù)邏輯,不是在單元測試中測試基礎(chǔ)設(shè)施代碼,基礎(chǔ)設(shè)施代碼可以在集成測試中進行測試。可以考慮使用一些架構(gòu)模式(如洋蔥架構(gòu)、六邊形架構(gòu)等)來將業(yè)務(wù)邏輯與基礎(chǔ)設(shè)施代碼分離。

單元測試的另一個優(yōu)點是速度快,因為它不需要依賴 Spring ApplicationContext。由于上下文的原因,與單元測試相比,同一測試金字塔中的集成測試速度要慢得多。

1.開始編碼

在分層架構(gòu)項目中,業(yè)務(wù)代碼主要位于服務(wù)層。這意味著服務(wù)層具有單元,需要進行測試。讓我們聚焦于最關(guān)鍵的部分。

以下是一段示例代碼:

  @Override
    public String saveUser(User user) {
        validateUser(user);
        try {
            User savedUser = userRepository.save(user);
            return savedUser.getEmail();
        } catch (Exception exception) {
            throw new IllegalArgumentException(E_GENERAL_SYSTEM);
        }
    }

    private void validateUser(User user) {
        if (Objects.isNull(user.getEmail())) {
            throw new IllegalArgumentException(E_USER_EMAIL_MUST_NOT_BE_NULL);
        }
        if (findByEmail(user.getEmail()).isPresent()) {
            throw new IllegalArgumentException(E_USER_ALREADY_REGISTERED);
        }
    }

    @Override
    public Optional findByEmail(String email) {
        return userRepository.findByEmail(email);
    }

上述代碼中有兩個公共方法和一個私有方法,私有方法可以被視為公共方法的一部分。此外,由于代碼的復(fù)雜性和功能需求,還存在許多可能的場景需要編寫多個測試用例來覆蓋各種情況,以確保代碼的正確性。

2.注解

@ExtendWith用于將Mockito庫集成到JUnit測試中。@Test 標記一個方法,使其成為一個測試方法,測試方法包含指定的測試用例,并由 JUnit 自動運行。

在測試過程中,需要模擬正在測試的類的依賴項。之前提到的原因是,由于 Spring ApplicationContext 不會啟動,我們無法將依賴項注入到上下文中。@Mock 用于創(chuàng)建一個模擬的依賴項,而 @InjectMocks 則用于將這些模擬的依賴項注入到被測試類中。

@BeforeEach和@AfterEach可用于在每個方法運行之前和之后執(zhí)行相應(yīng)的操作。

@ParameterizedTest 用于使用不同的參數(shù)值運行重復(fù)的測試用例。通過使用 @ValueSource,可以為方法提供不同的參數(shù)值,以便進行多次測試。

3.測試方法的三個主要階段

  • Given: 準備測試用例所需的對象
  • When: 執(zhí)行必要的操作以運行測試場景
  • Then: 檢查或驗證預(yù)期結(jié)果

doReturn/when 用于確定在給定指定參數(shù)時方法的行為方式。但是,由于依賴項是 @Mock,并不會真正執(zhí)行。

verify 用于檢查被測試代碼是否按照預(yù)期行為執(zhí)行。如果要測試的方法是 public void 類型,可以使用 verify 進行驗證。

斷言用于驗證預(yù)期結(jié)果。

 @ExtendWith(MockitoExtension.class)
class UserServiceImplTest {

    @InjectMocks
    private UserServiceImpl userService;

    @Mock
    private UserRepository userRepository;

    private User user;
    public static final String MOCK_EMAIL = "mert@bahardogan.com";

    @BeforeEach
    void setUp() {
        user = new User();
        System.out.println("init");
    }

    @AfterEach
    void teardown() {
        System.out.println("teardown");
    }

    @ParameterizedTest
    @ValueSource(strings = {"mert@bahardogan.com", "info@gmail.com"})
    @DisplayName("Happy Path: save user use cases")
    void givenCorrectUser_whenSaveUser_thenReturnUserEmail(String email) {
        // given
        user.setUserName("mertbahardogan").setEmail(email).setPassword("pass");
        User savedUser = new User().setEmail(email);
        doReturn(savedUser).when(userRepository).save(any());

        // when
        String savedUserEmail = userService.saveUser(user);

        // then
        verify(userRepository,times(1)).findByEmail(anyString());
        verify(userRepository,times(1)).save(any());
        assertEquals(email, savedUserEmail);
    }

    @Test
    @DisplayName("Exception Test: user email must not be null case")
    void givenNullUserEmail_whenSaveUser_thenThrowsEmailMustNotNullEx() {
        // when
        Exception exception = assertThrows(IllegalArgumentException.class, () -> userService.saveUser(user));

        // then
        assertNotNull(exception);
        assertEquals(E_USER_EMAIL_MUST_NOT_BE_NULL, exception.getMessage());
    }

    @Test
    @DisplayName("Exception Test: user is already registered case")
    void givenRegisteredUser_whenSaveUser_thenThrowsUserAlreadyRegisteredEx() {
        // given
        user.setEmail(MOCK_EMAIL);
        Optional savedUser = Optional.of(new User().setEmail(MOCK_EMAIL));
        doReturn(savedUser).when(userRepository).findByEmail(anyString());

        // when
        Exception exception = assertThrows(IllegalArgumentException.class, () -> userService.saveUser(user));

        // then
        assertNotNull(exception);
        assertEquals(E_USER_ALREADY_REGISTERED, exception.getMessage());
    }

    @Test
    @DisplayName("Exception Test: catch case")
    void givenIncorrectDependencies_whenSaveUser_thenThrowsGeneralSystemEx() {
        // given
        user.setEmail(MOCK_EMAIL);

        // when
        Exception exception = assertThrows(IllegalArgumentException.class, () -> userService.saveUser(user));

        // then
        assertNotNull(exception);
        assertEquals(E_GENERAL_SYSTEM, exception.getMessage());
    }

    @Test
    @DisplayName("Happy Path: find user by email")
    void givenCorrectUser_whenFindByEmail_thenReturnUserEmail() {
        // given
        Optional savedUser = Optional.of(new User().setEmail(MOCK_EMAIL));
        doReturn(savedUser).when(userRepository).findByEmail(anyString());

        // when
        Optional user = userService.findByEmail(MOCK_EMAIL);

        // then
        verify(userRepository,times(1)).findByEmail(anyString());
        assertEquals(savedUser, user);
    }
}

UserServiceImpl測試類運行時長為1秒693毫秒。


分享題目:三個要點,掌握Spring Boot單元測試
本文地址:http://m.5511xx.com/article/dhpecoe.html