Java 后端自動(dòng)化測(cè)試

cover

原文地址:https://alphahinex.github.io/2024/08/11/java-backend-test-automation/


description: "本文介紹了 Java 后端自動(dòng)化測(cè)試相關(guān)概念怀估、工具和示例代碼吮便。"
date: 2024.08.11 10:26
categories:
- Test
tags: [Java, Automation test]
keywords: JUnit, DbUnit, Mockito, JaCoCo, Maven


自動(dòng)化測(cè)試相關(guān)概念

測(cè)試用例

測(cè)試用例是一組輸入愉耙、執(zhí)行條件和預(yù)期結(jié)果的集合跌榔,用于驗(yàn)證軟件系統(tǒng)的正確性象踊。

自動(dòng)化測(cè)試

自動(dòng)化測(cè)試是指使用自動(dòng)化工具或腳本來(lái)執(zhí)行測(cè)試用例对湃,以減少人工測(cè)試的工作量哟沫,提高測(cè)試效率和準(zhǔn)確性鸠珠。

測(cè)試金字塔

測(cè)試金字塔 是一種指導(dǎo)自動(dòng)化測(cè)試策略的框架巍耗,它建議在不同層次上分配不同數(shù)量和類型的測(cè)試,以確保成本效益渐排、減輕團(tuán)隊(duì)負(fù)擔(dān)并提高測(cè)試準(zhǔn)確性炬太。這個(gè)概念最初由 Mike Cohn 提出,主要分為三個(gè)層次:?jiǎn)卧獪y(cè)試(Unit Tests)飞盆、服務(wù)測(cè)試(Service Tests娄琉,也稱為集成測(cè)試)、以及用戶界面測(cè)試(UI Tests)吓歇。越靠近塔底的測(cè)試類型執(zhí)行的速度越快孽水、越穩(wěn)定(不易發(fā)生變化);越靠近塔尖的測(cè)試類型編寫(xiě)成本越高城看、收益越低女气。

人們對(duì)測(cè)試金字塔中測(cè)試的類型有不同的劃分,但是總體的思想是一致的测柠。

pyramid

單元測(cè)試

單元測(cè)試是金字塔的基礎(chǔ)層炼鞠,它們不依賴外部資源(如數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)等)快速轰胁、獨(dú)立谒主,并且數(shù)量眾多,專注于單個(gè)代碼單元的行為驗(yàn)證赃阀。

集成測(cè)試

集成測(cè)試位于中間層霎肯,測(cè)試不同組件之間的交互,數(shù)量相對(duì)較少。

用戶界面測(cè)試

UI測(cè)試或端到端測(cè)試位于金字塔的頂層观游,覆蓋從用戶角度的完整交互流程搂捧,但數(shù)量最少,因?yàn)樗鼈兂杀靖咔揖S護(hù)難度大懂缕。

Mocking & Stubbing

Mocking(模擬)是指創(chuàng)建一個(gè)模擬對(duì)象來(lái)代替實(shí)際的依賴對(duì)象允跑。這個(gè)模擬對(duì)象會(huì)按照測(cè)試的需要來(lái)行為,通常用于驗(yàn)證被測(cè)試代碼是否按照預(yù)期與依賴項(xiàng)交互搪柑。

Stubbing(存根)與 Mocking 類似聋丝,但更側(cè)重于提供預(yù)定義的返回值或行為,而不是驗(yàn)證交互拌屏。Stub 對(duì)象用于替換實(shí)際的依賴對(duì)象潮针,以便在測(cè)試中控制或預(yù)測(cè)它們的輸出。

在實(shí)際的軟件開(kāi)發(fā)中倚喂,Mocking 和 Stubbing 通常結(jié)合使用每篷,以創(chuàng)建一個(gè)可控的測(cè)試環(huán)境。

