在Spring Boot中使用Docker在測試中進行高級功能測試

来源:https://www.cnblogs.com/youruike-/archive/2019/12/28/12112372.html
-Advertisement-
Play Games

最近又學到了很多新知識,感謝優銳課老師細緻地講解,這篇博客記錄下自己所學所想。 想更多地瞭解Spring Boot項目中的功能測試嗎?這篇文章帶你瞭解有關在測試中使用Docker容器的更多信息。 本文重點介紹在Spring Boot應用程式的功能測試期間應用一些最佳實踐。我們將演示一種高級方法,該方 ...


最近又學到了很多新知識,感謝優銳課老師細緻地講解,這篇博客記錄下自己所學所想。

想更多地瞭解Spring Boot項目中的功能測試嗎?這篇文章帶你瞭解有關在測試中使用Docker容器的更多信息。

本文重點介紹在Spring Boot應用程式的功能測試期間應用一些最佳實踐。我們將演示一種高級方法,該方法如何在不設置登臺環境的情況下將服務作為黑盒進行測試。 

理論

讓我們從定義功能測試的含義開始:

功能測試是在軟體開發中使用的軟體測試過程,其中對軟體進行測試以確保其符合所有要求。功能測試是一種檢查軟體的方法,以確保軟體具有其功能要求中指定的所有必需功能。

儘管這有點令人困惑,但請不要擔心——以下定義提供了進一步的解釋:

功能測試主要用於驗證某個軟體所提供的輸出與最終用戶或企業所需的輸出相同。通常,功能測試涉及評估每個軟體功能並將其與業務需求進行比較。通過為軟體提供一些相關的輸入來對其進行測試,以便可以評估輸出以查看其與基本要求相比是否相符,相關或變化。此外,功能測試還檢查軟體的可用性,例如通過確保導航功能按要求工作。

在我們的案例中,我們將微服務作為軟體,應根據最終用戶的要求提供一些輸出。

目的

功能測試應涵蓋我們應用程式的以下方面:

  •  上下文啟動-確保服務在上下文中沒有衝突,並且可以順利引導。
  • 業務需求/用戶案例-這包括請求的功能。

基本上,每個(或大多數)用戶故事都應該有自己專用的功能測試。如果至少有一個功能測試,我們不需要編寫上下文啟動測試,因為它仍然會測試它。

實踐

為了演示如何應用最佳實踐,我們需要編寫一些示例服務。讓我們從頭開始。

任務

我們的新服務要求滿足以下要求:

  • 用於存儲和檢索用戶詳細信息的REST API。
  • REST API,用於通過REST從聯繫人服務獲取豐富的聯繫方式的用戶詳細信息。

架構設計

對於此任務,我們將使用Spring平臺作為框架,並使用Spring Boot作為應用程式引導程式。為了存儲用戶詳細信息,我們將使用MariaDB。

由於該服務應存儲和檢索用戶詳細信息,因此將其命名為“用戶詳細信息”服務是合乎邏輯的。

在實施之前,應製作組件圖以更好地瞭解系統的主要組件:

 

實操

以下示例代碼包含許多Lombok批註。你可以在網站上的docs文件中找到有關每個註釋的說明。

Models

用戶詳細信息模型:

 1 @Value(staticConstructor = "of")
 2 public class UserDetails {
 3     String firstName;
 4     String lastName;
 5     public static UserDetails fromEntity(UserDetailsEntity entity) {
 6         return UserDetails.of(entity.getFirstName(), entity.getLastName());
 7     }
 8     public UserDetailsEntity toEntity(long userId) {
 9         return new UserDetailsEntity(userId, firstName, lastName);
10     }
11 }

 

用戶聯繫人模型:

1 @Value
2 public class UserContacts {
3     String email;
4     String phone;
5 }
6  

 

具有彙總信息的用戶:

1 @Value(staticConstructor = "of")
2 public class User {
3     UserDetails userDetails;
4     UserContacts userContacts;
5 }

 

