chai-as-promised:单个测试中有多个expect语句。

11

我正在使用chai-as-promised测试一些承诺。 我的问题是,我不确定如何在单个测试中有多个expect语句。 为了使expect().to.be.fulfilled正常工作,我需要像这样返回它:

我使用chai-as-promised测试承诺,但不确定如何在一个测试中有多个expect语句。为了使expect().to.be.fulfilled正常工作,需要将其返回。

it('test', () => {
  return expect(promise).to.be.fulfilled
}

...或者使用notify,像这样:

it('test', (done) => {
  expect(promise).to.be.fulfilled.notify(done)
}

当我需要检查另一件事情,比如某个函数是否被调用时,就会出现问题,代码如下:

it('test', (done) => {
  var promise = doSomething()
  expect(sinon_function_spy.callCount).to.equal(1)
  expect(promise).to.be.fulfilled.notify(done)
})

这里的问题在于,由于doSomething()是异步的,当我调用expect时,调用sinon_function_spy可能还没有发生,导致测试不可靠。如果我使用then,像这样:

it('test', (done) => {
  var promise = doSomething()
  promise.then(() => {
    expect(sinon_function_spy.callCount).to.equal(1)
  })
  expect(promise).to.be.fulfilled.notify(done)
})

在这种情况下,测试技术上会按预期成功或失败,但它会失败,因为承诺被拒绝,由于then调用中抛出了异常。同样,如果我有一个期望承诺被拒绝的情况:

it('test', (done) => {
  var promise = doSomething()
  promise.then(() => {
    expect(sinon_function_spy.callCount).to.equal(1)
  })
  expect(promise).to.be.rejected.notify(done)
})

因为 promise 被拒绝并且没有调用 then,所以对 sinon_function_spy 的检查从未被调用。

我怎样才能可靠地执行并返回正确值的两个 expect 语句?


Mocha 使用异常或 Promise 拒绝来报告失败。即使使用同步断言,如果第一个失败,则不会运行其余的断言。我认为 Mocha 真的不支持在失败后执行断言。你需要两个断言都执行的原因是什么? - Jacob
是的。我需要检查这两件事情是否发生,才能使测试用例成功。如果 Promise 按预期解决了,但函数没有被调用,那么测试用例就不应该通过。 - ewok
5个回答

3
实现多个期望的方法
it('should fail if no auth', () => {
    const promise = chai.request(server).get('/albums');
    return expect(promise).to.be.rejected.then(() => {
      return promise.catch(err => {
        expect(err).not.to.be.null;
        expect(err.response).to.have.status(401);
        expect(err.response).to.be.a.json;
      });
   });
});

2
如果您使用mocha或jest作为测试框架,您可以在then()块中返回带有期望的promise:
it('test', () => {
   return doSomething().then( () => {
     expect(sinon_function_spy.callCount).to.equal(1);
   });
});

这个测试不会结束,直到承诺成功完成并且 expect 已经运行。如果您正在使用Jasmine,可以使用jasmine-promises包来获得相同的功能。

对于反向情况,我建议创建一个包装器来反转承诺的极性:

function reverse( promise ) {
   //use a single then block to avoid issues with both callbacks triggering
   return promise.then(
       () => { throw new Error("Promise should not succeed"); }
       e => e; //resolves the promise with the rejection error
   );
}

现在你可以做以下事情:
it('test', () => {
   return reverse( doSomethingWrong() ).then( error => {
       expect( error.message ).to.equal("Oh no");
   });
});

如果我期望 Promise 被解决,那么这个方法可以工作。但是如果我的测试用例期望 Promise 被拒绝呢?返回 Promise 本身会导致 Mocha 将测试用例注册为失败。 - ewok

1
如果想要断言 Promise 已经被实现,并且调用已按预期执行,在断言方面你实际上不需要第一部分。只要你返回 Promise,只要 Promise 拒绝,mocha 测试用例本身就会失败:
it('test', () => {
  return doSomething()
    .then(() => {
      expect(sinon_function_spy.callCount).to.equal(1)
    });
});

如果doSomething()返回的Promise被拒绝,测试用例也会被拒绝。如果expect断言失败,它也会使测试用例失败并带有该失败的断言。如果您想更加明确:
it('test', () => {
  return doSomething()
    .then(() => {
      expect(sinon_function_spy.callCount).to.equal(1)
    }, err => {
      expect(err).to.not.exist;
    });
});

如果你使用这种带有两个回调函数的then,当第一个回调函数中的断言失败时,它不会到达第二个回调函数,所以只有Mocha会看到这个失败的断言。

以下是如何进行预期失败的Promise:

it('test', () => {
  return doSomething()
    .then(() => {
      throw new Error('Promise should not have resolved');
    }, err => {
      expect(err).to.exist;
      expect(sinon_function_spy.callCount).to.equal(1)
    });
})

1
chai-as-promised在某些情况下非常适用,但我发现有很多情况下,使用Promises比尝试编写测试来使用chai-as-promised更加简单。 - Jacob

0
我发现最好的方法是这样做:
it('test', async () => {
  await expect(promise1).to.eventually.be.equal(1);
  await expect(promise2).to.eventually.be.equal(2);
})

0
在单个测试中使用多个断言的问题之一是,如果第一个断言失败,我们就无法获得有关其余断言的反馈。在某些情况下,这可能没关系,但如果您的需求要求您获得所有断言的反馈,则只剩下几个选项。
其中一个选择是看看是否可以使用Jasmine和Jasmine的断言而不是Chai。默认情况下,Jasmine会检查并报告测试中的所有断言,并且Jasmine能够做到这一点,而Mocha和Chai则不能。 Jasmine的测试运行器和断言库更紧密地集成在一起,这也许是他们能够做到这一点的原因。
如果Jasmine不是一个选择,那么另一个想法是将测试执行放在before块中,然后在单独的it块中验证每个断言。这会产生更多的反馈,但是一些测试报告工具可能会将其报告为N个测试而不是单个测试。
第三个选择是使用我构建和发布的Node.js模块multi-assert。这允许在同一个测试中使用多个Chai断言(以及其他一些断言库),并将报告所有失败情况。以下是一个与异步代码配合使用的示例:
const { expect } = require('chai');
const { multiAssertAsync } = require('multi-assert');

describe('Test - async/promises', () => {
    
    async function fetchData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('{"status":"bluegreen"}');
            }, 300)
        });
    }

    it('should expect status to be yellowblue, yellowred, bluegreen, and 3 to equal 4', async () => {
        await multiAssertAsync([
            async () => expect((JSON.parse(await fetchData())).status).to.equal('yellowblue'),
            () => expect(3).to.equal(4),
            async () => expect((JSON.parse(await fetchData())).status).to.equal('bluegreen'),
            async () => expect((JSON.parse(await fetchData())).status).to.equal('yellowred')
        ]);
    });
});

