欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot web項(xiàng)目的TDD流程

 更新時(shí)間:2021年05月31日 08:53:25   作者:矚目學(xué)碼  
TDD(Test-driven development) 測(cè)試驅(qū)動(dòng)開(kāi)發(fā),簡(jiǎn)單點(diǎn)說(shuō)就是編寫(xiě)測(cè)試,再編寫(xiě)代碼。這是首要一條,不可動(dòng)搖的一條,先寫(xiě)代碼后寫(xiě)測(cè)試的都是假TDD。

概述

測(cè)試驅(qū)動(dòng)開(kāi)發(fā)可以分為三個(gè)周期,周而復(fù)始,紅燈-綠燈-重構(gòu)。由以下幾個(gè)步驟構(gòu)成:

  1. 編寫(xiě)測(cè)試
  2. 運(yùn)行所有測(cè)試
  3. 編寫(xiě)代碼
  4. 運(yùn)行所有測(cè)試
  5. 重構(gòu)
  6. 運(yùn)行所有測(cè)試

一開(kāi)始編寫(xiě)測(cè)試,肯定通不過(guò),紅燈狀態(tài),進(jìn)行代碼編寫(xiě),然后運(yùn)行測(cè)試,測(cè)試通不過(guò),測(cè)試通過(guò),即變成綠燈。

測(cè)試不通過(guò),或者需要重構(gòu)代碼,再次運(yùn)行所有測(cè)試代碼...

接下來(lái)通過(guò)一個(gè)簡(jiǎn)單的,一個(gè)RESTful請(qǐng)求的Spring boot web項(xiàng)目,演示和說(shuō)明TDD的過(guò)程。

這個(gè)功能大致是這樣的,一個(gè)simple元素有id和desc兩個(gè)屬性

用戶發(fā)送GET請(qǐng)求http接口 http://localhost:8080/simples 返回所有的simple元素的json數(shù)組

1 技術(shù)工具

  1. JDK8+
  2. Spring Boot 2.1+
  3. maven or Gradle
  4. JPA
  5. JUnit 5+
  6. Mockito
  7. Hamcrest

一個(gè)常見(jiàn)的RESTful請(qǐng)求處理的MVC架構(gòu):

  1. 用戶訪問(wèn)http url
  2. 通過(guò)Controller層接口
  3. Controller層調(diào)用Service的實(shí)現(xiàn)
  4. Service接口通過(guò)Repsoitory層訪問(wèn)數(shù)據(jù)庫(kù),并最終返回?cái)?shù)據(jù)給用戶

2 構(gòu)建Spring Boot工程

構(gòu)建一個(gè)Spring Boot Maven工程,并添加所需的依賴

參考依賴如下

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

3 開(kāi)始編寫(xiě)測(cè)試和代碼

1 Controller

首先編寫(xiě)測(cè)試Controller層的測(cè)試,test代碼區(qū)創(chuàng)建一個(gè)測(cè)試類,SimpleControllerTest

添加兩個(gè)注解 @ExtendWith和@WebMvcTest。

然后添加一個(gè)MockMvc對(duì)象,用來(lái)模擬mvc的請(qǐng)求。單元測(cè)試中,每個(gè)模塊應(yīng)當(dāng)獨(dú)立的測(cè)試,實(shí)際調(diào)用鏈中,Controller依賴Service層,因?yàn)楫?dāng)前測(cè)的是Controller層,對(duì)于Service層的代碼則進(jìn)行mock,這可以使用一個(gè)注解

@MockBean

整個(gè)代碼如下

@ExtendWith({SpringExtension.class})
@WebMvcTest
public class SimpleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    private SimpleService simpleService;

}

SimpleService不存在,編譯不通過(guò),紅燈,則創(chuàng)建它。

如是創(chuàng)建一個(gè)SimpleService作為Service層的Spring bean。

@Service
public class SimpleService {

}

