1. 程式人生 > >測試必須學spring RESTful Service(上)

測試必須學spring RESTful Service(上)

文末我會說說為什麼測試必須學spring。 # REST REST,是指REpresentational State Transfer,有個精闢的解釋什麼是RESTful, - 看url就知道要什麼 - 看method就知道幹什麼 - 看status code就知道結果如何 實際上,RESTful API已經成為構建微服務的標準了,因為RESTful API容易build和consume。 為什麼選擇REST?REST信奉web的規則,包括架構、效益和其他一切。這並非偶然,因為spring的作者Roy Fielding參與了十幾項web規則的定義,這些規則決定了web怎麼執行。 效益是什麼?web和它的核心協議,HTTP,提供了一系列特性, - 適當的actions (`GET`, `POST`, `PUT`, `DELETE`, …) - 快取 - 重定向和轉發 - 安全(加密和鑑權) 這些都是建立可伸縮性services的關鍵因素。但也不是全部。 web是由大量細小的規則構成的,所以不會存在什麼“標準之爭”,因此就能輕鬆的發展。 開發們(Javaer)可以使用第三方工具來實現這些不同的規則,在指尖就可以立即擁有client和server的技術。 所以建立在HTTP之上的REST APIs提供了建立靈活APIs的方法, - 支援向後相容 - 可擴充套件的API - 可伸縮性的services - 安全性的services - 無狀態到有狀態的services 但是,REST並不是一個普遍的標準,而是一個方法,一個style,一系列架構上的約束,來幫你建立web-scale的系統,區別這一點很重要。 # 下載示例程式碼 到[Spring Initializr](https://start.spring.io/)這個網址選擇, - Web - JPA - H2 然後生成專案。下載`.zip`檔案。解壓。就有了一個基於Maven的示例專案,包括一個`pom.xml`檔案。 Spirng Boot可以用任何IDE,包括Eclipse、IntelliJ IDEA、Netbeans等。 Eclipse可以使用一個工具STS(The Spring Tool Suite)。 # 先從非REST說起 我們先以最簡單的示例開始。先拋棄REST的概念,後面再新增REST,在示例中感受到不同之處。 示例建模了一個簡單的工資單service,管理公司employees。簡言之,需要儲存employee objects到一個H2記憶體資料庫(Java編寫的嵌入式資料庫引擎),然後通過JPA(Java Persistence API,把實體物件持久化到資料庫,是一種ORM規範,Hibernate是具體實現的框架)訪問。它會被封裝到Spring MVC layer進行遠端訪問。 nonrest/src/main/java/payroll/Employee.java ```java package payroll; import java.util.Objects; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity class Employee { private @Id @GeneratedValue Long id; private String name; private String role; Employee() {} Employee(String name, String role) { this.name = name; this.role = role; } public Long getId() { return this.id; } public String getName() { return this.name; } public String getRole() { return this.role; } public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } public void setRole(String role) { this.role = role; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Employee)) return false; Employee employee = (Employee) o; return Objects.equals(this.id, employee.id) && Objects.equals(this.name, employee.name) && Objects.equals(this.role, employee.role); } @Override public int hashCode() { return Objects.hash(this.id, this.name, this.role); } @Override public String toString() { return "Employee{" + "id=" + this.id + ", name='" + this.name + '\'' + ", role='" + this.role + '\'' + '}'; } } ``` 程式碼不多,這個Java class包含了, - `@Entity` 是JPA註解,標記這個object是儲存在基於JPA的資料庫的。 - `id`, `name`, 和 `role` 是domain object的屬性,第一個被多個JPA註解標記的,是主鍵,通過JPA provider實現了自增。 - 當建立新例項的時候,就會建立custom constructor,但是還沒有id。 domain object定義好以後,就可以用[Spring Data JPA](https://spring.io/guides/gs/accessing-data-jpa/)來處理冗長的資料庫互動。Spring Data repositories是一些介面,可以對後端資料庫進行reading, updating, deleting, 和 creating記錄。一些repositories也支援適當的data paging和sorting。Spring Data基於介面中的methods的命名約定來合成實現。 Spring Data JPA是Spring Data家族成員之一,只需要寫repository介面,包括custom finder methods,Spring會自動提供實現。除了JPA之外,還有多種repository實現,如Spring Data MongoDB, Spring Data GemFire, Spring Data Cassandra等。 nonrest/src/main/java/payroll/EmployeeRepository.java ```java package payroll; import org.springframework.data.jpa.repository.JpaRepository; interface EmployeeRepository extends JpaRepository { } ``` 介面繼承了Spring Data JPA的`JpaRepository`,定義domain type為`Employee`,id type為`Long`。這個介面表面上是空的,但是支援, - Creating new instances - Updating existing ones - Deleting - Finding (one, all, by simple or complex properties) Spring Data的[repository solution](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories)可以避開資料儲存細節,使用domain-specific術語來解決大部分問題。 不管你信不信,反正我信了!現在已經足夠來啟動一個應用了!Spring Boot應用至少有一個`public static void main` entry-point,和`@SpringBootApplication`註解。 nonrest/src/main/java/payroll/PayrollApplication.java ```java package payroll; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class PayrollApplication { public static void main(String... args) { SpringApplication.run(PayrollApplication.class, args); } } ``` `@SpringBootApplication`是元註解,引入了**component scanning**, **autoconfiguration**, 和**property support**。Spring Boot會啟動一個servlet container,併為我們的service服務。 然而,沒有資料的應用有點搞笑,先添點資料。下面這個類會由Spring自動載入, nonrest/src/main/java/payroll/LoadDatabase.java ```java package payroll; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration class LoadDatabase { private static final Logger log = LoggerFactory.getLogger(LoadDatabase.class); @Bean CommandLineRunner initDatabase(EmployeeRepository repository) { return args -> { log.info("Preloading " + repository.save(new Employee("Bilbo Baggins", "burglar"))); log.info("Preloading " + repository.save(new Employee("Frodo Baggins", "thief"))); }; } } ``` Spring載入這個類的時候會發生什麼? - 一旦應用上下文載入後,Spring Boot就會執行所有的`CommandLineRunner` beans - runner會請求剛才建立的`EmployeeRepository`的copy - 然後建立2個物件,並存儲 右鍵**Run** `PayRollApplication`, ```shell ... 2018-08-09 11:36:26.169 INFO 74611 --- [main] payroll.LoadDatabase : Preloading Employee(id=1, name=Bilbo Baggins, role=burglar) 2018-08-09 11:36:26.174 INFO 74611 --- [main] payroll.LoadDatabase : Preloading Employee(id=2, name=Frodo Baggins, role=thief) ... ``` # HTTP 為了用web layer封裝repository,必須轉Spring MVC。Spring Boot簡化了這部分工作,基礎程式碼只有一點點,從而把編碼重心放到actions, nonrest/src/main/java/payroll/EmployeeController.java ```java package payroll; import java.util.List; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController class EmployeeController { private final EmployeeRepository repository; EmployeeController(EmployeeRepository repository) { this.repository = repository; } // Aggregate root @GetMapping("/employees") List all() { return repository.findAll(); } @PostMapping("/employees") Employee newEmployee(@RequestBody Employee newEmployee) { return repository.save(newEmployee); } // Single item @GetMapping("/employees/{id}") Employee one(@PathVariable Long id) { return repository.findById(id) .orElseThrow(() -> new EmployeeNotFoundException(id)); } @PutMapping("/employees/{id}") Employee replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) { return repository.findById(id) .map(employee -> { employee.setName(newEmployee.getName()); employee.setRole(newEmployee.getRole()); return repository.save(employee); }) .orElseGet(() -> { newEmployee.setId(id); return repository.save(newEmployee); }); } @DeleteMapping("/employees/{id}") void deleteEmployee(@PathVariable Long id) { repository.deleteById(id); } } ``` - `@RestController` 表明了每個方法返回的資料會直接寫入到響應的body裡面,而不是render一個template。 - constructor注入了一個`EmployeeRepository` 到controller(依賴注入)。 - 每個operations的路由 (`@GetMapping`, `@PostMapping`, `@PutMapping` 和 `@DeleteMapping`, 對應HTTP `GET`, `POST`, `PUT`, 和 `DELETE` )。 - `EmployeeNotFoundException` 是當employee找不到時丟擲的異常。 nonrest/src/main/java/payroll/EmployeeNotFoundException.java ```java package payroll; class EmployeeNotFoundException extends RuntimeException { EmployeeNotFoundException(Long id) { super("Could not find employee " + id); } } ``` 當丟擲`EmployeeNotFoundException`,Spring MVC configuration會render一個`HTTP 404`, nonrest/src/main/java/payroll/EmployeeNotFoundAdvice.java ```java package payroll; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @ControllerAdvice class EmployeeNotFoundAdvice { @ResponseBody @ExceptionHandler(EmployeeNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) String employeeNotFoundHandler(EmployeeNotFoundException ex) { return ex.getMessage(); } } ``` - `@ResponseBody` 表示advice會直接render到response body。 - `@ExceptionHandler` 配置了只有拋`EmployeeNotFoundException` 異常的時候,advice才會響應。 - `@ResponseStatus` 表示發出 `HttpStatus.NOT_FOUND`, 比如 **HTTP 404**。 - advice的body生成具體內容。示例中,返回了異常的message。 執行應用有多種方式,可以右鍵`PayRollApplication`中的`public static void main`,然後選擇IDE的**Run**。 如果是Spring Initializr,可以輸入命令列, ```shell $ ./mvnw clean spring-boot:run ``` 如果是本地安裝了maven,可以輸入命令列, ```shell $ mvn clean spring-boot:run ``` 一旦應用啟動了,可以檢視http通訊, ```shell $ curl -v localhost:8080/employees ``` ```shell * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 8080 (#0) > GET /employees HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.54.0 > Accept: */* > < HTTP/1.1 200 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Thu, 09 Aug 2018 17:58:00 GMT < * Connection #0 to host localhost left intact [{"id":1,"name":"Bilbo Baggins","role":"burglar"},{"id":2,"name":"Frodo Baggins","role":"thief"}] ``` 能看到預載入的資料。 如果請求一個不存在的employee, ```shell $ curl -v localhost:8080/employees/99 ``` ```java * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 8080 (#0) >
GET /employees/99 HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.54.0 > Accept: */* > < HTTP/1.1 404 < Content-Type: text/plain;charset=UTF-8 < Content-Length: 26 < Date: Thu, 09 Aug 2018 18:00:56 GMT < * Connection #0 to host localhost left intact Could not find employee 99 ``` **HTTP 404** error,並列印了message,**Could not find employee 99**。 使用`-X`發個POST請求,建立新的`Employee`, ```shell $ curl -X POST localhost:8080/employees -H 'Content-type:application/json' -d '{"name": "Samwise Gamgee", "role": "gardener"}' ``` 使用`PUT`更新, ```shell $ curl -X PUT localhost:8080/employees/3 -H 'Content-type:application/json' -d '{"name": "Samwise Gamgee", "role": "ring bearer"}' ``` 使用`Delete`刪除, ```shell $ curl -X DELETE localhost:8080/employees/3 $ curl localhost:8080/employees/3 Could not find employee 3 ``` # 怎麼變得RESTful 現在已經實現了基於web的service,但是是非REST的, - 像`/employees/3`這種漂亮的URLs,不一定是REST - 只用了 `GET`, `POST` 等,不一定是REST - 實現了所有CRUD操作,不一定是REST 那到底怎麼樣才算REST? 實際上現在建立的這個應該叫做**RPC** (**Remote Procedure Call** 遠端過程呼叫)。因為並不知道以何種方式來和這個service互動。如果釋出這個程式碼,還必須寫個文件或者搞個開發入口網站,來把所有細節描述清楚。 看看Roy Fielding的這段話,是如何區別**REST** 和**RPC**的, ``` I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today’s example is the SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an X rating. What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period. Is there some broken manual somewhere that needs to be fixed? — Roy Fielding https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven ``` 大概意思就是,應用狀態引擎(API)不是超文字驅動的(我的理解是,像超文字一樣攜帶一個地址,可以定址定位資訊,如超文字的link和id屬性),就不是RESTful。 不包括hypermedia的壞處,就是clients必須硬編碼URIs來導航API。這導致了電子商務興起之前同樣的脆弱特性。JSON output需要優化。 [Spring HATEOAS](https://spring.io/projects/spring-hateoas),是一個spring專案,旨在幫你寫hypermedia-driven outputs。 接下來RESTful開搞,先新增Spring HATEOAS到pom.xml, ```xml org.springframework.boot spring-boot-starter-hateoas ``` 這個小library會提供定義RESTful service的constructs,然後以可接受的格式render,以便client消費。 對任何RESTful service來說,一個關鍵的要素,是給相關的操作新增[links](https://tools.ietf.org/html/rfc8288)。 Getting a single item resource ```java @GetMapping("/employees/{id}") EntityModel one(@PathVariable Long id) { Employee employee = repository.findById(id) // .orElseThrow(() -> new EmployeeNotFoundException(id)); return EntityModel.of(employee, // linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel(), linkTo(methodOn(EmployeeController.class).all()).withRel("employees")); } ``` 跟之前非REST有些類似,但也有不同, - 方法的返回值從 `Employee` 變成了 `EntityModel`。`EntityModel` 是Spring HATEOAS的通用container,不僅包含data,也包含links集合。 - `linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel()` 讓Spring HATEOAS建立link到 `EmployeeController` 's `one()` 方法,並標記為[self](https://www.iana.org/assignments/link-relations/link-relations.xhtml) link。 - `linkTo(methodOn(EmployeeController.class).all()).withRel("employees")` 讓Spring HATEOAS建立link到aggregate root(聚合根), `all()`,叫做"employees"。 建立link是什麼意思?Spring HATEOAS的核心types之一就是`Link`,包括一個 **URI** 和 一個 **rel** (relation)。正是Links改變了web。 在World Wide Web之前,其他的document systems會render information or links,但正是帶有這種關係metadata的documents link把web連在了一起。 Roy Fielding鼓勵使用相同的技術來建立APIs,links便是其中之一。 如果重啟應用,查詢employee *Bilbo*,會有一些不同, RESTful representation of a single employee ```json { "id": 1, "name": "Bilbo Baggins", "role": "burglar", "_links": { "self": { "href": "http://localhost:8080/employees/1" }, "employees": { "href": "http://localhost:8080/employees" } } } ``` 不只有`id`, `name` and `role`,還有 `_links`,包括2個URLs。整個文件是採用[HAL](http://stateless.co/hal_specification.html)格式化的。 HAL是一個輕量的[mediatype](https://tools.ietf.org/html/draft-kelly-json-hal-08),不僅允許encoding data,也能hypermedia controls,提醒consumers到能導航到的API的其他部分。在本示例中,就是"self"(類似於程式碼裡的`this`) link和能返回到**aggregate root**的link。 為了讓aggregate root也更RESTful,那麼會希望包含top level links,和包含其他RESTful components, Getting an aggregate root resource ```java @GetMapping("/employees") CollectionModel> all() { List> employees = repository.findAll().stream() .map(employee -> EntityModel.of(employee, linkTo(methodOn(EmployeeController.class).one(employee.getId())).withSelfRel(), linkTo(methodOn(EmployeeController.class).all()).withRel("employees"))) .collect(Collectors.toList()); return CollectionModel.of(employees, linkTo(methodOn(EmployeeController.class).all()).withSelfRel()); } ``` 我擦!之前只有一個方法`repository.findAll()`!現在多了這麼多程式碼!看不懂!不慌!排著隊一個一個來! `CollectionModel<>`是Spring HATEOAS的另外一個container,用於封裝集合,以及links。 封裝集合?employees集合? 不完全是。 既然已經在說REST了,那麼封裝的是**employee resources**的集合。 這就是為什麼獲取了所有employees後,還需要轉換為`EntityModel`的list。 重啟之後,獲取aggregate root, ```json { "_embedded": { "employeeList": [ { "id": 1, "name": "Bilbo Baggins", "role": "burglar", "_links": { "self": { "href": "http://localhost:8080/employees/1" }, "employees": { "href": "http://localhost:8080/employees" } } }, { "id": 2, "name": "Frodo Baggins", "role": "thief", "_links": { "self": { "href": "http://localhost:8080/employees/2" }, "employees": { "href": "http://localhost:8080/employees" } } } ] }, "_links": { "self": { "href": "http://localhost:8080/employees" } } } ``` 這個aggregate root,提供了employee resources的集合,有一個top-level **"self"** link。 **"collection"**列在**"_embedded"**下面。這就是HAL怎麼表示集合。 集合中每個獨立的成員,都有information和關聯的links。 新增links到底有什麼意義?它使得隨著時間的推移發展REST services成為可能。已存在的links能保留,新的links在未來被新增。新的clients可能用新的links,同時遺留clients仍然能用老的links。如果services需要重定位和移動,那這就會非常有用。只要link結構保留,clients就仍然能查詢和互動。 *後續內容請等待《測試必須學spring RESTful Service(下)》* 參考資料 https://spring.io/guides/tutorials/rest/ # 測試為什麼必須學spring 高階測試,需要懂架構,需要懂開發,需要能和開發在同一個Level交流。除了公司專案以外,業務時間是很少有合適的方式去學習一些開發技術的。尤其是對於我這種對程式碼不太敏感,對技術反應有些遲鈍的。光靠自己零零散散的學習,是很難真正提升的。那麼有一個比較好的方式,就是去看一些成熟的成體系的東西。對於Web來說,沒有任何一個框架比得上Java Spring成熟。在spring裡面可以瞭解到很多開發的技術點,這對了解整個技術棧是很有效的方式。雖然我平時寫Python比較多(畢竟生產力很強大),但仍然喜歡學Java,這樣才能接觸到更完整的生態。讓自己的測試眼界更寬廣。 版權申明:本文為博主原創文章,轉載請保留原文連結及作者。

相關推薦

測試必須spring RESTful Service

文末我會說說為什麼測試必須學spring。 # REST REST,是指REpresentational State Transfer,有個精闢的解釋什麼是RESTful, - 看url就知道要什麼 - 看method就知道幹什麼 - 看status code就知道結果如何 實際上,RESTful A

【一步一步學習springspring bean管理

proto id屬性 table handle isp 基礎上 turn 聲明 設置方法 1. spring 工廠類 我們前邊的demo中用到的spring 工廠類是ClassPathXmlApplicationContext,從上圖可以看到他還有一個兄弟類FileSys

Spring入門篇——第3章 Spring Bean裝配

第3章 Spring Bean裝配(上) 介紹Bean的作用域、生命週期、Aware介面、自動裝配和Resource等內容。 3-1 Spring Bean裝配之Bean的配置項及作用域       3-2 Spring B

一起下nuxt.js

Nuxt.js通用 vue.jsSSRSSR:伺服器端 VUE渲染成HTML返回瀏覽器SEO:VUE SPA(單頁)新聞搜尋引擎比較SPA載入較快因為新聞部落格都需要搜尋引擎抓取內容,大量的流量來自於伺服器端的渲染如果使用vue製作該類網站的話那麼有可能只能抓取到首頁,那麼我

MyBatis5:MyBatis 整合 Spring 事務管理

單獨使用MyBatis對事務進行管理 前面MyBatis的文章有寫過相關內容,這裡繼續寫一個最簡單的Demo,算是複習一下之前MyBatis的內容吧,先是建表,建立一個簡單的Student表: create table student (     stude

強大的Spring快取技術

快取是實際工作中非常常用的一種提高效能的方法, 我們會在許多場景下來使用快取。 本文通過一個簡單的例子進行展開,通過對比我們原來的自定義快取和 spring 的基於註釋的 cache 配置方法,展現了 spring cache 的強大之處,然後介紹了其基本的原理,擴充套

【10分鐘Spring】:初識Spring框架

簡介 Spring是一個輕量級的企業級的Java開發框架。主要是用來替代原來更加重量級的企業級Java技術,比如EJB(Enterprise JavaBean)、Java資料物件(Java Data Object)等。Spring的出現極大簡化了Java開發。 另外Spring框架是一個一體化的框架,它不僅能

小白 Python 爬蟲27:自動化測試框架 Selenium 從入門到放棄

人生苦短,我用 Python 前文傳送門: 小白學 Python 爬蟲(1):開篇 小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝 小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門 小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門 小白學 Pyth

Spring】使用Spring和AMQP發送接收消息

com load 設定 支持 消息發送 結果 alt 來看 接下來 講AMQP之前,先講下傳統的JMS的消息模型,JMS中主要有三個參與者:消息的生產者、消費者、傳遞消息的通道(隊列或者主題),兩種消息模型如下:通道是隊列: 通道是隊列: 通道是主題: 在JMS中,雖然

6-感覺身體被掏空,但還是要Pandas

搜索結果 分配 淘寶 時間 amr 一次 記得 每天 包含 關註群主朋友圈的人都知道,本周群主過得RIO充實,每天工作到半夜寫爬蟲,先後寫了Linkedin,生E經、生意參謀以及淘寶關鍵詞搜索結果等爬蟲。記得去年寫工商信息爬蟲時是灰頭土臉的,雖然每爬一次隨機sleep幾秒,

跟我設計模式視頻教程——管擦者模式,責任鏈模式

tar eight color font content 設計模式 name -m ack 課程視頻 觀察者模式(下) 責任鏈模式(上) 課程筆記 課程筆記 課程代碼 課程代碼 新課程火熱報名中 課程介紹

java之SpringAOP前奏-動態代理設計模式

對象 .cn 分享圖片 賦值 alt his 編程習慣 輸出 style 我們常常會遇到這樣的事,項目經理讓你為一個功能類再加一個功能A,然後你加班為這個類加上了功能A; 過了兩天又來了新需求,再在A功能後面加上一個新功能B,你加班寫好了這個功能B,加在了A後面;又過

spring 發布 Jax-Ws Service

name 描述 services quest wss poi org 設置 註意 Service import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult;

Spring框架學習8spring mvc傳下載

class tor XML smart details targe resp imp common 內容源自:spring mvc上傳下載 如下示例: 頁面: web.xml: <?xml version="1.0" encoding="UTF-8"?>

如何對第一個Vue.js元件進行單元測試

  首先,為什麼要單元測試元件?   單元測試是持續整合的關鍵。通過專注於小的、獨立的實體,確保單元測試始終按預期執行,使程式碼更加可靠,你可以放心地迭代你的專案而不必擔壞事兒。   單元測試不僅限於指令碼。可以獨立測試的任何東西都是可單元測試的,只要你遵循一些好的做法。這些例項包括單一責任、可預測性和鬆

spring之我見--Controller註冊到DispatchServlet請求處理

對應上一章 《spring之我見–從spring的啟動到ioc容器的建立》 今天我們探討一下Springmvc的工作原理,Springmvc的核心是Controller請求部分,所以我們的探討從Controller被註冊開始,到Controller如何被請求的。 1.Contro

spring原始碼學習5.1.0版本——Bean的初始化

目錄   前言 源頭 preInstantiateSingletons方法 getBean(String beanName) doGetBean getObjectForBeanInstance getObjectFromFactoryBean doGe

Spring MVC 註解筆記

一 對於spring mvc來說2.0以後大量使用註解確實簡單很多,最近在一個專案使用spring mvc遇到上傳檔案問題,由於使用了註解所以網上沒有找到相關使用註解上傳檔案的。官方文件又沒有更新都是老的,看了一些原始碼這才解決。 使用註解很簡單。 寫個例子:控制器類 FileUplo

使用EOLINKER做介面測試最佳路徑

本文內容: 測試指令碼管理:講述如何在 EOLINKER 上設計測試專案目錄結構。 編寫測試指令碼:講述如何在 EOLINKER 上編寫介面測試指令碼。 測試指令碼執行及報告:講述如何在 EOLINKER 上執行介面測試指令碼,及如何檢視和下載測試報告。 測試專案人員協作:講述

Spring Boot學習:使用@SpringBootTest註解進行單元測試

一、簡介 專案中經常會遇到需要單元測試的情況,那麼SpringBoot如何實現這種需求,使用@SpringBootTest註解可以執行環境,測試後臺程式碼。 二、環境準備 eclipse + maven + Spring Boot 三、程式碼示例 pom.xml