执行后,我们看到第一个、第三个和第四个断言失败,并报告代码中的各种缺陷,提供最大的透明度和机会,使开发人员和测试人员完全修复代码。
1) Test - async/promises
       should expect status to be yellowblue, yellowred, bluegreen, and 3 to equal 4:
     AssertionError: 

      MultipleAssertionError: expected 3 to equal 4
        at /Users/user123/proj/multi-assert/examples/example-async.spec.js:17:32
        at /Users/user123/proj/multi-assert/multi-assert-async.js:12:27
        at Array.map (<anonymous>)

      MultipleAssertionError: expected 'bluegreen' to equal 'yellowblue'
        at /Users/user123/proj/multi-assert/examples/example-async.spec.js:16:75
        at async /Users/user123/proj/multi-assert/multi-assert-async.js:12:21
        at async Promise.all (index 0)

      MultipleAssertionError: expected 'bluegreen' to equal 'yellowred'
        at /Users/user123/proj/multi-assert/examples/example-async.spec.js:19:75
        at async /Users/user123/proj/multi-assert/multi-assert-async.js:12:21
        at async Promise.all (index 3)

      at /Users/user123/proj/multi-assert/multi-assert-async.js:22:23

我还没有尝试使用.then语法,因为在很多情况下,async/await可能更易读和理解。如果存在任何替代语法的问题,我很乐意帮忙解决。希望这可以帮到你!

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