У меня есть проблема, для которой я не знаю, что является лучшим решением. Просто для контекста, я использую Typescript для приложения NodeJS (v12). У меня есть класс, который оборачивает некоторую библиотеку и выдает некоторые значения из Subject (rxjs).
Идея состоит в том, что в будущем я ожидаю изменений в этом упакованном классе, поэтому я построил интерфейс поверх него и использую Subject для передачи значений, которые будут использоваться другими классами.
Iя искал ответы на этот вопрос, мне кажется, что этот вариант использования довольно распространен, поэтому я предположил бы, что раньше этот вопрос задавали больше людей, но все ответы, которые я нахожу, связаны со значением next (), так чтомодульное тестирование результата некоторой логики, которая в конечном итоге запускает некоторые новые данные.
Этот вариант использования будет включать в себя модульный тест, который вводит некоторые данные в субъект, а затем ожидает вызова нескольких служб (используя для этого sinon)
Небольшой фрагмент для этого будет выглядеть следующим образом:
- Библиотека, которая скоро будет заменена
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, чтобы иметь возможность ожидать, что заставляет модульный тест ждатьискусственно и произвольно
Это заставляет тест "ждать", чтобы что-то было сделано (так как я высмеиваю все зависимости, это происходит немедленно), но я считаю, что это не правильный (или лучший) подход для этого.
Другой подход пришел мне на ум - протестировать код отдельно, а для этого класса просто убедитесь, что он подписан на конкретную подписку.
Есть ли какая-нибудь хорошая практика, которую я мог бы принять здесь