用wiremock自建mock服务
我们很多时候都需要 mock 服务,比如
- 在做性能测试的时候,我们希望调用第三方服务的接口可以被 mock 掉,这样就不会因为压测而对第三方依赖造成巨大的负载
- 第三方服务归属于其他团队,不同环境之间的沟通和协调其实比较麻烦
- 消除不同测试环境之间的差异
- 控制请求的时延
- 如果是使用云服务的话,使用 mock 接口还可以节约出口的带宽
- 提升第三方服务的稳定性
所以 mock 的作用就是用最小的代价实现跟第三方接口几乎一样的假的接口,最好返回的内容是动态可配置的,这样我们在测试环境就可以直接去调用这个假接口,从而消除了对第三方的依赖。
实现 mock 的方式很多,最简单的做法就是随机应变,自行实现,比如在 python 技术栈里,我们可以用 flask 去这样实现
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/get-json')
def hello():
return jsonify(hello='world') # Returns HTTP Response with {"hello": "world"}
用 go 实现起来也不难,下面的例子来自 go 的官方文档,使用了 gin 框架。https://go.dev/doc/tutorial/web-service-gin
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// album represents data about a record album.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// albums slice to seed record album data.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
// postAlbums adds an album from JSON received in the request body.
func postAlbums(c *gin.Context) {
var newAlbum album
// Call BindJSON to bind the received JSON to
// newAlbum.
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// Add the new album to the slice.
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
// getAlbumByID locates the album whose ID value matches the id
// parameter sent by the client, then returns that album as a response.
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// Loop through the list of albums, looking for
// an album whose ID value matches the parameter.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
从上面的代码可以看出来,如果不需要实现创建接口的话,代码量还能少三分之一左右,总的来看其实难度也不算大。