如何在订阅中对测试逻辑进行单元化?

时间:2019-10-03 06:30:17

标签: typescript unit-testing rxjs observable

我有一个问题,我不知道什么是最佳解决方案。仅出于上下文考虑,我将Typescript用于NodeJS(v12)应用程序,我有一个包装一些库并从Subject(rxjs)发出一些值的类。

想法是,将来我希望对此包装的类进行更改,因此我在顶部构建了一个接口,并使用Subject发出了要由其他类使用的值。

我一直在寻找答案,在我看来,这种用例非常普遍,因此我假设以前有更多人遇到过这个问题,但是我发现的所有答案都与next()的值有关。 )是,因此要对最终触发新数据的某些逻辑的结果进行单元测试。

此用例将涉及单元测试,将一些数据注入到Subject中,然后期望已经调用了多个服务(为此使用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);
    }
} 
  1. 要替换的库的包装器
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);
        });
    }
}
  1. 使用包装的类,我要为其测试代码
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');
    }
}
  1. 测试
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才能达到我的期望,从而使单元测试不得不等待人为地,任意地等待。

这使测试“等待”要完成的事情(因为我嘲笑了所有依赖项,因此会立即发生),但是我认为这不是正确(或最佳)的方法。

我想到的另一种方法是分别测试代码,对于此类,只需确保已预订了特定的预订即可。

这里有什么好的做法可以采用吗?

0 个答案:

没有答案