區(qū)別

  • 目的:Mocking 主要用于驗(yàn)證代碼與依賴項(xiàng)的交互端圈,而 Stubbing 主要用于控制測(cè)試環(huán)境焦读,提供可預(yù)測(cè)的輸出。
  • 行為:Mock 可以在測(cè)試中模擬更復(fù)雜的行為舱权,如條件返回或引發(fā)異常矗晃,而 Stub 通常只提供簡(jiǎn)單的固定返回值。
  • 驗(yàn)證:Mock 對(duì)象可以在測(cè)試后驗(yàn)證方法是否被正確調(diào)用宴倍,包括調(diào)用次數(shù)和參數(shù)张症,而 Stub 通常不進(jìn)行這種驗(yàn)證。

測(cè)試覆蓋率

測(cè)試覆蓋率鸵贬,反映了測(cè)試用例對(duì)軟件代碼的覆蓋程度俗他,通常以百分比來(lái)表示。

測(cè)試覆蓋率是一種度量標(biāo)準(zhǔn)阔逼,用于衡量測(cè)試是否覆蓋了代碼的各個(gè)部分兆衅,例如語(yǔ)句覆蓋、分支覆蓋嗜浮、條件覆蓋羡亩、路徑覆蓋等。

測(cè)試覆蓋率越高危融,意味著測(cè)試用例覆蓋的代碼越多畏铆,但并不意味著測(cè)試用例的質(zhì)量越高,100% 的測(cè)試覆蓋率也不能保證軟件完全沒(méi)有缺陷吉殃,所以在設(shè)計(jì)測(cè)試用例時(shí)及志,應(yīng)該注重測(cè)試用例的質(zhì)量片排。

測(cè)試驅(qū)動(dòng)開(kāi)發(fā)

測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(Test-Driven Development寨腔,簡(jiǎn)稱TDD)是一種軟件開(kāi)發(fā)流程速侈,其核心理念是先編寫(xiě)測(cè)試用例,再編寫(xiě)能夠通過(guò)這些測(cè)試用例的代碼迫卢。TDD的目的是確保代碼的可測(cè)試性倚搬、可維護(hù)性和質(zhì)量。

自動(dòng)化測(cè)試常用工具

Build Tool

通常情況下乾蛤,構(gòu)建工具(如 Maven每界、Gradle)會(huì)在項(xiàng)目構(gòu)建過(guò)程中自動(dòng)執(zhí)行測(cè)試用例。

以 Maven 為例家卖,可在 https://start.spring.io/ 生成一個(gè) Spring Boot 項(xiàng)目眨层,解壓后可以找到一個(gè) src/test/java/com/example/demo/DemoApplicationTests.java 測(cè)試類:

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
    }

}

使用 Maven 運(yùn)行測(cè)試用例:

$ mvn test
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.demo.DemoApplicationTests
...
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.843 s -- in com.example.demo.DemoApplicationTests
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

執(zhí)行 mvn package 命令時(shí)也會(huì)自動(dòng)執(zhí)行測(cè)試用例,如果測(cè)試用例失敗上荡,構(gòu)建過(guò)程會(huì)終止趴樱。如果需要跳過(guò)測(cè)試用例,可以使用 -DskipTests 參數(shù):

mvn package -DskipTests

JUnit

JUnit 是一個(gè) Java 編程語(yǔ)言的單元測(cè)試框架酪捡,用于編寫(xiě)和運(yùn)行重復(fù)測(cè)試叁征。JUnit 提供了注解和斷言來(lái)編寫(xiě)測(cè)試用例,可以方便地進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā)逛薇。

當(dāng)前 Junit 的主要版本是 JUnit 5捺疼,上一個(gè)主要版本 JUnit 4 的最后發(fā)布版 4.13.2 是 2021 年發(fā)布的。

JUnit5

