Mocha如何知道在测试套件中首先加载哪个文件

12

我正在尝试学习使用MongoDB进行测试驱动开发。文件夹结构如下:

enter image description here

在src文件夹中有一个用于测试的user.js文件。

const mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
const Schema = mongoose.Schema;

const UserSchema = new Schema ({
    name: String
});

const User = mongoose.model('user', UserSchema);

module.exports = User;

test_helper.js 的内容

const mongoose = require('mongoose');;

mongoose.connect('mongodb://localhost/users_test');

    mongoose.connection
    .once('open', () => {
        console.log('Connected to Mongo!');
        done()}) 
    .on('error', (error) => { 
        console.warn('Warning', error);
    });

create_test.js 的内容

const assert = require('assert');
const User = require('../src/user');

describe('Creating records', () => {

    it('Saves a user', (done) => {
        const user = new User({ name: 'Ankur' });
        user.save()
                .then(() => {
                    assert(!user.isNew);
                    done();
                });

现在我运行 npm test,测试已经通过了。

Connected to Mongo!
  Creating records
    √ Saves a user (779ms)

但是我的疑问是,Mocha 如何知道每次都要先运行 test_helper.js 文件。(并且将该文件命名为其他任何名称都不会改变其行为)。

同时我没有使用任何根级别的 hook。

我知道 Mocha 递归地加载每个目录中的文件,从根目录开始,由于这里所有内容都在一个目录中,所以这里没有任何区别。

请问有人能否建议或帮忙,Mocha 究竟如何知道应首先运行 test_helper.js(或具有相同内容的任何文件名)。

3个回答

12

Mocha没有默认的测试文件加载顺序。

当 Mocha 扫描目录 查找文件时,它会使用 fs.readdirSync。此调用是对 readdir(3) 的封装,其本身不保证顺序。现在,由于一种 实现奇怪之处fs.readdirfs.readdirSync 在 Linux 上(以及可能是 POSIX 系统)上排序,但在 Windows 上则不排序。此外,Linux 上的排序行为可能最终会被删除,因为文档说 fs.readdir 只是 readdir(3),而后者不保证顺序。有理由认为,在 Linux 上观察到的行为是错误的(请参见我上面链接的问题)。

请注意,Mocha有一个--sort选项,在找到文件后会对其进行排序。但默认情况下此选项关闭。
您观察到的行为不仅可以通过加载顺序来解释,还可以通过执行顺序来解释。以下是发生的情况:
1. Mocha加载测试文件并执行它们。因此,文件顶部的任何内容都会立即执行。这意味着test_helper.js中的代码会立即执行。每次调用describe都会立即执行其回调函数。然而,对it的调用会记录测试以供稍后执行。Mocha在执行此操作时正在“发现”您的测试,但不会立即“执行”它们。
2. 所有文件都被执行后,Mocha开始运行测试。此时,test_helper.js中的代码已经运行,并且您的测试受益于它所创建的连接。

重要警告 连接数据库是一个异步操作,目前没有任何保证test_helper.js中的异步操作将在测试开始之前完成。现在它能正常工作只是运气好而已。

如果我是你,我会把连接创建放在全局异步before钩子中。(出现在任何测试文件中的全局before钩子将在任何测试之前执行,甚至是出现在其他文件中的测试。)或者我会使用--delay并显式调用run()来确保连接被建立后启动测试套件。


谢谢您提供如此详细的答案。我已经将它放在了 before hook 中。只是没有 before hook,测试条件也显示出这种行为,这让我感到惊讶。 - Ankur Anand
啊,是的,如果它在全局的before钩子中,你就避免了我所描述的问题。这也符合我所解释的行为。before钩子必须在任何测试之前执行。它不依赖于加载顺序。 - Louis
fs.readdir和fs.readdirSync在Linux上是有序的。如果我能投两次票,我一定会为教我这个知识点的人投两次。因为在你回答之前,Peter Müller(https://github.com/Munter)Mocha贡献者已经回答了这个问题,而我还在阅读文档,试图弄清楚它如何按字母顺序加载文件,而你给出了相同的答案。 - Ankur Anand
Peter回复 --- "Mocha按字母顺序加载和评估文件夹中的所有文件。包含测试的文件将为mocha排队以便稍后运行测试。test_helper.js的内容不包含测试并立即执行。这就是为什么在测试开始运行之前您会获得DB连接,但这是偶然的,您不能依赖于时机和db连接准备好进行测试。如果您想要确保在任何测试开始运行之前都有一个db连接,则需要将连接建立代码放在before hook中,并在其准备就绪时进行回调。" - Ankur Anand
是的,我进行了几次测试,看到了排序顺序,然后就感到有些困惑,因为我知道这完全取决于readdir(3),而它并不保证顺序。然后我发现了问题报告,解释了这种意外行为。 - Louis

1

它不会

测试不应该有特定的顺序。 所有测试套件都应该作为独立的,与其他套件无关的工作。在套件内部,您可以使用“before”和“beforeEach”(或“after”,“afterEach”)来创建设置和拆卸步骤。

但是,如果测试的顺序很重要,则设计存在问题。


1
在 mocha 的配置文件中,您可以指定在所有测试开始之前加载哪些 JavaScript 文件,这就是您放置辅助文件的地方。 - eavichay
@Creynders.. create_test.js需要mongoose连接进行验证..如果它首先被执行,那么在没有与mongodb连接的情况下如何进行验证? - Ankur Anand
@Creynders 在4个不同的系统上以及在cloud9 ide上出现相同的行为不可能是巧合。 :) .. - Ankur Anand
@AnkurAnand 是的,可能是按字母顺序加载的,或者基于创建日期等等...然后是的,在所有系统上都会有相同的行为。我的观点是这不是你可以依赖的东西。将其与使用for..in迭代对象进行比较,99%的时间它看起来总是按照相同的顺序(即您声明键的顺序),但突然间,它就不是了。这是同样的事情。但是,如果你很难相信我,没问题。只要记住,当它突然不再工作时。 - Creynders
请看这里:https://github.com/mochajs/mocha/blob/master/lib/utils.js#L657 - Creynders
显示剩余6条评论

0

有一种非常简单的方法可以按顺序加载测试。

步骤1:在package.json中设置一个测试脚本: 例如:

"scripts": {
    "test": "mocha ./tests.js"
  }

假设tests.js是一个定义执行测试顺序的文件。
require("./test/general/test_login.js");
require("./test/Company/addCompany.js");
...
...

所以这里 test_login 将首先运行,然后一个接一个地运行其他测试。

步骤2:然后使用以下命令运行测试:

$ npm test

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接