REST API

 1 @RestController
 2 @RequestMapping("user")
 3 @AllArgsConstructor
 4 public class UserController {
 5     private final UserService userService;
 6     @GetMapping("/{userId}") //1
 7     public User getUser(@PathVariable("userId") long userId) {
 8         return userService.getUser(userId);
 9     }
10     @PostMapping("/{userId}/details") //2
11     public void saveUserDetails(@PathVariable("userId") long userId, @RequestBody UserDetails userDetails) {
12         userService.saveDetails(userId, userDetails);
13     }
14     @GetMapping("/{userId}/details") //3
15     public UserDetails getUserDetails(@PathVariable("userId") long userId) {
16         return userService.getDetails(userId);
17     }
18 }
  1. 按ID獲取用戶彙總數據
  2. 按ID為用戶發佈用戶詳細信息
  3. 通過ID獲取用戶詳細信息

 

聯繫人服務客戶端

 1 @Component
 2 public class ContactsServiceClient {
 3     private final RestTemplate restTemplate;
 4     private final String contactsServiceUrl;
 5     public ContactsServiceClient(final RestTemplateBuilder restTemplateBuilder,
 6                                  @Value("${contacts.service.url}") final String contactsServiceUrl) {
 7         this.restTemplate = restTemplateBuilder.build();
 8         this.contactsServiceUrl = contactsServiceUrl;
 9     }
10     public UserContacts getUserContacts(long userId) {
11         URI uri = UriComponentsBuilder.fromHttpUrl(contactsServiceUrl + "/contacts")
12                 .queryParam("userId", userId).build().toUri();
13         return restTemplate.getForObject(uri, UserContacts.class);
14     }
15 }

 

詳細信息實體及其存儲庫

 1 @Entity
 2 @Data
 3 @NoArgsConstructor
 4 @AllArgsConstructor
 5 public class UserDetailsEntity {
 6     @Id
 7     private Long id;
 8     @Column
 9     private String firstName;
10     @Column
11     private String lastName;
12 }
13 @Repository
14 public interface UserDetailsRepository extends JpaRepository<UserDetailsEntity, Long> {
15 }

 

用戶服務

 1 @Service
 2 @AllArgsConstructor
 3 public class UserService {
 4     private final UserDetailsRepository userDetailsRepository;
 5     private final ContactsServiceClient contactsServiceClient;
 6     public User getUser(long userId) {
 7         UserDetailsEntity userDetailsEntity = userDetailsRepository.getOne(userId); //1
 8         UserDetails userDetails = UserDetails.fromEntity(userDetailsEntity);
 9         UserContacts userContacts = contactsServiceClient.getUserContacts(userId); //2
10         return User.of(userDetails, userContacts); //3
11     }
12     public void saveDetails(long userId, UserDetails userDetails) {
13         UserDetailsEntity entity = userDetails.toEntity(userId);
14         userDetailsRepository.save(entity);
15     }
16     public UserDetails getDetails(long userId) {
17         UserDetailsEntity userDetailsEntity = userDetailsRepository.getOne(userId);
18         return UserDetails.fromEntity(userDetailsEntity);
19     }
20 }
  1. 從資料庫檢索用戶詳細信息
  2. 從通訊錄服務中檢索用戶通訊錄
  3. 向用戶返回彙總數據

 

應用及其屬性

UserDetailsServiceApplication.java

1 @SpringBootApplication
2 public class UserDetailsServiceApplication {
3     public static void main(String[] args) {
4         SpringApplication.run(UserDetailsServiceApplication.class, args);
5     }
6 }

 

application.properties:

 1 #contact service
 2 contacts.service.url=http://www.prod.contact.service.com
 3 #database
 4 user.details.db.host=prod.maria.url.com
 5 user.details.db.port=3306
 6 user.details.db.schema=user_details
 7 spring.datasource.url=jdbc:mariadb://${user.details.db.host}:${user.details.db.port}/${user.details.db.schema}
 8 spring.datasource.username=prod-username
 9 spring.datasource.password=prod-password
10 spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

 