不同于之前版本的 JUnit永罚,JUnit 5 是由三個(gè)不同的子項(xiàng)目組成的模塊化測(cè)試框架:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform 負(fù)責(zé)在 JVM 中啟動(dòng)測(cè)試框架啤呼。它定義了 TestEngine API 用來(lái)開(kāi)發(fā)可在其平臺(tái)上運(yùn)行的測(cè)試框架。
  • JUnit Jupiter 包含了對(duì) JUnit 5 新注解的支持呢袱,并提供了一個(gè)能夠運(yùn)行 JUnit 5 測(cè)試用例的 TestEngine 實(shí)現(xiàn)官扣。
  • JUnit Vintage 提供了用于運(yùn)行 JUnit 3 和 JUnit 4 的測(cè)試用例的 TestEngine 實(shí)現(xiàn)。
JUnit 5 常用注解 作用 JUnit 4 對(duì)應(yīng)注解
@Test 標(biāo)記一個(gè)方法是測(cè)試方法 @Test
@BeforeEach 每個(gè)測(cè)試方法之前都執(zhí)行的方法 @Before
@AfterEach 每個(gè)測(cè)試方法之后都執(zhí)行的方法 @After
@BeforeAll 在所有測(cè)試方法之前執(zhí)行一次的方法产捞,需要 static @BeforeClass
@AfterAll 在所有測(cè)試方法之后執(zhí)行一次的方法醇锚,需要 static @AfterClass
@Disabled 禁用測(cè)試類或方法 @Ignore

更多注解可見(jiàn) 2.1. Annotations

JUnit5 基礎(chǔ)注解

package com.example.demo;

import org.junit.jupiter.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    private static Logger LOGGER = LoggerFactory.getLogger(DemoApplicationTests.class);

    @BeforeAll
    static void setup() {
        LOGGER.info("@BeforeAll - executes once before all test methods in this class");
    }

    @BeforeEach
    void init() {
        LOGGER.info("@BeforeEach - executes before each test method in this class");
    }

    @AfterEach
    void tearDown() {
        LOGGER.info("@AfterEach - executed after each test method.");
    }

    @AfterAll
    static void done() {
        LOGGER.info("@AfterAll - executed after all test methods.");
    }

    @Test
    void contextLoads() {
    }

    @DisplayName("Single test successful")
    @Test
    void testSingleSuccessTest() {
        LOGGER.info("Success");
    }

    @Test
    @Disabled("Not implemented yet")
    void testShowSomething() {
    }

}
$ mvn test
...
17:34:33.848 [main] INFO com.example.demo.DemoApplicationTests -- @BeforeAll - executes once before all test methods in this class

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.2)

2024-08-08T17:34:34.251+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : Starting DemoApplicationTests using Java 17.0.2 with PID 35736 (started by alphahinex in /Users/alphahinex/Desktop/demo)
2024-08-08T17:34:34.253+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : No active profile set, falling back to 1 default profile: "default"
2024-08-08T17:34:34.957+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : Started DemoApplicationTests in 1.063 seconds (process running for 2.405)
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2024-08-08T17:34:35.904+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : @BeforeEach - executes before each test method in this class
2024-08-08T17:34:35.911+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : @AfterEach - executed after each test method.
2024-08-08T17:34:35.933+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : @BeforeEach - executes before each test method in this class
2024-08-08T17:34:35.934+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : Success
2024-08-08T17:34:35.935+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : @AfterEach - executed after each test method.
2024-08-08T17:34:35.940+08:00  INFO 35736 --- [demo] [           main] com.example.demo.DemoApplicationTests    : @AfterAll - executed after all test methods.
[WARNING] Tests run: 3, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 2.521 s -- in com.example.demo.DemoApplicationTests
[INFO]
[INFO] Results:
[INFO]
[WARNING] Tests run: 3, Failures: 0, Errors: 0, Skipped: 1
...

從輸出的日志信息可以看到坯临,@BeforeAll@AfterAll 的日志只打印了一次焊唬,@BeforeEach@AfterEach 的日志在每個(gè)沒(méi) @Disabled@Test 方法執(zhí)行前后都會(huì)打印。

JUnit5 斷言

斷言是測(cè)試用例最重要的組成部分看靠。

