Как заглушить Fluture?

Фон

Я пытаюсь преобразовать фрагмент кода из старых добрых обещаний во что-то, используя Flutures и Sanctuary:

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

проблема

Теперь, обычно, используя Обещания, я могу использовать библиотеку, такую ​​как sinonjs, чтобы заглушить обещания, то есть подделать их результаты, заставить их решить, отклонить и т. Д.

Это принципиально, так как помогает проверить несколько направлений ветвления и убедиться, что все работает как положено.

Однако с Flutures все по-другому. Нельзя просто заглушить Fluture, и я не нашел ни одной синонимичной библиотеки, которая могла бы помочь.

Вопросы

  1. Как вы заглушаете Flutures?
  2. Есть ли конкретные рекомендации по использованию TDD с Flutures/Sanctuary?

1 ответ

Решение

Я не уверен, но эти Flutures (это имя! ... не имеет значения, API выглядит круто) - простые объекты, как обещания. У них только более сложный API и другое поведение.

Более того, вы можете легко создавать "фальшивые" флейты с Future.of, Future.reject вместо того, чтобы делать некоторые реальные вызовы API.

Да, Sinon содержит сахарные помощники, такие как resolves, rejects но они просто оболочки, которые могут быть реализованы с callsFake,

Таким образом, вы можете легко создать заглушку, которая создает будущее, как это.

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();
});

Как предположил Збигнев, Future.of а также Future.reject являются хорошими кандидатами для насмешек с использованием простого старого javascript или любых других инструментов или фреймворков, которые вам нравятся.

Чтобы ответить на часть 2 вашего вопроса, любые конкретные рекомендации, как сделать TDD с Fluture. Конечно, нет единственно верного способа сделать это. Однако я рекомендую вам потратить немного времени на удобочитаемость и простоту написания тестов, если вы планируете использовать Futures во всем приложении.

Это относится ко всему, что вы часто включаете в тесты, а не только к фьючерсам. Идея состоит в том, что когда вы просматриваете тестовые случаи, вы увидите намерение разработчика, а не шаблон, чтобы заставить ваши тесты делать то, что вам нужно.

В моем случае я использую mocha & chai в стиле BDD (дано тогда, когда). И для удобства чтения я создал эти вспомогательные функции.

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 справиться с этим.

Другие вопросы по тегам