2016-03-23 27 views
1

我想写很好我已经创建的REST API的单元测试。我有这样的简单结构:GO单元测试结构化REST API项目

ROOT/ 
    config/ 
    handlers/ 
    lib/ 
    models/ 
    router/ 
    main.go 

config包含JSON配置和一个简单的config.go读取和解析JSON文件,填补了Config结构。 handlers包含控制器(即,在router/routes.go中描述的相应METHOD + URL的处理程序)。 lib包含一些数据库,请求响应器和记录器逻辑。 models包含要从JSON和DB映射的结构和它们的funcs。最后router包含路由器和路由定义。

现在我在GO中搜索和阅读了很多关于单元测试REST API的文章,并且发现了或多或少令人满意的关于如何设置测试服务器,定义路由和测试请求的文章。一切都好。 但只有当你想测试一个文件!

我现在的问题是如何为所有处理程序设置测试环境(服务器,路线,数据库连接)?发现here(我发现很容易理解和实现)的方法我有一个问题:或者我必须为每个处理程序分别运行测试我必须在一个测试文件中为所有处理程序编写测试套件 。我相信你明白,这两种情况都不是很高兴(第一,因为我需要保持运行go test运行所有测试成功和第二,因为有一个测试文件覆盖所有处理函数将变得无法维护)。

现在我成功了(根据the linked article),只是我把所有的测试和初始化代码放入一个func文件中,但我不喜欢这种方法。

我想达成什么是那种setUp()init()与第一触发测试运行一次完成所有必需的变量全局可见,初始化,所有接下来的测试可能已经再次同时确保使用它们,而不需要实例化他们的这个设置文件仅编译测试...

我不确定这是否完全清楚,或者如果这种问题需要一些代码示例(除了什么是already linked in the article,但我会添加任何你认为是必需的,只是告诉我!

+0

为什么不可能在init函数中为您的测试执行所有设置? – Machiel

+0

@Machiel如果我有一个init函数来设置一切,我需要为一个测试文件定义全局变量。这意味着我无法在另一个测试文件中定义相同的变量。我想确保我能够单独运行每个测试并同时运行它们...... – shadyyx

+0

“测试文件”是什么意思?我知道你有a_test.go b_test.go,但这不应该是个问题吧?如果你运行一个测试,它将所有东西都设置为一次测试,否则它会将所有设置设置为多次测试? – Machiel

回答

1

测试软件包,而不是文件!

由于您正在测试处理程序/端点,因此将所有_test文件放入处理程序或路由器程序包中将是有意义的。 (例如,每个端点/处理程序一个文件)。

此外,请勿使用init()来设置您的测试。所述testing包指定一个函数具有以下特征:

func TestMain(m *testing.M) 

生成的测试将调用TestMain(米),而不是直接运行测试 。 TestMain在主要的goroutine中运行,并且可以执行任何 设置和拆卸在m.Run的调用周围。它应该然后 呼叫os.Exit与m.Run

里面的TestMain功能,你可以做任何设置,您才能运行测试需要的结果。如果你有全局变量,这是申报和初始化它们的地方。每个软件包只需要执行一次,因此将TestMain代码放在单独的_test文件中是有意义的。例如:

package router 

import (
    "testing" 
    "net/http/httptest" 
) 

var (
    testServer *httptest.Server 
) 

func TestMain(m *testing.M) { 
    // setup the test server 
    router := ConfigureRouter() 
    testServer = httptest.NewServer(router) 

    // run tests 
    os.Exit(m.Run()) 
} 

最后用go test my/package/router运行测试。

+0

好吧,所以初始化这个'TestMain'和全局变量我只是依靠他们在我的处理程序测试(ofc。我有一个测试每个处理程序/端点),它会工作吗?这个'os.Exit()'会在所有软件包的所有测试完成后调用?我需要能够使用'go test。/ ...'运行测试,因为这将从构建和部署脚本调用。 – shadyyx

+0

我想用银杏做到这一点,但不幸的是,我遇到了一些问题,以及... ... http://stackoverflow.com/questions/36202934/running-ginkgo-test-suite-beforesuite-setup-before-any -spec-is-ran – shadyyx

+0

@shadyyx是的,你可以依靠他们在处理程序。但请记住,全局变量是软件包的作用域。如果你用'go test。/ ...'测试多个软件包,你将需要每个软件包包含一个'TestMain'。是的,当所有测试完成时,os.Exit()将被调用。 – fl0cke

1

也许你可以把你想要的设置代码从多个单元测试文件使用单独测试包,只有单元测试使用?

或者你可以把设置代码放到普通包中,并在单元测试中使用它。

之前已经有人问过,Go作者选择不隐式提供一个测试标签,该标签可以用来在普通包文件中选择性地启用函数编译。

+0

我试图将* init *的东西拉到额外的包中,但这带来了一个缺点 - 为了使包/文件可访问,它必须在包中没有'_test'名称,这使得它在内部构建生产代码(即最终可执行文件)。这不是真的想要的......当我将init代码拉入一个'_test'包中时,它的'init()'func中设置的变量不能被测试文件访问。 'SetUpTests()'func我也试过... ... – shadyyx

+0

对不起。至少当我构建包时,我可以控制它们是否以我的生产代码结束。如果我的生产代码不导入包,则不包括该包。通过包,我的意思是一个去包,所以它的文件是在他们自己的目录中。它们不能与生产代码文件位于同一个目录中。 – WeakPointer