斷言可以用來(lái)驗(yàn)證方法的行為是否符合預(yù)期赶促,并在斷言失敗時(shí)使測(cè)試用例失敗,進(jìn)而體現(xiàn)到最終的測(cè)試報(bào)告中挟炬。

可以說(shuō)沒(méi)有斷言的測(cè)試用例沒(méi)有任何意義鸥滨,因?yàn)闇y(cè)試用例始終會(huì)執(zhí)行通過(guò)嗦哆。

JUnit 5 的斷言都包含在 org.junit.jupiter.api.Assertions 類的靜態(tài)方法中,并支持了 Lambda 表達(dá)式等 Java 新特性婿滓,常見(jiàn)的斷言包括:

  • assertTrue:用于驗(yàn)證條件是否為 true老速。
  • assertFalse:用于驗(yàn)證條件是否為 false
  • assertNull:用于驗(yàn)證對(duì)象是否為 null凸主。
  • assertNotNull:用于驗(yàn)證對(duì)象是否不為 null橘券。
  • assertEquals:用于驗(yàn)證兩個(gè)對(duì)象是否相等。
  • assertNotEquals:用于驗(yàn)證兩個(gè)對(duì)象是否不相等卿吐。
  • assertArrayEquals:用于驗(yàn)證兩個(gè)數(shù)組是否相等旁舰。
  • assertSame:用于驗(yàn)證兩個(gè)對(duì)象是否是同一個(gè)對(duì)象。
  • assertNotSame:用于驗(yàn)證兩個(gè)對(duì)象是否不是同一個(gè)對(duì)象嗡官。
  • assertThrows:用于驗(yàn)證方法是否拋出了指定的異常箭窜。
  • assertAll:用于組合多個(gè)斷言,當(dāng)其中一個(gè)斷言失敗時(shí)衍腥,后續(xù)斷言不會(huì)執(zhí)行磺樱。
@Test
void groupAssertions() {
    int[] numbers = {0, 1, 2, 3, 4};
    assertNotNull(numbers);
    assertAll("numbers",
        () -> assertEquals(0, numbers[0]),
        () -> assertSame(3, numbers[3]),
        () -> assertArrayEquals(new int[]{0, 1, 2, 3, 4}, numbers)
    );
}

JUnit5 假設(shè)

假設(shè)用來(lái)在測(cè)試方法中定義前提條件,如果假設(shè)不成立紧阔,則測(cè)試方法會(huì)被忽略坊罢。

JUnit 5 的假設(shè)方法包含在 org.junit.jupiter.api.Assumptions 類中,有三類靜態(tài)方法:

  1. assumeTrue:假設(shè)條件為 true擅耽,否則忽略測(cè)試方法活孩。
  2. assumeFalse:假設(shè)條件為 false,否則忽略測(cè)試方法乖仇。
  3. assumingThat:假設(shè)條件為 true憾儒,否則忽略測(cè)試方法。
@Test
void testOnlyOnCiServer() {
    assumeTrue("CI".equals(System.getenv("ENV")));
    // remainder of test
}

JUnit5 驗(yàn)證異常

JUnit 5 中不再使用之前的 @Test(expected = …)ExpectedException 規(guī)則來(lái)設(shè)定期待拋出的異常乃沙。異常的驗(yàn)證都通過(guò) Assertions.assertThrows(…) 方法實(shí)現(xiàn):

@Test
void shouldThrowException() {
    Throwable exception = assertThrows(UnsupportedOperationException.class, () -> {
      throw new UnsupportedOperationException("Not supported");
    });
    assertEquals("Not supported", exception.getMessage());
}

@Test
void assertThrowsException() {
    String str = null;
    assertThrows(IllegalArgumentException.class, () -> {
      Integer.valueOf(str);
    });
}

DbUnit

DbUnit 是一個(gè) JUnit 4 的擴(kuò)展起趾,可以在測(cè)試過(guò)程中基于 XML 數(shù)據(jù)集管控測(cè)試數(shù)據(jù)庫(kù)中數(shù)據(jù)狀態(tài),最后的發(fā)布版本是 2024年06月02日 的 v2.8.0警儒。