然后編寫(xiě)請(qǐng)求/simples http請(qǐng)求的測(cè)試代碼

    @Test
    void testFindAllSimples() throws Exception {
        List<Simple> simpleList = new ArrayList<>();
        simpleList.add(new Simple(1L,"one"));
        simpleList.add(new Simple(2L,"two"));
        when(simpleService.findAll()).thenReturn(simpleList);

        mockMvc.perform(MockMvcRequestBuilders.get("/simples")
                .contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$", hasSize(2))).andDo(print());
    }

when then結(jié)構(gòu)來(lái)自Mockito框架,when表示了執(zhí)行的條件,then用于執(zhí)行驗(yàn)證,這里的操作對(duì)simpleService.findAll方法結(jié)果進(jìn)行了mock,這里 在這一層不需關(guān)心的simpleService的真實(shí)實(shí)現(xiàn)。后面perform方法 mock了 /simples的請(qǐng)求。

這里報(bào)錯(cuò),紅燈,接下來(lái)編寫(xiě)Simple類的實(shí)現(xiàn)。

@Entity
public class Simple {
    private Long id;
    private String desc;
    
    public Simple(String desc) {
        this.desc = desc;
    }
    
}

因?yàn)閟impleService.findAll方法未定義,所以還是報(bào)錯(cuò)的,紅燈。接下來(lái)保持簡(jiǎn)單,給SimpleService創(chuàng)建一個(gè)findAll方法。

    public List<Simple> findAll() {
        return new ArrayList<>();
    }

編譯問(wèn)題都解決了,下面開(kāi)始運(yùn)行測(cè)試代碼。

報(bào)錯(cuò),

java.lang.AssertionError: No value at JSON path “$”

還是紅燈,這是因?yàn)槲覀僲ock的perform 沒(méi)有存在。接下來(lái)創(chuàng)建一個(gè)SimpleController類作為RestController,并編寫(xiě)/simples請(qǐng)求的接口。

@RestController
public class SimpleController {

    @Autowired
    private SimpleService simpleService;

    @GetMapping("/simples")
    public ResponseEntity<List<Simple>> getAllSimples() {
        return new ResponseEntity<>(simpleService.findAll(), HttpStatus.OK);

    }
}

再次運(yùn)行測(cè)試用例,測(cè)試都通過(guò)了,綠燈。

2 Service

接下來(lái)讓我們關(guān)注Service層的代碼測(cè)試,test代碼區(qū)創(chuàng)建一個(gè)SimpleServiceTest類。該類對(duì)下一層Repository依賴,同樣的,創(chuàng)建一個(gè)Repository的mock對(duì)象。

@SpringBootTest
public class SimpleServiceTest {

    @MockBean
    private SimpleRepository simpleRepository;

}

編譯報(bào)錯(cuò),紅燈,需要?jiǎng)?chuàng)建一個(gè)SimpleRepository。

@Repository
public interface SimpleRepository extends JpaRepository<Simple,Long> {
}

以上,創(chuàng)建SimpleRepository作為實(shí)體Simple類對(duì)象的JPA存儲(chǔ)服務(wù)。

編寫(xiě)測(cè)試代碼

    @Test
    void testFindAll() {
        Simple simple = new Simple("one");
        simpleRepository.save(simple);
        SimpleService simpleService = new SimpleService(simpleRepository);
        List<Simple> simples = simpleService.findAll();
        Simple entity = simples.get(simples.size() - 1);
        assertEquals(simple.getDesc(),entity.getDesc());
        assertEquals(simple.getId(),entity.getId());
    }

繼續(xù)解決編譯報(bào)錯(cuò)的問(wèn)題,SimpleService沒(méi)有構(gòu)造方法。添加Repository 并注入bean。

@Service
public class SimpleService {

    private SimpleRepository simpleRepository;



    public SimpleService(SimpleRepository simpleRepository) {
        this.simpleRepository = simpleRepository;
    }

    public List<Simple> findAll() {
        return new ArrayList<>();
    }
}

這里插播一個(gè)題外話,為啥Spring推薦通過(guò)構(gòu)造方法的方式注入bean, 方便編寫(xiě)可測(cè)試代碼是個(gè)重要原因。

