1

背景

我正在尝试使用 Flutures 和 Sanctuary 将旧 Promises 中的代码片段转换为某些东西:

https://codesandbox.io/embed/q3z3p17rpj?codemirror=1

问题

现在,通常,使用 Promises,我可以使用像sinonjs这样的库来存根 Promise,即伪造它们的结果、强制解决、拒绝等。

这是基本的,因为它有助于测试多个分支方向并确保一切正常。

然而,对于 Flutures,情况就不同了。不能简单地存根 Fluture,我也没有找到任何可以提供帮助的 sinon 式库。

问题

  1. 你如何存根 Flutures ?
  2. 对使用 Flutures/Sanctuary 进行 TDD 有什么具体建议吗?
4

2 回答 2

1

我不确定,但那些 Flutures(这个名字!......没关系,API 看起来很酷)是普通对象,就像 Promise 一样。它们只有更复杂的 API 和不同的行为。

此外,您可以使用 轻松创建“模拟”fluture Future.ofFuture.reject而不是进行一些真正的 API 调用。

是的,sinon 包含类似的糖助手resolvesrejects但它们只是可以用callsFake.

因此,您可以轻松地创建像这样创建 Fluture 的存根。

someApi.someFun = sinon.stub().callsFake((arg) => {
    assert.equals(arg, 'spam');
    return Future.of('bar');
});

然后,您可以像任何其他 API 一样对其进行测试。唯一的问题是“异步性”,但这可以像下面提出的那样解决。

// with async/await
it('spams with async', async () => {
    const result = await someApi.someFun('spam).promise();
    assert.equals(result, 'bar');
});

// or leveraging mocha's ability to wait for returned thenables
it('spams', async () => {
    return someApi.someFun('spam)
        .fork(
             (result) => { assert.equals(result, 'bar');},
             (error) => { /* ???? */ }
        )
        .promise();
});
于 2018-07-18T20:46:19.037 回答
0

正如 Zbigniew 所建议的那样,Future.of并且Future.reject是使用普通的旧 javascript 或您喜欢的任何工具或框架进行模拟的绝佳候选者。

要回答您问题的第 2 部分,关于如何使用 Fluture 进行 TDD 的任何具体建议。当然,它不应该是一种真正的方式。但是,如果您计划在整个应用程序中使用 Futures,我确实建议您花一点时间在可读性和编写测试的易用性上。

这适用于您经常包含在测试中的任何内容,而不仅仅是 Futures。这个想法是,当您浏览测试用例时,您会看到开发人员的意图,而不是样板文件来让您的测试完成您需要的工作。

就我而言,我使用 BDD 风格的 mocha & chai(当时给出)。为了便于阅读,我创建了这些辅助函数。

const {expect} = require('chai');

exports.expectRejection = (f, onReject) =>
    f.fork(
        onReject,
        value => expect.fail(
            `Expected Future to reject, but was ` +
            `resolved with value: ${value}`
        )
    );

exports.expectResolve = (f, onResolve) =>
    f.fork(
        error => expect.fail(
            `Expected Future to resolve, but was ` +
            `rejected with value: ${error}`
        ),
        onResolve
    );

正如你所看到的,没有什么神奇的事情发生,我只是让意想不到的结果失败,让你处理预期的路径,用它做更多的断言。

现在一些测试看起来像这样:

const Future = require('fluture');
const {expect} = require('chai');
const {expectRejection, expectResolve} = require('../util/futures');

describe('Resolving function', () => {
    it('should resolve with the given value', done => {
        // Given
        const value = 42;
        // When
        const f = Future.of(value);
        // Then
        expectResolve(f, out => {
            expect(out).to.equal(value);
            done();
        });
    });
});

describe('Rejecting function', () => {
    it('should reject with the given value', done => {
        // Given
        const value = 666;
        // When
        const f = Future.of(value);
        // Then
        expectRejection(f, out => {
            expect(out).to.equal(value);
            done();
        });
    });
});

跑步应该是一次通过,一次失败。

  ✓ Resolving function should resolve with the given value: 1ms
  1) Rejecting function should reject with the given value

  1 passing (6ms)
  1 failing

  1) Rejecting function
       should reject with the given value:
     AssertionError: Expected Future to reject, but was resolved with value: 666

请记住,这应该被视为异步代码。这就是为什么我总是接受该done函数作为参数it()并在预期结果结束时调用它。或者,您可以更改辅助函数以返回一个承诺并让 mocha 处理它。

于 2018-12-12T09:26:43.033 回答