如何使用debounce
运算符编写Jasmine测试以测试observable?我跟着this blog post了解了应该如何测试的原则,但它似乎没有用。
以下是我用来创建observable的工厂:
import Rx from "rx/dist/rx.all";
import DOMFactory from "../utils/dom-factory";
import usernameService from "./username.service";
function createUsernameComponent(config) {
const element = DOMFactory(config);
const username = Rx.Observable
.fromEvent(element.find('input'), 'input')
.pluck('target', 'value')
.startWith(config.value);
const isAvailable = username
.debounce(500)
.tap(() => console.info('I am never called!'))
.flatMapLatest(usernameService.isAvailable)
.startWith(false);
const usernameStream = Rx.Observable.combineLatest(username, isAvailable)
.map((results) => {
const [username, isAvailable] = results;
return isAvailable ? username : ''
})
.distinctUntilChanged();
return Object.freeze({
stream: usernameStream,
view: element
});
}
export default createUsernameComponent;
请注意,测试从不调用tap
运算符。但是,如果我在浏览器上运行此代码,它将正确执行。
以下是我对测试的尝试:
import Rx from "rx/dist/rx.all";
import Username from "./username.component";
import DataItemBuilder from "../../../test/js/utils/c+j-builders";
import usernameService from "./username.service"
describe('Username Component', () => {
let input, username;
beforeEach(() => {
const usernameConfig = DataItemBuilder.withName('foo')
.withPrompt('label').withType('text').build();
const usernameComponent = Username(usernameConfig);
usernameComponent.stream.subscribe(value => username = value);
input = usernameComponent.view.find('input');
});
it('should set to a valid username after debounce', () => {
const scheduler = injectTestSchedulerIntoDebounce();
scheduler.scheduleRelative(null, 1000, () => {
doKeyUpTest('abcddd', 'abcdd');
scheduler.stop();
});
scheduler.start();
scheduler.advanceTo(1000);
});
function injectTestSchedulerIntoDebounce() {
const originalOperator = Rx.Observable.prototype.debounce;
const scheduler = new Rx.TestScheduler();
spyOn(Rx.Observable.prototype, 'debounce').and.callFake((dueTime) => {
console.info('The mocked debounce is never called!');
if (typeof dueTime === 'number') {
return originalOperator.call(this, dueTime, scheduler);
}
return originalOperator.call(this, dueTime);
});
return scheduler;
}
function doKeyUpTest(inputValue, expectation) {
input.val(inputValue);
input.trigger('input');
expect(username).toBe(expectation);
}
});
当我运行测试时,假的debounce
永远不会被调用。一旦我越过username
,我打算嘲笑debounce
服务。
答案 0 :(得分:1)
在测试代码中,您将触发scheduleRelative
函数内的输入事件。这不起作用,因为您在进行更改之前提前1000ms。然后,debouncer等待500ms去除isAvailable
呼叫,但你已经停止了调度程序,所以时间不会在之后推进。
你应该做的是:在推进调度程序时间之前触发输入事件,或者在scheduleRelative
函数中触发输入事件一段时间< = 500ms,然后在scheduleRelative
函数内部触发1000ms你必须使用预期的输出调用expect
函数,然后停止调度程序。
它应该是这样的:
it('should set to a valid username after debounce', () => {
const scheduler = injectTestSchedulerIntoDebounce();
scheduler.scheduleRelative(null, 500, () => {
input.val(inputValue);
input.trigger('input');
});
scheduler.scheduleRelative(null, 1000, () => {
expect(username).toBe(expectation);
scheduler.stop();
});
scheduler.start();
scheduler.advanceTo(1000);
});
除此之外,我对scheduleAbsolute
而不是scheduleRelative
有更好的体验,因为它不那么令人困惑。
答案 1 :(得分:1)
根据Simon Jentsch的回答,以下是使用scheduleAbsolute
代替scheduleRelative
的答案:
import Rx from "rx/dist/rx.all";
import Username from "./username.component";
import DataItemBuilder from "../../../test/js/utils/c+j-builders";
import usernameService from "./username.service"
describe('Username Component', () => {
let input, username, promiseHelper;
const scheduler = new Rx.TestScheduler(0);
beforeEach(() => {
spyOn(usernameService, 'isAvailable').and.callFake(() => {
return Rx.Observable.just(true);
});
});
beforeEach(() => {
const usernameConfig = DataItemBuilder.withName('foo')
.withPrompt('label').withType('text').build();
const usernameComponent = Username(usernameConfig, scheduler);
usernameComponent.stream.subscribe(value => username = value);
input = usernameComponent.view.find('input');
});
it('should set the username for valid input after debounce', (done) => {
doKeyUpTest('abcddd', '');
scheduler.scheduleAbsolute(null, 100, () => {
expect(usernameService.isAvailable).not.toHaveBeenCalled();
expect(username).toBe('');
});
scheduler.scheduleAbsolute(null, 1000, () => {
expect(usernameService.isAvailable).toHaveBeenCalled();
expect(username).toBe('abcddd');
scheduler.stop();
done();
});
scheduler.start();
});
function doKeyUpTest(inputValue, expectation) {
input.val(inputValue);
input.trigger('input');
expect(username).toBe(expectation);
}
});