運(yùn)行測(cè)試用例,會(huì)繼續(xù)報(bào)錯(cuò),這里是因?yàn)镴PA hibernate沒(méi)有和實(shí)體類對(duì)象交互,需要添加主鍵注解,默認(rèn)構(gòu)造函數(shù) getter/setter 重新編寫(xiě)實(shí)體類的代碼。

@Entity
public class Simple {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String desc;

    public Simple() {
    }

    public Simple(String desc) {
        this.desc = desc;
    }

   // 省略 getter/setter ...
   

}

修改完畢之后 運(yùn)行測(cè)試用例 依然失敗,findAll方法測(cè)試未通過(guò),修改SimpleService的findAll方法,調(diào)用 jpa repository的findAll方法

    public List<Simple> findAll() {
        return simpleRepository.findAll();
    }

現(xiàn)在再次運(yùn)行測(cè)試用例,測(cè)試通過(guò)。

3 Repository

前面已經(jīng)通過(guò)了TDD去實(shí)現(xiàn)Controller層和Service層的代碼,理論上Repository實(shí)現(xiàn)了JPA的接口,我們沒(méi)有做任何代碼的編寫(xiě),應(yīng)該不需要進(jìn)行測(cè)試,但是我們不確定數(shù)據(jù)是否通過(guò)數(shù)據(jù)庫(kù)進(jìn)行了存儲(chǔ)和查詢。為了保證數(shù)據(jù)庫(kù)存儲(chǔ),將真正的JPA respoitory實(shí)例注入的Service對(duì)象中。修改@MockBean 為@Autowired。

@SpringBootTest
public class SimpleServiceTest {

    @Autowired
    private SimpleRepository simpleRepository;

    @Test
    void testFindAll() {
        Simple simple = new Simple("one");
        simpleRepository.save(simple);
        SimpleService simpleService = new SimpleService(simpleRepository);
        List<Simple> simpleEntities = simpleService.findAll();
        Simple entity = simpleEntities.get(simpleEntities.size() - 1);
        assertEquals(simple.getDesc(),entity.getDesc());
        assertEquals(simple.getId(),entity.getId());
    }
}

創(chuàng)建H2 database配置。

classpath下 創(chuàng)建schema.sql和data.sql,創(chuàng)建表和插入一點(diǎn)數(shù)據(jù)。

#************H2  Begin****************
#創(chuàng)建表的MySql語(yǔ)句位置
spring.datasource.schema=classpath:schema.sql
#插入數(shù)據(jù)的MySql語(yǔ)句的位置
spring.datasource.data=classpath:data.sql
# 禁止自動(dòng)根據(jù)entity創(chuàng)建表結(jié)構(gòu),表結(jié)構(gòu)由schema.sql控制
spring.jpa.hibernate.ddl-auto=none

spring.jpa.show-sql=true

schema.sql

DROP TABLE IF EXISTS simple;

CREATE TABLE `simple` (
 id  BIGINT(20) auto_increment,
 desc varchar(255)
);

data.sql

INSERT INTO `simple`(`desc`) VALUES ('test1');
INSERT INTO `simple`(`desc`) VALUES ('test2');

繼續(xù)運(yùn)行測(cè)試用例,所有用例都測(cè)試通過(guò),瀏覽器直接訪問(wèn)localhost:8080/simples

返回data.sql插入的數(shù)據(jù)

[
    {
		"id": 1,
		"desc": "test1"
	},
	{
		"id": 2,
		"desc": "test2"
	}
]

4 總結(jié)

以上是一個(gè)完整的TDD開(kāi)發(fā)流程的演示,每一個(gè)模塊的測(cè)試具備獨(dú)立性,當(dāng)前模塊中,可以mock其他模塊的數(shù)據(jù)。關(guān)于測(cè)試用例的結(jié)構(gòu),遵循的是AAA模式。

  1. Arrange: 單元測(cè)試的第一步,需要進(jìn)行必要的測(cè)試設(shè)置,譬如創(chuàng)建目標(biāo)類對(duì)象,必要時(shí),創(chuàng)建mock對(duì)象和其他變量初始化等等
  2. Action: 調(diào)用要測(cè)試的目標(biāo)方法
  3. Assert: 單元測(cè)試的最后異步,檢查并驗(yàn)證結(jié)果與預(yù)期的結(jié)果是否一致。