POM文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <artifactId>user-details-service</artifactId>
 6     <version>0.0.1-SNAPSHOT</version>
 7     <packaging>jar</packaging>
 8     <name>User details service</name>
 9     <parent>
10         <groupId>com.tdanylchuk</groupId>
11         <artifactId>functional-tests-best-practices</artifactId>
12         <version>0.0.1-SNAPSHOT</version>
13     </parent>
14     <dependencies>
15         <dependency>
16             <groupId>org.springframework.boot</groupId>
17             <artifactId>spring-boot-starter-data-jpa</artifactId>
18         </dependency>
19         <dependency>
20             <groupId>org.springframework.boot</groupId>
21             <artifactId>spring-boot-starter-web</artifactId>
22         </dependency>
23         <dependency>
24             <groupId>org.projectlombok</groupId>
25             <artifactId>lombok</artifactId>
26             <scope>provided</scope>
27         </dependency>
28         <dependency>
29             <groupId>org.mariadb.jdbc</groupId>
30             <artifactId>mariadb-java-client</artifactId>
31             <version>2.3.0</version>
32         </dependency>
33     </dependencies>
34     <build>
35         <plugins>
36             <plugin>
37                 <groupId>org.springframework.boot</groupId>
38                 <artifactId>spring-boot-maven-plugin</artifactId>
39             </plugin>
40         </plugins>
41     </build>
42 </project> 

註意:父級是自定義的功能測試最佳實踐項目,該項目繼承了spring-boot-starter-parent。稍後將介紹其目的。

Structure

 

這幾乎是我們滿足初始需求所需的一切:保存和檢索用戶詳細信息以及檢索包含聯繫人的用戶詳細信息。

 

功能測試

是時候添加功能測試了!對於TDD,在實現之前需要閱讀本節。

地點

開始之前,我們需要選擇功能測試的位置;還有兩個更合適的地方:

  • 與單元測試一起在單獨的文件夾中:

 

 

這是開始添加功能測試的最簡單,最快的方法,儘管它有一個很大的缺點:如果要單獨運行單元測試,則需要排除功能測試文件夾。為什麼每次應用較小的代碼修改後都不運行所有測試?因為在大多數情況下,功能測試與單元測試相比具有巨大的執行時間,因此應單獨進行修改以節省開發時間。

  • 在一個單獨的項目以及一個公共父項下的服務項目中:

 

  1. Parent POM (aggregative project)
  2. Service project
  3. Functional tests project

這種方法比以前的方法有一個優勢——我們有一個與服務單元測試隔離的功能測試模塊,因此我們可以通過分別運行單元測試或功能測試輕鬆地驗證邏輯。另一方面,這種方法需要一個多模塊的項目結構,與單模塊的項目相比,難度更大。

你可能已經從服務pom.xml中猜到了,對於我們的情況,我們將選擇第二種方法。

 

POM文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <groupId>com.tdanylchuk</groupId>
 6     <artifactId>functional-tests-best-practices</artifactId>
 7     <version>0.0.1-SNAPSHOT</version>
 8     <packaging>pom</packaging>
 9     <name>Functional tests best practices parent project</name>
10     <parent>                                                   <!--1-->
11         <groupId>org.springframework.boot</groupId>
12         <artifactId>spring-boot-starter-parent</artifactId>
13         <version>2.0.4.RELEASE</version>
14         <relativePath/>
15     </parent>
16     <modules>                                                  <!--2-->
17         <module>user-details-service</module>
18         <module>user-details-service-functional-tests</module>
19     </modules>
20     <properties>
21         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
22         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
23         <java.version>1.8</java.version>
24     </properties>
25 </project>
  1. spring-boot-starter-parent是我們的父POM的父項目。這樣,我們就為Spring提供了依賴管理。
  2. 模塊聲明。註意:順序很重要,功能測試應始終排在最後。

案例

