【问题标题】:@hapi/lab with lab-transform-typescript is returning buggy code coverage results@hapi/lab 与 lab-transform-typescript 正在返回错误的代码覆盖率结果
【发布时间】:2020-07-12 03:25:20
【问题描述】:

我在 TypeScript 中有一个 HapiJS 项目,想开始添加一些单元测试。现在代码很简单:

server.ts

import * as Hapi from "@hapi/hapi";
import routes from "./routes";

const server = new Hapi.Server({
    port: 80,
    host: "0.0.0.0",
    debug: {
        request: ["error"]
    }
});

let serverSetup = false;

const setupServer = async () =>
{
    if (serverSetup) return;

    await server.register([routes]);

    serverSetup = true;
};

export const init = async () =>
{
    await setupServer();
    await server.initialize();
    return server;
};

export const start = async () =>
{
    await setupServer();
    await server.start();
    console.log(`Server running at: ${server.info.uri}`);
    return server;
};

routes.ts

import * as Hapi from "@hapi/hapi";

export default {
    name: "RouteIndex",
    version: "1.0.0",
    register: function (server:Hapi.Server, options:Hapi.ServerRegisterOptions)
    {

        server.route({
            method: "GET",
            path: "/",
            handler: (request, h) =>
            {
                return {message: "Hello, World!"};
            }
        });

        server.route({
            method: "*",
            path: "/{any*}",
            handler: (request, h) =>
            {
                return "404!";
            }
        });

    }
};

然后是我的单元测试:

server.test.js

const Lab = require("@hapi/lab");
const { expect } = require("@hapi/code");
const { afterEach, beforeEach, describe, it } = exports.lab = Lab.script();
const { init, start } = require("../src/server");

const HTTP_PORT = 80;
const HTTP_STATUS_OK = 200;

describe("General Server Tests", () =>
{
    let server;

    beforeEach(async () =>
    {
        server = await init();
    });

    afterEach(async () =>
    {
        await server.stop();
    });

    it("Starts successfully", async () =>
    {
        server = await start();
        expect(server.type).to.equal("tcp");
        expect(server.settings.port).to.equal(HTTP_PORT);
        expect(server.settings.host).to.equal("0.0.0.0");
    });

    it("Responds to GET requests", async () =>
    {
        const res = await server.inject({
            method: "GET",
            url: "/"
        });
        expect(res.statusCode).to.equal(HTTP_STATUS_OK);
    });
});

要运行我的测试,我的 package.json 中有以下内容:

{
    // ...
    "scripts": {
        // ...
        "test": "lab -vclS -T node_modules/lab-transform-typescript **/*.test.js",
        // ...
    }
    // ...
}
  • 添加 -v 标志是因为我更喜欢详细输出
  • 添加了-c 标志,以便我可以获得代码覆盖率报告,这就是我的问题所在
  • 必须添加 -l 标志,否则我会收到错误消息:The following leaks were detected:__extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator,...(由 TypeScript 编译器创建的所有全局变量)
  • -S 标志是根据 lab-transform-typescript documentation (-S == --sourcemaps) 添加的
  • -T 标志用于加载 lab-transform-typescript (-T == --transform)

当我运行测试时,结果如下:

stevenbarnett@MacBook-Pro hapi-test % npm run test

