如何存根 Fluture?
How to stub Fluture?
背景
我正在尝试使用 Flutures 和 Sanctuary 将代码片段从良好的旧 Promises 转换为某些东西:
https://codesandbox.io/embed/q3z3p17rpj?codemirror=1
问题
现在,通常情况下,使用 Promises,我可以使用像 sinonjs 这样的库来存根 promises,即伪造它们的结果,强制解决,拒绝等。
这是基础,因为它有助于测试多个分支方向并确保一切正常。
然而,对于 Flutures,情况就不同了。不能简单地存根 Fluture,我也没有找到任何 sinon-esque 库可以提供帮助。
问题
- 你如何存根 Flutures ?
- 是否有关于使用 Flutures/Sanctuary 进行 TDD 的具体建议?
我不确定,但是那些 Flutures(这个名字!...没关系,API 看起来很酷)是普通对象,就像承诺一样。他们只有更精细的 API 和不同的行为。
此外,您可以使用 Future.of
、Future.reject
轻松创建 "mock" fluture,而不是进行一些真正的 API 调用。
是的,sinon 包含像 resolves
、rejects
这样的糖助手,但它们只是可以用 callsFake
.
实现的包装器
因此,您可以像这样轻松创建创建 fluture 的存根。
someApi.someFun = sinon.stub().callsFake((arg) => {
assert.equals(arg, 'spam');
return Future.of('bar');
});
然后你就可以像测试其他任何东西一样测试它了API。
唯一的问题是 "asynchronicity",但可以按照下面的建议解决。
// 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();
});
正如 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()
中的参数并在预期结果结束时调用它。或者,您可以将辅助函数更改为 return promise 并让 mocha 处理它。
背景
我正在尝试使用 Flutures 和 Sanctuary 将代码片段从良好的旧 Promises 转换为某些东西:
https://codesandbox.io/embed/q3z3p17rpj?codemirror=1
问题
现在,通常情况下,使用 Promises,我可以使用像 sinonjs 这样的库来存根 promises,即伪造它们的结果,强制解决,拒绝等。
这是基础,因为它有助于测试多个分支方向并确保一切正常。
然而,对于 Flutures,情况就不同了。不能简单地存根 Fluture,我也没有找到任何 sinon-esque 库可以提供帮助。
问题
- 你如何存根 Flutures ?
- 是否有关于使用 Flutures/Sanctuary 进行 TDD 的具体建议?
我不确定,但是那些 Flutures(这个名字!...没关系,API 看起来很酷)是普通对象,就像承诺一样。他们只有更精细的 API 和不同的行为。
此外,您可以使用 Future.of
、Future.reject
轻松创建 "mock" fluture,而不是进行一些真正的 API 调用。
是的,sinon 包含像 resolves
、rejects
这样的糖助手,但它们只是可以用 callsFake
.
因此,您可以像这样轻松创建创建 fluture 的存根。
someApi.someFun = sinon.stub().callsFake((arg) => {
assert.equals(arg, 'spam');
return Future.of('bar');
});
然后你就可以像测试其他任何东西一样测试它了API。 唯一的问题是 "asynchronicity",但可以按照下面的建议解决。
// 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();
});
正如 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()
中的参数并在预期结果结束时调用它。或者,您可以将辅助函数更改为 return promise 并让 mocha 处理它。