JavaScript / Jest:如何仅在测试失败时才显示测试案例的日志?

时间:2019-11-19 14:47:59

标签: javascript logging jestjs winston

我正在使用硒开发一个端到端的测试套件,其中的测试用例是使用笑话测试运行程序以JavaScript编写的。

我的问题是,当某事不起作用时,硒经常会毫不客气地失败,而对失败的原因几乎没有解释。不用说,调试这样的测试可能非常困难。

我正在寻找一种记录每个测试用例的方式,以便我知道测试失败的地方,但是仅在测试实际失败时才在测试输出中显示这些日志(以免污染测试的控制台输出有很多不必要的日志)。

所以我想做类似的事情:

var query = app.models.company.newQuery(); // Should this not be models.company.newQuery as there is no datasource named "carcompanies" ?
query.prefetch.autos._add();
var results = query.run();
var calculatedRecords = []; //contained a capitalization error here

results.forEach(function(company) {
  var totals = {wholesale:0, retail:0, profit:0};

  var records = company.autos;

  records.forEach(function(item) {
    totals.wholesale += item.wholesale;
    totals.retail += item.retail;
    totals.profit += (item.retail - item.wholesale);
  });

  var record = app.models.totals.newRecord();
  record.Company = company.name;
  record.wholesale = totals.wholesale;
  record.retail = totals.retail;
  record.profit = totals.profit;
  calculatedRecords.push(record);
});
return calculatedRecords;

因此,如果#change order of columns for change ordering of final value column df = df[df.columns[::-1]] #reshape by stack df = (df.set_index('Node') .stack() .rename_axis(('Node','num1')) .astype(int) .reset_index(name='num2')) #get last non NaN value to num1 column df['num1'] = df.groupby('Node')['num2'].transform('first') #remove last row per groups df = df[df.duplicated(subset=['Node'])].copy() print (df) Node num1 num2 1 YYZ 3 2 2 YYZ 3 1 4 DFW 7 6 5 DFW 7 5 6 DFW 7 4 8 DEN 21 20 10 BOS 106 105 11 BOS 106 104 12 BOS 106 103 13 BOS 106 102 14 BOS 106 101 15 BOS 106 100 中的测试失败并且describe(() => { it('case 1', async () => { // this log should only be printed on the console if this test fails logger.info('message from case 1'); // ... }); it('case 2', () => { logger.info('message from case 2'); // ... }); }); 没有失败,那么我会在控制台输出中看到case 1(最好是在该测试用例的错误之前),并且< em> not case 2

开玩笑有可能吗?我可以随意使用任何日志记录库。

4 个答案:

答案 0 :(得分:6)

我遇到了同样的问题,无法找到确定的解决方案。看来it's low on Facebook's to do list是个解决方法。 它使用的是我发现的herehere代码段。 这个想法是,在每次玩笑之前,您都要设置一个消息存储区并全局覆盖控制台,以将所有日志记录转移到该消息存储区。 每次测试之后,您都要检查该测试是否失败,如果失败,请打印出隐藏的消息。

package.json

start-btn

setup.js

"jest": {
    ...
    "verbose": true,
    "setupFilesAfterEnv": ["<rootDir>/test/setup.js"],
    ...
  }

如果需要执行其他任何清理操作,则可以像这样在测试文件中的“每次”之后“扩展”:

some.test.js

const util = require('util')

global.consoleMessages = []

// get information about the current test
jasmine.getEnv().addReporter({
    specStarted: result => jasmine.currentTest = result,
    specDone: result => jasmine.currentTest = result,
})

function squirrelAway(text, logger) {
    // use error to get stack trace
    try {
        throw new Error('stacktrace')
    } catch (err) {
        let trace = err.stack.split('\n')
        trace.shift()   // removes Error: stacktrace
        trace.shift()   // removes squirrelAway() call from the "throw" command
        trace.shift()   // removes console logger call in the console override
        consoleMessages.push({logger: logger, payload: text, stacktrace: trace.join('\n')})
    }
}

const orig = console
global.console = {...console,
    // use jest.fn() to silence, comment out to leave as it is
    log: (text => squirrelAway(text, orig.log)),
    error: (text => squirrelAway(text, orig.error)),
    warn: (text => squirrelAway(text, orig.warn)),
    info: (text => squirrelAway(text, orig.info)),
    debug: (text => squirrelAway(text, orig.debug))
}