基本思路是繼承 DBTestCase 基類后训裆,通過(guò)實(shí)現(xiàn) getDataSet() 方法,將準(zhǔn)備的 XML 格式數(shù)據(jù)文件加載到測(cè)試庫(kù)中蜀铲,之后通過(guò) org.dbunit.Assertion 中的斷言進(jìn)行數(shù)據(jù)驗(yàn)證边琉。

如果想在 JUnit 5 中使用 DbUnit,需要在依賴中添加 JUnit 4 和 JUnit Vintage 引擎:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <scope>test</scope>
</dependency>

以下是一個(gè)使用 DbUnit 的示例:

com.example.demo.DataSourceDBUnitTest:

package com.example.demo;

import org.dbunit.Assertion;
import org.dbunit.DataSourceBasedDBTestCase;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.Test;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DataSourceDBUnitTest extends DataSourceBasedDBTestCase {
    @Override
    protected DataSource getDataSource() {
        JdbcDataSource dataSource = new JdbcDataSource();
        dataSource.setURL(
            "jdbc:h2:mem:default;MODE=LEGACY;DB_CLOSE_DELAY=-1;init=runscript from 'classpath:dbunit/schema.sql'");
        dataSource.setUser("sa");
        dataSource.setPassword("sa");
        return dataSource;
    }

    @Override
    protected IDataSet getDataSet() throws Exception {
        return new FlatXmlDataSetBuilder().build(getClass().getClassLoader()
            .getResourceAsStream("dbunit/data.xml"));
    }

    @Override
    protected DatabaseOperation getSetUpOperation() {
        return DatabaseOperation.REFRESH;
    }

    @Override
    protected DatabaseOperation getTearDownOperation() {
        return DatabaseOperation.DELETE_ALL;
    }

    @Test
    public void testGivenDataSetEmptySchema_whenDataSetCreated_thenTablesAreEqual() throws Exception {
        IDataSet expectedDataSet = getDataSet();
        ITable expectedTable = expectedDataSet.getTable("CLIENTS");
        IDataSet databaseDataSet = getConnection().createDataSet();
        ITable actualTable = databaseDataSet.getTable("CLIENTS");
        Assertion.assertEquals(expectedTable, actualTable);
    }

}

src/test/resources/dbunit/schema.sql:

CREATE TABLE IF NOT EXISTS CLIENTS
(
    `id`         int AUTO_INCREMENT NOT NULL,
    `first_name` varchar(100)       NOT NULL,
    `last_name`  varchar(100)       NOT NULL,
    PRIMARY KEY (`id`)
);

CREATE TABLE IF NOT EXISTS ITEMS
(
    `id`       int AUTO_INCREMENT NOT NULL,
    `title`    varchar(100)       NOT NULL,
    `produced` date,
    `price`    float,
    PRIMARY KEY (`id`)
);

src/test/resources/dbunit/data.xml:

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <CLIENTS id='1' first_name='Charles' last_name='Xavier'/>
    <ITEMS id='1' title='Grey T-Shirt' price='17.99' produced='2019-03-20'/>
    <ITEMS id='2' title='Fitted Hat' price='29.99' produced='2019-03-21'/>
    <ITEMS id='3' title='Backpack' price='54.99' produced='2019-03-22'/>
    <ITEMS id='4' title='Earrings' price='14.99' produced='2019-03-23'/>
    <ITEMS id='5' title='Socks' price='9.99'/>
</dataset>

更多 DbUnit 用法可參見(jiàn) Introduction to DBUnitGetting Started 等文檔记劝。

個(gè)人感覺(jué) Spring Framework 下的 Spring TestContext Framework 中所提供的 Executing SQL Scripts 方式面向 SQL变姨,相比 XML 更加直觀,且無(wú)需引入三方依賴厌丑,對(duì) JUnit 版本也沒(méi)有限制定欧。