為了挑選功能測試涵蓋的案例,我們需要考慮兩個主要方面:

  • 功能需求——基本上,每個請求的需求都應具有自己的功能測試。
  • 較長的執行時間——專註於應用程式的關鍵部分,與單元測試相反,在單元測試中,應涵蓋每個較小的案例。否則,構建時間將是巨大的。

Architecture

是的,測試還需要架構,尤其是功能測試,在這些測試中執行時間很重要,邏輯可能會隨著時間變得過於複雜。而且,它們應該是可維護的。這意味著,如果發生功能轉移,功能測試對於開發人員而言將不會是頭痛的事情。

Steps

步驟(也稱為固定裝置)是一種封裝每個通信通道邏輯的方法。 每個通道應具有自己的步驟對象,該對象與其他步驟隔離。

就我們而言,我們有兩個溝通渠道:

  • 用戶詳細信息服務REST API(在渠道中)
  • 聯繫人服務REST API(渠道外)

對於通道中的REST,我們將使用名為REST Assured的庫。與我們使用MockMvc進行REST API驗證的集成測試相比,這裡我們使用更多的黑盒風格測試,以免將Spring上下文與測試模擬對象弄混。

至於REST out通道,將使用WireMock。我們不會指出Spring用模擬的模板替代REST模板。取而代之的是,WireMock在後臺使用的碼頭伺服器將與我們的服務一起被引導,以模擬真正的外部REST服務。

 

用戶詳細信息步驟

 1 @Component
 2 public class UserDetailsServiceSteps implements ApplicationListener<WebServerInitializedEvent> {
 3     private int servicePort;
 4     public String getUser(long userId) {
 5         return given().port(servicePort)
 6                 .when().get("user/" + userId)
 7                 .then().statusCode(200).contentType(ContentType.JSON).extract().asString();
 8     }
 9     public void saveUserDetails(long userId, String body) {
10         given().port(servicePort).body(body).contentType(ContentType.JSON)
11                 .when().post("user/" + userId + "/details")
12                 .then().statusCode(200);
13     }
14     public String getUserDetails(long userId) {
15         return given().port(servicePort)
16                 .when().get("user/" + userId + "/details")
17                 .then().statusCode(200).contentType(ContentType.JSON).extract().asString();
18     }
19     @Override
20     public void onApplicationEvent(@NotNull WebServerInitializedEvent webServerInitializedEvent) {
21         this.servicePort = webServerInitializedEvent.getWebServer().getPort();
22     }
23 }

 

從步驟對象中可以看到,每個API端點都有自己的方法。

預設情況下,REST安全的將調用localhost,但是需要指定埠,因為我們的服務將使用隨機埠引導。為了區分它,應該偵聽WebServerInitializedEvent

註意:這裡不能使用@LocalServerPor註釋,因為在Spring Boot嵌入式容器啟動之前已創建步驟bean。

 

聯絡人服務步驟

1 @Component
2 public class ContactsServiceSteps {
3     public void expectGetUserContacts(long userId, String body) {
4         stubFor(get(urlPathMatching("/contacts")).withQueryParam("userId", equalTo(String.valueOf(userId)))
5                 .willReturn(okJson(body)));
6     }
7 }

在這裡,我們需要以與從應用程式調用遠程服務時完全相同的方式對伺服器進行模擬:端點,參數等。

 

資料庫

我們的服務是將數據存儲在Maria DB中,但是就功能測試而言,存儲數據的位置無關緊要,因此,如黑盒測試所要求的,在測試中不應提及任何內容。

在將來,如果我們考慮將Maria DB更改為某些NoSQL解決方案,則測試應保持不變。

但是,解決方案是什麼?

當然,我們可以像在集成測試中對H2資料庫那樣使用嵌入式解決方案,但是在生產中,我們的服務將使用Maria DB,這可能會導致出現問題。

例如,我們有一個名為MAXVALUE的列,並針對H2運行測試,一切正常。但是,在生產中,該服務失敗,因為這是MariaDB中的保留字,這意味著我們的測試不如預期的好,並且可能會浪費大量時間來解決問題,而該服務將保持不發佈狀態。

