翻译:时序
上篇文章: 挑战:微服务集成测试(一)
我们会测试2个小服务的集成。
服务provider是我们在jenkins plugin例子里使用过的相同的服务。它叫’bringon‘,是用Go写的一个是用mongoDB的保存软件构建信息的注册表。
我们的consumer是一个很薄的python客户端,它只知道从bringon是用构建编号来获取构建信息的。
在CDC里consumer先开始 - 我们从这来。
consumer代码现在由一个带有2个函数的client.py文件组成。我们只关注叫’build‘的函数 - 它是我们要测试的函数。
import requests … def getbuild(host, port, buildnum): """Fetch a build by number .""" uri = 'http://' + host + ':' + port + '/builds/' + str(buildnum) return requests.get(uri).json()
为了为它生成pact - 我们写一个叫build_test.py的测试文件:
import atexit import unittest import client from pact import Consumer, Provider pact = Consumer('buildReader').has_pact_with(Provider('bringon')) pact.start_service() atexit.register(pact.stop_service) class GetBuildInfoContract(unittest.TestCase): def test_get_build(self): true = True expected = { u'name':u'#3455', u'completed': true, #boolean u'info':{ u'coverage':30, u'apiversion':0.1, u'swaggerlink':u'http://swagger', u'buildtime':230} } (pact .given('build 3455 exists') .upon_receiving('a request for build 3455') .with_request('get', '/builds/3455') .will_respond_with(200, body=expected)) with pact: result = client.build(3455) self.assertEqual(result, expected)
这很直接 - 我们建了一个mock的service,定义了一个期望的http reponse和body, 并调用client.build()来保证交互是按期望进行的。
如果一切正常 - 一个叫buildreader-bringon.json的pact文件会写入到我们的工作目录。
现在我们可以将这个文件发给bringon的开发,让他们可以用这个pact来测试他们的服务。
这可以用pact-go来完成 - Golang的框架。测试看起来会是这样:
func TestPact(t *testing.T) { go startInstrumentedBringon() pact := createPact() // Verify the Provider with local Pact Files log.Println("Start verify ", []string{filepath.ToSlash(fmt.Sprintf("%s/buildreader-bringon.json", pactDir))}, fmt.Sprintf("http://localhost:%d/setup", port), fmt.Sprintf("http://localhost:%d", port), fmt.Sprintf("%s/buildReader-bringon.json", pactDir)) err := pact.VerifyProvider(types.VerifyRequest{ ProviderBaseURL: fmt.Sprintf("http://localhost:%d", port), PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/buildReader-bringon.json", pactDir))}, ProviderStatesSetupURL: fmt.Sprintf("http://localhost:%d/setup", port), }) if err != nil { t.Fatal("Error:", err) } }
记住这需要一点额外的工作。我们需要需要实现startInstrumentedBringon()方法,它使用额外的'/setup' endpoint来定义服务状态并启动我们的服务。在我们的场景这用于创建一个入口点来满足我们消费者的期望。我们也需要创建一个Pact客户端对象来校验所有交互动作。像这样:
func createPact() dsl.Pact { // Create Pact connecting to local Daemon log.Println("Creating pact") return dsl.Pact{ Consumer: "buildreader", Provider: "bringon", LogDir: logDir, PactDir: pactDir, } }
一个使用pact-go的缺点是需要你在后端运行一个常驻进程。这个进程控制服务的初始化,关闭和pact校验。
这在容器内运行一个独立临时进程不太合适。
所以如果我们需要的是用pact测试我们的服务 - 我们可以使用pact-go包中的轻量级pact-provider-verifier工具。
像这样:
pact-provider-verifier --pact-urls <path_to>/buildreader-bringon.json --provider-base-url http://localhost:8091 --provider-states-setup-url http://localhost:8091/setup
请记住在这个例子里我们需要实现和构建'/setup' endpoint作为我们服务的一部分。这在我们想让我们的服务可测试时是个好主意。
服务的代码可以在我们的Github找到:
bringon(也就是Provider): https://github.com/codefreshdemo/bringon buildreader(也就是消费者) https://github.com/antweiss/cdc-pact-demo Pact源码和例子:https://github.com/pact-foundation
在这个博客的下篇我们会展示如何让运行合约测试作为你的Codefresh(译者注:codefresh.io是一个CI/CD提供商) 流水线的一部分。
本文来自祝坤荣(时序)的微信公众号「麦芽面包,id「darkjune_think」转载请注明。交流Email: zhukunrong@yeah.net