用Docker与Golang进行MongoDB测试
发布日期:2016-4-17 11:4:53
用Docker与Golang进行MongoDB测试 1.背景 我们不断寻找新技术来解决开发中遇到的问题。我们一直在使用Java+Spring,然而Java 8与Spring Boot却给为我们带来了新的生机,并且改变了单一的Java应用为微服务模式。当你有API的时,你仅仅需一个合适的前端框架就可以替代jsp与jQuery:在我们的案例中我们选择AngularJS。在两年前我们第一次使用Angular,现在我们所有的项目都引入了AngularJS。 2.超过10年的Java 在我们的的灵魂深处留下了深刻的印记 这两三年,我一直在寻找更好的东西。在这个行业中,最好的事情就是你会有各种选择。曾经我们使用NodeJS构建了几个项目,也学习了Ruby的服务配置管理框架Chef。当然我们也有一些Scala项目,并了解过Clojure、Haskell与Rust语言,后来我们发现了Go。虽然我们只使用Go语言编写了几个小服务,但却为与Go相关的语言、标准库、工具与社区而震惊。网上有很多的文章解释了为什么不同的公司选择了Go,本文不再赘述。同时如果你想学习如何编写Go,或者其他数据库知识,如mysql,可以观看我们的视频。 3.负载测试 我可能需要相当长的篇幅来介绍负载测试,所有的编程语言都需要编写单元测试代码,此外还有一些需要使用TDD方法与达到100%测试覆盖率的目标的方法。动态语言需要安排更多类型的测试,当你可能经过了上百次的测试,你的应用才能达到一个稳定的状态。痛苦的是,由于有不同的开发语言,因此你的测试需要很多的准备工作:曾经几秒钟就可以完成的事情,到现在可能会需要几分钟,甚至是几十分钟才能完成。所以,你要开始仓库(数据库)的调用,并建立集成测试的数据库开发的预载与清除方法。有时候集成测试可能会失败,而原因可能是由于超时或者仅仅因为两个开发版本使用相同的数据库在同时运行。 4.使用Golang与Docker进行的测试 Golang则不会有类似的问题,有了Golang的快速构建、测试周期与一些Docker魔法的支持,你能在几秒内启动MongoDB Docker容器并运行所有的测试。从开始到结束只需要几秒的时间,但第一次运行除外,由于第一次运行的时候需要下载与提供MongoDB Docker容器。 5.让我们做一些可以进行Docker实验的好的事情 我已经研究Golang+AngularJS一段时间了,目前是最佳时间来证明Docker是否真的那么好。对于OS X用户来说,涉及Docker时会有个小烦恼:它只在Linux上面运行。是的,你可以运用Boot2Docker来安装到OS X上,但是Boot2Docker将在虚拟化的Linux上运行Docker。我已经通过Ubuntu来使用Vagrant作为开发环境,因此我刚刚在这上面安装了Docker。 首先,我要熟悉Camlistore的实施环境并且复制它。特别感谢Brad Fitzpartick,你通过Camlistore与Golang标准程序库来完成了出色的工作。 可以通过story_test.go来找到实际测试。对于那些看不懂Golang的用户,我已经在最重要的代码部分添加了额外的注释。以下部分有代码: Setup test environment func TestStoryCreateAndGet(t *testing.T) { // Start MongoDB Docker container // // One of the most powerful features in Golang // is the ability to return multiple values from functions. // In this we get: // – containerID (type=ContainerID struct) // – ip (type=string) containerID, ip := dockertest.SetupMongoContainer(t) // defer schedules KillRemove(t) function call to run immediatelly // when TestStoryCreateAndGet(t) function is done, // so you can place resource clenup code close to resource allocation defer containerID.KillRemove(t) app := AppContext{} // Connect to Dockerized MongoDB mongoSession, err := mgo.Dial(ip) // Golang favors visible first hand error handling. // Main idea is that Errors are not exceptional so you should handle them if err != nil { Error.Printf(“MongoDB connection failed, with address ‘%s’.”, Configuration.MongoUrl) } // close MongoDB connections when we’re finished defer mongoSession.Close() app.mongoSession = mongoSession // create test http server with applications route configuration ts := httptest.NewServer(app.createRoutes()) defer ts.Close() storyId := testCreate(ts, t) // run create test testGet(ts, storyId, t) // run get test for created story } Post json document to http handler func testCreate(ts *httptest.Server, t *testing.T) string { postData := strings.NewReader(“{\”text\”:\”tekstiä\”,\”subjectId\”:\”k2j34\”,\”subjectUrl\”:\”www.fi/k2j34\”}”) // create http POST with postData JSON res, err := http.Post(ts.URL+”/story”, applicationJSON, postData) // read http response body data data, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { t.Error(err) } id := string(data) // verify that we got correct http status code if res.StatusCode != http.StatusCreated { t.Fatalf(“Non-expected status code: %v\n\tbody: %v, data:%s\n”, http.StatusCreated, res.StatusCode, id) } // verify that we got valid lenght response data if res.ContentLength != 5 { t.Fatalf(“Non-expected content length: %v != %v\n”, res.ContentLength, 5) } return id } Test that previously created story exists func testGet(ts *httptest.Server, storyId string, t *testing.T) { // create http GET request with correct path res, err := http.Get(ts.URL + “/story/” + storyId) data, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { t.Error(err) } body := string(data) // validate status code if res.StatusCode != http.StatusOK { t.Fatalf(“Non-expected status code: %v\n\tbody: %v, data:%s\n”, http.StatusCreated, res.StatusCode, body) } // validate that response has correct storyId if !strings.Contains(body, “{\”storyId\”:\””+storyId+”\”,”) { t.Fatalf(“Non-expected body content: %v”, body) } // validate that content leght is what is should be if res.ContentLength < 163 && res.ContentLength > 165 { t.Fatalf(“Non-expected content length: %v < %v, content:\n%v\n", res.ContentLength, 160, body) } } 所以,启动MongoDB Docker容器,将它配置到应用程序,然后用内置的测试直至创建HTTP服务器。然后,我们设置同样的路由给服务器,并且对测试服务器运行两个请求,第一个请求来创建故事评论,另外一个请求来获取它。所有的数据都被存储了,并且从MongoDB中获取。那么所有这一切需要多久时间呢? 仅仅两秒以下! 即使你运行一些条件选择器它仍只需要不到3秒 \o/ Docker是针对所有的用户,而不仅仅是Golang用户 对于那些可以使用Golang的用户,Docker也可以帮助你。它当然没有Golang那么快速,但是与使用外部的MongoDB服务器一样的快,而且没有额外的清理麻烦。毫无疑问,Docker是虚拟化业务中的游戏变化者,并且这些炒作也得到了很好的回报。这样就没有借口来针对MongoDB功能编写任何模拟测试。 上一条: MySQL如何对半年数十TB数据增量 下一条: mysql数据库优化方法
|