避免這種情況的唯一方法是在測試中使用真正的Maria DB。同時,我們需要確保我們的測試可以在本地執行,而無需在其中設置Maria DB的任何其他登臺環境中。

為瞭解決這個問題,我們將選擇testcontainers項目,該項目提供了常見資料庫,Selenium Web瀏覽器或可在Docker容器中運行的任何其他東西的輕量級,一次性的實例。

但是testcontainers庫不支持開箱即用的Spring Boot。因此,我們將使用另一個名為testcontainers-spring-boot的庫,而不是為MariaDB編寫自定義的通用容器並將其手動註入Spring Boot。它支持可能在你的服務中使用的最常見技術,例如:MariaDB,Couchbase,Kafka,Aerospike,MemSQL,Redis,neo4j,Zookeeper,PostgreSQL,ElasticSearch。

要將真正的Maria DB註入我們的測試中,我們只需要向我們的user-details-service-functional-tests項目pom.xml文件中添加適當的依賴項,如下所示:

1 <dependency>
2     <groupId>com.playtika.testcontainers</groupId>
3     <artifactId>embedded-mariadb</artifactId>
4     <version>1.9</version>
5     <scope>test</scope>
6 </dependency>

 

如果你的服務不使用Spring Cloud,則應在上述依賴項的基礎上添加下一個依賴項:

1 <dependency>
2     <groupId>org.springframework.cloud</groupId>
3     <artifactId>spring-cloud-context</artifactId>
4     <version>2.0.1.RELEASE</version>
5     <scope>test</scope>
6 </dependency>

 

在Spring Boot上下文啟動之前,它需要對dockerized資源進行引導。

這種方法顯然有很多優點。由於我們擁有“真實”資源,因此如果無法測試所需資源的真實連接,則無需在代碼中編寫變通辦法。 不幸的是,該解決方案帶來了一個巨大的缺點——測試只能在安裝了Docker的環境中運行。 這意味著你的工作站和CI工具應在板載Docker上。另外,你應該準備好測試將需要更多的時間來執行。

Parent Tests Class

由於執行時間很重要,因此我們需要避免為每個測試載入多個上下文,因此Docker容器將僅對所有測試啟動一次。Spring預設情況下啟用了上下文緩存功能,但是我們需要謹慎,因為通過僅添加簡單的註釋@MockBean,我們迫使Spring使用模擬的bean創建新的上下文,而不是重用現有的上下文。這個問題的解決方案是創建一個單一的父抽象類,該類將包含所有必需的Spring批註,以確保將單個上下文重用於所有測試套件:

 1 @RunWith(SpringRunner.class)
 2 @SpringBootTest(
 3         classes = UserDetailsServiceApplication.class,               //1
 4         webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)  //2
 5 @ActiveProfiles("test")                                              //3
 6 public abstract class BaseFunctionalTest {
 7     @Rule
 8     public WireMockRule contactsServiceMock = new WireMockRule(options().port(8777)); //4
 9     @Autowired                                                       //5
10     protected UserDetailsServiceSteps userDetailsServiceSteps;
11     @Autowired
12     protected ContactsServiceSteps contactsServiceSteps;
13     @TestConfiguration                                               //6
14     @ComponentScan("com.tdanylchuk.user.details.steps")
15     public static class StepsConfiguration {
16     }
17 }

 

  1. 指向Spring Boot測試註釋以載入我們服務的主要配置類。
  2. 在生產環境中使用Bootstraps Web環境(預設情況下使用模擬的環境)
  3. 需要測試配置文件來載入application-test.properties,其中將覆蓋生產屬性,例如URL,用戶,密碼等。
  4. WireMockRule啟動碼頭伺服器以在提供的埠上存根。 
  5. 步驟的受保護的自動接線,因此在每次測試中都可以訪問它們。
  6.  @TestConfiguration 通過掃描程式包將步驟載入到上下文中。

在這裡,我們試圖不修改上下文,將在生產環境中通過向其中添加一些util項來進一步使用該上下文,例如步驟和屬性覆蓋。