global.afterEach(() => {
    // this includes tests that got aborted, ran into errors etc.
    let failed = (jasmine && jasmine.currentTest
                  && Array.isArray(jasmine.currentTest.failedExpectations)) ?
                 jasmine.currentTest.failedExpectations.length>0 : true
    //orig.log(`test "${jasmine.currentTest.fullName}" finished. failed? ${failed}`)
    if (failed) {
        //orig.log(`Logging for "${jasmine.currentTest.fullName}" start`)
        consoleMessages.forEach(msg => {
            if (typeof msg.payload === 'object' || typeof msg.payload === 'function') {
                msg.payload = util.inspect(msg.payload, false, null, true)
            }
            msg.logger.call(msg.logger, msg.payload + '\n' + msg.stacktrace)
        })
        //orig.log(`Logging for "${jasmine.currentTest.fullName}" end`)
    }
    consoleMessages = []
})

缺点

  • 因为我们重写了控制台,所以日志记录的堆栈跟踪丢失了,并且在调用控制台时,玩笑使用的堆栈跟踪仅包含被覆盖的console.log(/ debug / error / ...)调用的堆栈。在安装文件中。因此,为了掌握原始堆栈跟踪信息,我们抛出了一个错误。然后可以将其附加到正在记录的文本中。不是特别漂亮,但是可以用。

答案 1 :(得分:1)

有多种方法可以让您的expect调用变得棘手,让您知道发生故障的位置。这样的东西有用吗?

const location = "case 1 failed";
const result = someFunction();  
expect({result: result, location}).toEqual({result: "hello", location});

现在,如果someFunction()返回的值不是“ hello”,它会告诉您位置值,因为它抱怨期望的值。

仅当您遇到Jest错误,但没有从正常的expect故障消息中获取足够的信息并且您需要更多详细信息时,这才真正有用。

答案 2 :(得分:0)

测试环境很好地处理了这个问题。它缓冲所有控制台消息,按测试对它们进行分组,并且仅在测试失败时才显示它们。环境与设置文件或报告程序的好处在于,它可以有选择地应用于特定测试,而将全局控制台留给所有其他人。

./tests/testEnvironment.js

const NodeEnvironment = require('jest-environment-node');
const colors = require('colors');

class TestEnvironment extends NodeEnvironment {
    constructor(config, context) {
        super(config, context);
    }

    async setup() {
        await super.setup();

        this.global.consoleItems = [];

        this.global.console = { ...console,
            original: console,
            log: ((message) => this.global.consoleItems.push({type: 'log', message })),
            error: ((message) => this.global.consoleItems.push({type: 'error', message })),
            warn: ((message) => this.global.consoleItems.push({type: 'warn', message })),
            info: ((message) => this.global.consoleItems.push({type: 'info', message })),
            debug: ((message) => this.global.consoleItems.push({type: 'debug', message })),
        };
    }

    async teardown() {
        this.global.console = this.global.console.original;
        await super.teardown();
    }

    async handleTestEvent(event, state) {
        if (event.name === 'test_done' && event.test.errors.length > 0) {
            let test = event.test;
            let fullTestName = event.test.name;
            while (test.parent != null && test.parent.name !== 'ROOT_DESCRIBE_BLOCK') {
                fullTestName = test.parent.name + ' › ' + fullTestName;
                test = test.parent;
            }
            this.global.console.original.log(colors.bold.red('Console messages for failed test ' + fullTestName));
            this.global.consoleItems.forEach((item) => {
                if (item.type === 'log') {
                    this.global.console.original.log('    - ' + item.message);
                } else if (item.type === 'error') {
                    this.global.console.original.error('    - ' + item.message);
                } else if (item.type === 'warn') {
                    this.global.console.original.warn('    - ' + item.message);
                } else if (item.type === 'info') {
                    this.global.console.original.info('    - ' + item.message);
                } else if (item.type === 'debug') {
                    this.global.console.original.debug('    - ' + item.message);
                }
            });
            this.global.console.original.log('\n');
        }
        if (event.name === 'test_done') {
            this.global.consoleItems = [];
        }
    }

}

module.exports = TestEnvironment;

对于每个测试套件,使用此环境需要以下注释:

/**
 * @jest-environment ./tests/testEnvironment
 */

答案 3 :(得分:0)

这可能是不好的做法,但我注意到 expect().toBe() 或其他东西如果不相等会抛出错误,您可以先捕获它并记录它。

这是我尝试测试每条短信是否都有翻译语言时的示例代码。

test('All message has translate', () => {

  allAvailableText.forEach((key) => {

    const languagesOnTheKey = Object.keys(text[key]).sort()
    try {
      expect(languagesOnTheKey).toStrictEqual(languages)
    } catch (error) {
      console.log(key, languagesOnTheKey)
    }
    expect(languagesOnTheKey).toStrictEqual(languages)

  })
})

这只是最快的方法,对我来说也最容易阅读,因为选择的答案让我很难理解。