Как выполнить модульное тестирование логики в рамках подписки? - PullRequest
0 голосов
/ 03 октября 2019

У меня есть проблема, для которой я не знаю, что является лучшим решением. Просто для контекста, я использую Typescript для приложения NodeJS (v12). У меня есть класс, который оборачивает некоторую библиотеку и выдает некоторые значения из Subject (rxjs).

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

Iя искал ответы на этот вопрос, мне кажется, что этот вариант использования довольно распространен, поэтому я предположил бы, что раньше этот вопрос задавали больше людей, но все ответы, которые я нахожу, связаны со значением next (), так чтомодульное тестирование результата некоторой логики, которая в конечном итоге запускает некоторые новые данные.

Этот вариант использования будет включать в себя модульный тест, который вводит некоторые данные в субъект, а затем ожидает вызова нескольких служб (используя для этого sinon)

Небольшой фрагмент для этого будет выглядеть следующим образом:

  1. Библиотека, которая скоро будет заменена
import {Subject} from 'rxjs';

// This is to fake another library I am using, I will just call a callback on an interval
class SomeCustomLibrary {
    generateSomethingToBeWrapped(f: (someDate: Date) => void): void {
        setInterval(() => {
            f(new Date());
        }, 1000);
    }
} 
Обертка вокруг этой библиотеки, подлежащей замене
import {Subject} from 'rxjs';

interface IWrapper {
    someObservable: Subject<any>;
}

class CustomWrapper implements IWrapper {

    public someObservable: Subject<any>;
    public someLibrary: SomeCustomLibrary;

    constructor() {
        this.someObservable = new Subject<any>();
        this.someLibrary = new SomeCustomLibrary();

        this.someLibrary.generateSomethingToBeWrapped(someDate => {
            this.someObservable.next(someDate);
        });
    }
}
Класс, который использует обертку и для которого я хочу проверить код
class MyVeryImportantBusinessLogicService {
    // Making it public to avoid some boilerplate in the test, I am making it private and
    // injecting it the real implementation
    public myWrappedObservable: IWrapper;

    constructor() {
        this.myWrappedObservable = new CustomWrapper();
        this.myWrappedObservable.someObservable.subscribe(newData => {
            this.doSomethingVeryCritical(newData);
        });
    }

    private doSomethingVeryCritical(newData: any) {
        // call external class 1
        // call external class 2
        // call external class 3
        console.log('Something has been done at this point');
    }
}
Тест
describe('Now trying to test my logic', () => {
    const myVeryImportantBusinessLogicService = new MyVeryImportantBusinessLogicService();
    it('Here I would expect all the external classes (services) to be called', (done) => {
        myVeryImportantBusinessLogicService.myWrappedObservable.someObservable.next({hello: 'world'});
        setTimeout(() => {
            // sinon expect external class 1
            // sinon expect external class 2
            // sinon expect external class 3
            done();
        }, 1);
    });
});

Этот подход работает, но меня беспокоит то, что я вынужден ввести setTimeout, чтобы иметь возможность ожидать, что заставляет модульный тест ждатьискусственно и произвольно

Это заставляет тест "ждать", чтобы что-то было сделано (так как я высмеиваю все зависимости, это происходит немедленно), но я считаю, что это не правильный (или лучший) подход для этого.

Другой подход пришел мне на ум - протестировать код отдельно, а для этого класса просто убедитесь, что он подписан на конкретную подписку.

Есть ли какая-нибудь хорошая практика, которую я мог бы принять здесь

...