使用@MockBean註釋是不好的做法,該註釋將應用程式的一部分替換為模擬,並且該部分將保持未經測試的狀態。

不可避免的情況-即在邏輯中檢索當前時間,例如System.currentTimeMillis(),應重構此類代碼,因此將改用Clock對象:clock.millis()。並且,在功能測試中,應該模擬Clock對象,以便可以驗證結果。

 

測試性質

application-test.properties:

 1 #contact service                                          #1
 2 contacts.service.url=http://localhost:8777
 3 #database                                                 #2
 4 user.details.db.host=${embedded.mariadb.host}
 5 user.details.db.port=${embedded.mariadb.port}
 6 user.details.db.schema=${embedded.mariadb.schema}
 7 spring.datasource.username=${embedded.mariadb.user}
 8 spring.datasource.password=${embedded.mariadb.password}
 9                                                           #3
10 spring.jpa.hibernate.ddl-auto=create-drop
  1. 使用WireMock碼頭伺服器端點而不是生產聯繫服務URL。
  2. 資料庫屬性的覆蓋。註意:這些屬性由spring-boo-test-containers庫提供。
  3. 在測試中,資料庫架構將由Hibernate創建。

 

自我測試

為了進行此測試,已經做了很多準備工作,所以讓我們看一下它的外觀:

 1 public class RestUserDetailsTest extends BaseFunctionalTest {
 2     private static final long USER_ID = 32343L;
 3     private final String userContactsResponse = readFile("json/user-contacts.json");
 4     private final String userDetails = readFile("json/user-details.json");
 5     private final String expectedUserResponse = readFile("json/user.json");
 6     @Test
 7     public void shouldSaveUserDetailsAndRetrieveUser() throws Exception {
 8         //when
 9         userDetailsServiceSteps.saveUserDetails(USER_ID, userDetails);
10         //and
11         contactsServiceSteps.expectGetUserContacts(USER_ID, userContactsResponse);
12         //then
13         String actualUserResponse = userDetailsServiceSteps.getUser(USER_ID);
14         //expect
15         JSONAssert.assertEquals(expectedUserResponse, actualUserResponse, false);
16     }
17 }

 

對於stubbing和asserting,使用先前通過JSON文件創建的。這樣,請求和響應格式都得到了驗證。最好不要在此處使用測試數據,而應使用生產請求/響應的副本。

由於整個邏輯都封裝在步驟,配置和JSON文件中,因此如果更改與功能無關,則此測試將保持不變。例如:

  • 響應更改的格式-僅應修改測試JSON文件。
  • 聯繫人服務端點更改-應該修改ContactsServiceSteps對象。
  • Maria DB被No SQL DB取代-pom.xml和測試屬性文件應被修改。

 

功能測試項目

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <artifactId>user-details-service-functional-tests</artifactId>
 6     <version>0.0.1-SNAPSHOT</version>
 7     <name>User details service functional tests</name>
 8     <parent>
 9         <groupId>com.tdanylchuk</groupId>
10         <artifactId>functional-tests-best-practices</artifactId>
11         <version>0.0.1-SNAPSHOT</version>
12     </parent>
13     <dependencies>
14         <dependency>                                      <!--1-->
15             <groupId>com.tdanylchuk</groupId>
16             <artifactId>user-details-service</artifactId>
17             <version>${project.version}</version>
18             <scope>test</scope>
19         </dependency>
20         <dependency>
21             <groupId>org.springframework.boot</groupId>
22             <artifactId>spring-boot-starter-test</artifactId>
23             <scope>test</scope>
24         </dependency>
25         <dependency>
26             <groupId>org.springframework.cloud</groupId>
27             <artifactId>spring-cloud-context</artifactId>
28             <version>2.0.1.RELEASE</version>
29             <scope>test</scope>
30         </dependency>
31         <dependency>
32             <groupId>com.playtika.testcontainers</groupId>
33             <artifactId>embedded-mariadb</artifactId>
34             <version>1.9</version>
35             <scope>test</scope>
36         </dependency>
37         <dependency>
38             <groupId>com.github.tomakehurst</groupId>
39             <artifactId>wiremock</artifactId>
40             <version>2.18.0</version>
41             <scope>test</scope>
42         </dependency>
43         <dependency>
44             <groupId>io.rest-assured</groupId>
45             <artifactId>rest-assured</artifactId>
46             <scope>test</scope>
47         </dependency>
48     </dependencies>
49 </project>
  1. 添加了用戶詳細信息服務作為依賴項,因此可以由SpringBootTest載入。

 

