mercredi 25 juillet 2018

Why is Jest reporting these lines in my async Node code as not covered by tests?

Jest is reporting 3 lines as not covered, though as far as I can tell the tests do test them. This is part of a much larger project so I put together a couple of (relatively) small files that illustrate the issue.

The lines it reports uncovered are:

  • Line 10 is resolve({ within the setTimeout callback within the new Promise within fakePrefApiCall. -- This line should be covered because the "generates preference strings" test checks that the promise returned does resolve.
  • Line 48 is u.Preferences = pref.data; within findUserByName -- This line should be covered because "finds Abby" checks that the resolved value has the correct Preferences property
  • Line 50 is return u; within findUserByName -- Same as above

Am I doing something wrong here?

I found that using /* istanbul ignore next */ makes Jest ignore the line. I'm not entirely keen on simply ignoring things, though I tried it. When I ignored the 3 reported lines, it then reported 38 as uncovered (if (0 !== result.Path.indexOf(this.rootPath)) continue;) even though it is tested by the "finds Abby" test (false on iteration 1,4,5 and true on iteration 2,3)


The output I get is:

$ jest --verbose --detectOpenHandles tests/jest-oddity.test.js
 PASS  tests/jest-oddity.test.js
  Testing line coverage
    √ fake API call resolves (20ms)
    √ generates preference strings (4ms)
    √ finds Abby (5ms)
    √ handles ambiguos users (4ms)
    √ handles missing users (4ms)

--------------------------|----------|----------|----------|----------|-------------------|
File                      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
--------------------------|----------|----------|----------|----------|-------------------|
 *** IRRELEVANT LINES DELETED ***
  jest-oddity.js          |    81.82 |     87.5 |    83.33 |    83.33 |          10,48,50 |
--------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        3.857s
Ran all test suites matching /tests\\jest-oddity.test.js/i.
Done in 26.07s.

Code file:

// jest-oddity.js
class SampleClass {
  constructor () {
    this.rootPath = '/users/corporate';
  }

  async fakePrefApiCall(uid) {
    return new Promise((resolve, reject) => {
      setTimeout((uid) => {
        resolve({
          data: `pref-string-${uid}`
        });
      }, 1000);
    });
  }

  async getPreferences(uid) {
    /*return Promise.resolve({
      data: `pref-string-${uid}`
    });*/

    return this.fakePrefApiCall(uid);
  }

  async findUserByName(user) {
    let d = [
      {Id: 1, Username: 'Abby', Path: '/users/corporate/asmith'},
      {Id: 2, Username: 'Bob', Path: '/users/external/bwilliams'},
      {Id: 3, Username: 'Carly', Path: '/users/sales/cpoe'},
      {Id: 4, Username: 'Dean', Path: '/users/corporate/dmiller'},
      {Id: 5, Username: 'Dean', Path: '/users/corporate/dsmith'}
    ];

    let candidates = [];
    for (let result of d) {
      if (result.Username != user) continue;
      if (0 !== result.Path.indexOf(this.rootPath)) continue;

      candidates.push(result);
    }

    if (!candidates.length) { return null; }
    else if (candidates.length > 1) { return Promise.reject(`Multiple (${candidates.length}) results found for ${user}`); }

    let u = candidates[0];
    let pref = await this.getPreferences(u.Id);

    u.Preferences = pref.data;

    return u;
  }
}

module.exports = SampleClass;

Test file:

// jest-oddity.test.js
const SampleClass = require("./jest-oddity");

describe("Testing line coverage", () => {
  beforeAll(() => {
    testObj = new SampleClass();
  });

  it("fake API call resolves", () => {
    expect(testObj.fakePrefApiCall(22)).resolves.toMatchObject({
      data: 'pref-string-22'
    });
  });

  it("generates preference strings", () => {
    expect(testObj.getPreferences(57)).resolves.toEqual({data: 'pref-string-57'});
  });

  it("finds Abby", () => {
    let userPromise = testObj.findUserByName("Abby");
    expect(userPromise).resolves.toMatchObject({
      Username: 'Abby',
      Preferences: 'pref-string-1'
    });
  });

  it("handles ambiguos users", () => {
    expect(testObj.findUserByName("Dean")).rejects.toMatch('Multiple');
  });

  it("handles missing users", () => {
    expect(testObj.findUserByName("nonexistent")).resolves.toBe(null);
  })
});

Aucun commentaire:

Enregistrer un commentaire