如何在大量 Node.JS BDD 测试中避免代码冗余
How to avoid code redundancy in large amounts of Node.JS BDD tests
在过去的几个月里,我一直在为一个我们从头开始的大型项目的后端 (REST API) 工作。我们遵循 BDD(行为驱动开发)标准,所以现在我们有大量测试(~1000)。测试是使用 chai 编写的 - Node.JS 的 BDD 框架,但我认为这个问题可以扩展到编写测试时的一般良好实践。
起初,我们尽量避免代码冗余,而且进展顺利。随着代码行数和从事该项目的人员的增加,它变得越来越混乱,但可读性却越来越差。有时可以在 15 分钟内应用的代码中的微小更改导致需要更改,例如30 多个文件等中的模拟数据和方法,这意味着 6 个小时的更改和 运行 测试(极端示例)。
TL:DR
我们现在想重构这些 BDD 测试。例如我们有这样一个函数:
function RegisterUserAndGetJWTToken(user_data, next: any){
chai.request(server).post(REGISTER_URL).send(user_data).end((err: any, res: any) => {
token = res.body.token;
next(token);
})
}
我们的大部分测试文件都使用了这个函数。创建包含此类函数的 test-suite
之类的东西是否有意义,或者在编写测试时是否有更好的方法来避免冗余?然后我们可以使用像这样的导入:
import {RegisterUserAndGetJWTToken} from "./test-suite";
import {user_data} from "./test-mock-data";
- 您有什么好的做法可以分享吗?
- 是否有任何有用的 npm 包(或用于
其他编程语言)?
- 你认为这种方法也有缺点吗(比如混乱
会有多个进口)?
- 也许有办法
inject
或 inherit
test-suite
每个文件,以避免导入并默认在每个文件中都有它?
编辑:忘记提及 - 我的意思是集成测试。
提前致谢!
重构当前测试套件
您的原则应该是提高测试本身的抽象级别。这意味着测试应该包含用领域语言表达的高级方法调用。例如:
registerUser('John', 'john@smith.com')
lastEmail = getLastEmailSent()
lastEmail.receipient.should.be 'john@smith.com'
lastEmail.contents.should.contain 'Dear John'
现在在这些方法的实施过程中,可能会发生很多事情。特别是, registerUser
函数可以执行 post 请求(如您的示例)。 getLastEmailSent
可以从消息队列或伪造的 SMTP 服务器中读取。问题是你隐藏了 API.
背后的细节
如果您遵循这一原则,您最终会创建一个自动化层 - 一个面向领域的、程序化的 API 到您的系统。创建这一层时,您遵循所有良好的设计原则,例如 DRY。
好处是,当代码发生变化时,测试代码中只有一个地方需要更改——在自动化层中,而不是在测试本身中。
我看到您提出的建议(提取 RegisterUserAndGetJWTToken
和测试数据)是朝着创建自动化层迈出的良好一步。我不会担心 require
电话。我看不出有什么理由不明确说明我们的测试依赖什么。也许在稍后阶段,其中一些可以收集到更大的模块中(registration
、emailing
等)。
可维护测试套件的良好实践
在适当的级别实现自动化。
有时通过 UI 或 REST 会更好,但通常直接调用函数会更明智。例如,如果您编写一个计算发票税金的测试,那么为每个测试用例遍历整个应用程序就太过分了。最好留下一个端到端测试,看看所有部分是否一起行动,并在尽可能低的级别上自动化所有特定案例。这样我们就可以获得良好的覆盖率,以及测试套件的速度和稳健性。
编写测试的指导原则是可读性。
你可以参考this discussion的解释。
像对待生产代码一样谨慎对待测试助手代码/自动化层。
这意味着您应该非常小心地重构它,遵循所有良好的设计原则。
在过去的几个月里,我一直在为一个我们从头开始的大型项目的后端 (REST API) 工作。我们遵循 BDD(行为驱动开发)标准,所以现在我们有大量测试(~1000)。测试是使用 chai 编写的 - Node.JS 的 BDD 框架,但我认为这个问题可以扩展到编写测试时的一般良好实践。
起初,我们尽量避免代码冗余,而且进展顺利。随着代码行数和从事该项目的人员的增加,它变得越来越混乱,但可读性却越来越差。有时可以在 15 分钟内应用的代码中的微小更改导致需要更改,例如30 多个文件等中的模拟数据和方法,这意味着 6 个小时的更改和 运行 测试(极端示例)。
TL:DR
我们现在想重构这些 BDD 测试。例如我们有这样一个函数:
function RegisterUserAndGetJWTToken(user_data, next: any){
chai.request(server).post(REGISTER_URL).send(user_data).end((err: any, res: any) => {
token = res.body.token;
next(token);
})
}
我们的大部分测试文件都使用了这个函数。创建包含此类函数的 test-suite
之类的东西是否有意义,或者在编写测试时是否有更好的方法来避免冗余?然后我们可以使用像这样的导入:
import {RegisterUserAndGetJWTToken} from "./test-suite";
import {user_data} from "./test-mock-data";
- 您有什么好的做法可以分享吗?
- 是否有任何有用的 npm 包(或用于 其他编程语言)?
- 你认为这种方法也有缺点吗(比如混乱 会有多个进口)?
- 也许有办法
inject
或inherit
test-suite
每个文件,以避免导入并默认在每个文件中都有它?
编辑:忘记提及 - 我的意思是集成测试。
提前致谢!
重构当前测试套件
您的原则应该是提高测试本身的抽象级别。这意味着测试应该包含用领域语言表达的高级方法调用。例如:
registerUser('John', 'john@smith.com')
lastEmail = getLastEmailSent()
lastEmail.receipient.should.be 'john@smith.com'
lastEmail.contents.should.contain 'Dear John'
现在在这些方法的实施过程中,可能会发生很多事情。特别是, registerUser
函数可以执行 post 请求(如您的示例)。 getLastEmailSent
可以从消息队列或伪造的 SMTP 服务器中读取。问题是你隐藏了 API.
如果您遵循这一原则,您最终会创建一个自动化层 - 一个面向领域的、程序化的 API 到您的系统。创建这一层时,您遵循所有良好的设计原则,例如 DRY。
好处是,当代码发生变化时,测试代码中只有一个地方需要更改——在自动化层中,而不是在测试本身中。
我看到您提出的建议(提取 RegisterUserAndGetJWTToken
和测试数据)是朝着创建自动化层迈出的良好一步。我不会担心 require
电话。我看不出有什么理由不明确说明我们的测试依赖什么。也许在稍后阶段,其中一些可以收集到更大的模块中(registration
、emailing
等)。
可维护测试套件的良好实践
在适当的级别实现自动化。
有时通过 UI 或 REST 会更好,但通常直接调用函数会更明智。例如,如果您编写一个计算发票税金的测试,那么为每个测试用例遍历整个应用程序就太过分了。最好留下一个端到端测试,看看所有部分是否一起行动,并在尽可能低的级别上自动化所有特定案例。这样我们就可以获得良好的覆盖率,以及测试套件的速度和稳健性。
编写测试的指导原则是可读性。
你可以参考this discussion的解释。
像对待生产代码一样谨慎对待测试助手代码/自动化层。
这意味着您应该非常小心地重构它,遵循所有良好的设计原则。