結構體

 

 

 

放在一起,我們就有了下一個結構。

向服務添加功能不會更改當前結構,只會擴展它。如果增加了更多的通信渠道,則可以通過其他步驟執行以下操作:使用常用方法添加utils文件夾;帶有測試數據的新文件;當然,還要針對每個功能要求進行額外的測試。

結論

在本文中,我們根據給定的需求構建了一個新的微服務,並通過功能測試涵蓋了這些需求。在測試中,我們使用了黑盒類型的測試,在這種測試中,我們嘗試不更改應用程式的內部部分,而是作為普通客戶端從外部與它進行通信,以儘可能地模擬生產行為。同時,我們奠定了功能測試體繫結構的基礎,因此將來的服務更改將不需要重構現有測試,並且添加新測試將儘可能地容易。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 先看知識點總結 一、序列操作符x in s 如果x是列表s的元素,返回True,否則Falses + t 連接兩個序列s和ts*n或者n*s 將序列s複製n次s[i] 返回s中第i元素s[i:j]或s[i:j: k] 切片,返回序列s中第i到j-1以k為步長的元素子序列12345函數len(s) 返 ...
  • 素描作為一種近乎完美的表現手法有其獨特的魅力,隨著數字技術的發展,素描早已不再是專業繪畫師的專利,今天這篇文章就來講一講如何使用python批量獲取小姐姐素描畫像。文章共分兩部分: 第一部分介紹兩種使用python生成素描畫的思路 第二部分介紹如何批量獲取素描畫 一、獲取素描圖的兩個思路 本部分介紹 ...
  • Struts2整合AJAX有2種方式: 使用type="stream"類型的<result> 使用JSON插件 使用type="stream"類型的<result> 獲取text 前端 <body> <form> 學號:<input type="text" id="no"><br /> 姓名:<in ...
  • 將pip的下載源換成國內的,速度會有很大的提升。 下麵是一些國內常用鏡像: 阿裡雲 http://mirrors.aliyun.com/pypi/simple/ 中國科技大學 https://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣(douban) http://pyp ...
  • ArrayList和LinkedList的區別 步驟 1 : ArrayList和LinkedList的區別 ArrayList , 插入,刪除數據慢 LinkedList, 插入,刪除數據快 ArrayList是順序結構,所以 定位很快 ,指哪找哪。 就像電影院位置一樣,有了電影票,一下就找到位置 ...
  • Cookie Cookie 是一種伺服器發送給瀏覽器以鍵值對形式存儲小量信息的技術。 當瀏覽器首次請求伺服器時,伺服器會將一條信息封裝成一個Cookie發送給瀏覽器,瀏覽器收到Cookie,會將它保存在記憶體中(註意這裡的記憶體是本機記憶體,而不是伺服器記憶體)或者本地文件,那之後每次向伺服器發送請求,瀏覽 ...
  • Java ArrayList和HashSet的區別 示例 1 : 是否有順序 ArrayList: 有順序 HashSet: 無順序 HashSet的具體順序,既不是按照插入順序,也不是按照hashcode的順序。 以下是 HasetSet源代碼 中的部分註釋 / It makes no guara ...
  • 運行環境:centos 7,jdk 1.8 問題一: 原因:無法創建本地文件問題,用戶最大可創建文件數太小 解決方案:切換到root用戶,編輯limits.conf配置文件, 添加類似如下內容: vim /etc/security/limits.conf 添加如下內容:* soft nofile 6 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...