> hapi-test@1.0.0 test /Users/stevenbarnett/Repos/hapi-test
> lab -vclS -T node_modules/lab-transform-typescript **/*.test.js

Server running at: http://0.0.0.0:80
General Server Tests
  ✔ 1) Starts successfully (4 ms)
  ✔ 2) Responds to GET requests (7 ms)


3 tests complete
Test duration: 111 ms
Coverage: 71.50% (61/214)
src/server.ts missing coverage from file(s):
        null on line(s): , , , , , , , , , , , , , , , , , , 
src/routes.ts missing coverage from file(s):
        null on line(s): , , , , , , , , , , , , , , , , , , , , , 
        src/routes/index.ts on line(s): 24

所以我知道src/routes/index.ts 在第 24 行缺少覆盖(这是 404 错误,我没有测试 404)——但其他行是荒谬的:

  • null on line(s): , , , , , , , , , , , , , , , , , ,

为什么会发生这种情况,我该如何解决?

【问题讨论】:

    标签: node.js typescript unit-testing hapijs lab


    【解决方案1】:

    我创建了一个 HapiJS Lab 的分支来解决我自己的问题。我的 fork 不是完美(它不会处理具有多个源文件的代码,例如串联),但它适用于 TypeScript 处理。

    请注意,我的更改是从版本 22.0.4 派生出来的(特别是提交 cd0bd3b1ad063ae62b58a764751fb3465a49fe99),因此这些更改可能无法完全在较新的版本上运行。

    我的第一个更改是lib/coverage.js

             ret.source[num].hits = data.lines[num];
             ret.source[num].miss = isMiss;
         });
     
    +    // Translate source maps
    +    if (ret.sourcemaps)
    +    {
    +        const mappedSource = {};
    +        const loadedOriginalSource = {};
    +        Object.keys(ret.source).forEach(id =>
    +        {
    +            const line = ret.source[id];
    +            if (line.originalFilename)
    +            {
    +                // ERROR: If a file came from two original source files (e.g. concatenation)
    +                //        then we'll only include the first file in line.source and we'll
    +                //        combine the number of hits
    +                //
    +                // Ideally this method needs a way to return more than one file and the files
    +                // need to be merged by whoever reads the result
    +                //
    +                // Although for just TypeScript-to-JavaScript, this is fine
    +                const originalSource = loadedOriginalSource[line.originalFilename] || Fs.readFileSync(line.originalFilename, 'utf8').split("\n");
    +                loadedOriginalSource[line.originalFilename] = originalSource;
    +                let originalLine = mappedSource[line.originalLine] || {
    +                    source: originalSource[line.originalLine - 1],
    +                    hits: 0,
    +                    miss: false
    +                };
    +                originalLine.hits += (line.hits || 0);
    +                originalLine.miss = originalLine.miss || line.miss;
    +                mappedSource[line.originalLine] = originalLine;
    +            }
    +        });
    +        ret.source = mappedSource;
    +        ret.sloc = Object.keys(ret.source).length;
    +        ret.hits = 0;
    +        ret.misses = 0;
    +        ret.sourcemaps = false;
    +        Object.keys(ret.source).forEach(id =>
    +        {
    +            if (ret.source[id].miss)
    +            {
    +                ret.misses++;
    +            }
    +            else
    +            {
    +                ret.hits++;
    +            }
    +        });
    +    }
    +
         ret.percent = ret.hits / ret.sloc * 100;
         return ret;
     };
    

    我的第二个也是不太重要的变化是提高输出。当我看到“2/127”之类的东西时,我一直感到困惑,并认为只有 2 行代码覆盖,而实际上 125 行代码覆盖。当 100% 的行具有代码覆盖率时,也有 no 输出,这是我不喜欢的 - 我希望看到我确定地达到了我的目标。所以我对lib/reporters/console.js做了如下修改:

         const coverage = notebook.coverage;
         if (coverage) {
             const status = coverage.percent.toFixed(2) + '%';
    +        const outputColor = coverage.percent === 100 ? green : red;
             output += 'Coverage: ';
    -        output += coverage.percent === 100 ? green(status) : red(status + ' (' + (coverage.sloc - coverage.hits) + '/' + coverage.sloc + ')');
    +        output += outputColor(status + ' (' + coverage.hits + '/' + coverage.sloc + ' lines)');
             if (coverage.percent < 100) {
                 for (const file of coverage.files) {
                     let missingLines;
    

    【讨论】:

      猜你喜欢
      • 2015-09-17
      • 1970-01-01
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      • 1970-01-01
      • 2013-08-18
      • 2021-05-25
      相关资源
      最近更新 更多