以上就是Spring Boot web項(xiàng)目的TDD流程的詳細(xì)內(nèi)容,更多關(guān)于Spring Boot web項(xiàng)目TDD的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 通過(guò)實(shí)例了解java spring使用構(gòu)造器注入的原因

    通過(guò)實(shí)例了解java spring使用構(gòu)造器注入的原因

    這篇文章主要介紹了通過(guò)實(shí)例了解spring使用構(gòu)造器注入的原因,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Spring Boot 與 Kotlin 使用Redis數(shù)據(jù)庫(kù)的配置方法

    Spring Boot 與 Kotlin 使用Redis數(shù)據(jù)庫(kù)的配置方法

    Redis是目前業(yè)界使用最廣泛的內(nèi)存數(shù)據(jù)存儲(chǔ)。下面通過(guò)本文給大家介紹Spring Boot 與 Kotlin 使用Redis數(shù)據(jù)庫(kù)的配置方法,感興趣的朋友一起看看吧
    2018-01-01
  • 詳解JVM棧溢出和堆溢出

    詳解JVM棧溢出和堆溢出

    今天帶大家學(xué)習(xí)的是Java的相關(guān)知識(shí),文章圍繞著JVM棧溢出和堆溢出展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • 關(guān)于Unsupported major.minor version 49.0的錯(cuò)誤解決辦法

    關(guān)于Unsupported major.minor version 49.0的錯(cuò)誤解決辦法

    這篇文章主要介紹了關(guān)于Unsupported major.minor version 49.0的錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • 解析rainbond以應(yīng)用為中心的架構(gòu)設(shè)計(jì)原理

    解析rainbond以應(yīng)用為中心的架構(gòu)設(shè)計(jì)原理

    這篇文章主要為大家介紹了rainbond以應(yīng)用為中心的架構(gòu)設(shè)計(jì)實(shí)現(xiàn)及原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-02-02
  • 一篇文章帶你入門(mén)Java之編程規(guī)范

    一篇文章帶你入門(mén)Java之編程規(guī)范

    這篇文章主要介紹了如何養(yǎng)成良好java代碼編碼規(guī)范,規(guī)范需要平時(shí)編碼過(guò)程中注意,是一個(gè)慢慢養(yǎng)成的好習(xí)慣,下面小編就帶大家來(lái)一起詳細(xì)了解一下吧
    2021-08-08
  • SpringBoot+aop實(shí)現(xiàn)主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離操作

    SpringBoot+aop實(shí)現(xiàn)主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離操作

    讀寫(xiě)分離的作用是為了緩解寫(xiě)庫(kù),也就是主庫(kù)的壓力,但一定要基于數(shù)據(jù)一致性的原則,就是保證主從庫(kù)之間的數(shù)據(jù)一定要一致,這篇文章給大家介紹SpringBoot+aop實(shí)現(xiàn)主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離操作,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • Java實(shí)戰(zhàn)之客戶信息管理系統(tǒng)

    Java實(shí)戰(zhàn)之客戶信息管理系統(tǒng)

    這篇文章主要介紹了Java實(shí)戰(zhàn)之客戶信息管理系統(tǒng),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • Java設(shè)計(jì)模式中的代理設(shè)計(jì)模式詳細(xì)解析

    Java設(shè)計(jì)模式中的代理設(shè)計(jì)模式詳細(xì)解析

    這篇文章主要介紹了Java設(shè)計(jì)模式中的代理設(shè)計(jì)模式詳細(xì)解析,代理模式,重要的在于代理二字,何為代理,我們可以聯(lián)想到生活中的例子,比如秘書(shū)、中介這類職業(yè),我們可以委托中介去幫我們完成某些事情,而我們自己只需要關(guān)注我們必須完成的事情,需要的朋友可以參考下
    2023-12-12
  • Springboot?配置SqlSessionFactory方式

    Springboot?配置SqlSessionFactory方式

    這篇文章主要介紹了Springboot?配置SqlSessionFactory方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論