Mockito

Mockito 是 Java 生態(tài)常用的 Mock 框架渔呵,用于創(chuàng)建和配置 Mock 對(duì)象,以及驗(yàn)證測(cè)試中的行為砍鸠。Mockito 會(huì)被 Spring Boot Starter 自動(dòng)依賴扩氢,無(wú)需額外引入。

org.mockito.Mockito 類中常用的靜態(tài)方法包括:

  • mock:創(chuàng)建一個(gè) Mock 對(duì)象睦番。
  • verify:驗(yàn)證 Mock 對(duì)象的行為类茂。
  • spy:創(chuàng)建一個(gè)部分 Mock 的對(duì)象,真實(shí)方法會(huì)被調(diào)用托嚣,但依然可以進(jìn)行驗(yàn)證和 stub。
  • when:配置 Mock 對(duì)象的行為厚骗。
@Test
void mockAndVerify() {
    List<String> mockedList = mock(List.class);

    mockedList.add("one");
    mockedList.add("two");
    mockedList.add("two");
    mockedList.add("three");
    verify(mockedList).add("three");

    verify(mockedList, times(2)).add("two");
    verify(mockedList, atLeastOnce()).add("three");
    verify(mockedList, atMost(3)).add("one");
}

@Test
void spyAndStub() {
    List<String> list = new ArrayList<>();
    List<String> spiedList = spy(list);
    spiedList.add("one");
    spiedList.add("two");
    spiedList.add("three");
    assertEquals(3, spiedList.size());

    when(spiedList.get(0)).thenReturn("first");
    assertEquals("first", spiedList.get(0));
    assertEquals("two", spiedList.get(1));
}

JaCoCo

JaCoCo 是 Java 的代碼覆蓋率工具示启,可與 Maven 或 Gradle 集成,用于生成代碼覆蓋率報(bào)告领舰。

在 Maven 中使用 JaCoCo 插件夫嗓,只需在 pom.xml 中添加以下配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.12</version>
            <executions>
                <execution>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
                <execution>
                    <id>report</id>
                    <phase>test</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

執(zhí)行 mvn test 后,JaCoCo 會(huì)生成一個(gè) target/site/jacoco/index.html 的代碼覆蓋率報(bào)告冲秽。

jacoco

示例代碼

完整示例代碼可見(jiàn):https://github.com/AlphaHinex/java-test-demo

相關(guān)資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锉桑,一起剝皮案震驚了整個(gè)濱河市排霉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌民轴,老刑警劉巖攻柠,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異后裸,居然都是意外死亡瑰钮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門微驶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浪谴,“玉大人,你說(shuō)我怎么就攤上這事因苹」冻埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵容燕,是天一觀的道長(zhǎng)梁呈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蘸秘,這世上最難降的妖魔是什么官卡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任蝗茁,我火速辦了婚禮,結(jié)果婚禮上寻咒,老公的妹妹穿的比我還像新娘哮翘。我一直安慰自己,他們只是感情好毛秘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布饭寺。 她就那樣靜靜地躺著,像睡著了一般叫挟。 火紅的嫁衣襯著肌膚如雪艰匙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天抹恳,我揣著相機(jī)與錄音员凝,去河邊找鬼。 笑死奋献,一個(gè)胖子當(dāng)著我的面吹牛健霹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓶蚂,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼糖埋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了窃这?” 一聲冷哼從身側(cè)響起瞳别,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钦听,沒(méi)想到半個(gè)月后洒试,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朴上,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年垒棋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痪宰。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叼架,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衣撬,到底是詐尸還是另有隱情乖订,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布具练,位于F島的核電站乍构,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏扛点。R本人自食惡果不足惜哥遮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一岂丘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧眠饮,春花似錦奥帘、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至扔茅,卻和暖如春已旧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咖摹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工评姨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萤晴。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胁后,于是被迫代替她去往敵國(guó)和親店读。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容