给定以下课程:
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
export class ObjectStateContainer<T> {
private currentStateSubject = new BehaviorSubject<T>(undefined);
private $currentState = this.currentStateSubject.asObservable();
public $isDirty = this.$currentState.pipe(map(t => t !== this.t));
constructor(public t: T) {
this.update(t);
}
undoChanges() {
this.currentStateSubject.next(this.t);
}
update(t: T) {
this.currentStateSubject.next(t);
}
}
我想编写一些测试来验证$isDirty
包含执行各种函数调用后我期望的值。具体来说,我想测试创建一个变量,更新它,然后撤消更改并验证$isDirty
每个阶段的值。目前,我已经看到了两种测试 observables 的方法,但我不知道如何用它们中的任何一种来做这个测试。我希望测试执行以下操作:
- 创建一个新的
ObjectStateContainer
.- 断言这
$isDirty
是错误的。
- 断言这
- 在 上调用更新
ObjectStateContainer
。- 断言这
$isDirty
是真的。
- 断言这
- 在
ObjectStateContainer
.- 断言这
$isDirty
是错误的。
- 断言这
import { ObjectStateContainer } from './object-state-container';
import { TestScheduler } from 'rxjs/testing';
class TestObject {
}
describe('ObjectStateContainer', () => {
let scheduler: TestScheduler;
beforeEach(() =>
scheduler = new TestScheduler((actual, expected) =>
{
expect(actual).toEqual(expected);
})
);
/*
SAME TEST AS ONE BELOW
This is a non-marble test example.
*/
it('should be constructed with isDirty as false', done => {
const objectStateContainer = new ObjectStateContainer(new TestObject());
objectStateContainer.update(new TestObject());
objectStateContainer.undoChanges();
/*
- If done isn't called then the test method will finish immediately without waiting for the asserts inside the subscribe.
- Using done though, it gets called after the first value in the stream and doesn't wait for the other two values to be emitted.
- Also, since the subscribe is being done here after update and undoChanges, the two previous values will already be gone from the stream. The BehaviorSubject backing the observable will retain the last value emitted to the stream which would be false here.
I can't figure out how to test the whole chain of emitted values.
*/
objectStateContainer
.$isDirty
.subscribe(isDirty => {
expect(isDirty).toBe(false);
expect(isDirty).toBe(true);
expect(isDirty).toBe(false);
done();
});
});
/*
SAME TEST AS ONE ABOVE
This is a 'marble' test example.
*/
it('should be constructed with isDirty as false', () => {
scheduler.run(({ expectObservable }) => {
const objectStateContainer = new ObjectStateContainer(new TestObject());
objectStateContainer.update(new TestObject());
objectStateContainer.undoChanges();
/*
- This will fail with some error message about expected length was 3 but got a length of one. This seemingly is happening because the only value emitted after the 'subscribe' being performed by the framework is the one that gets replayed from the BehaviorSubject which would be the one from undoChanges. The other two have already been discarded.
- Since the subscribe is being done here after update and undoChanges, the two previous values will already be gone from the stream. The BehaviorSubject backing the observable will retain the last value emitted to the stream which would be false here.
I can't figure out how to test the whole chain of emitted values.
*/
const expectedMarble = 'abc';
const expectedIsDirty = { a: false, b: true, c: false };
expectObservable(objectStateContainer.$isDirty).toBe(expectedMarble, expectedIsDirty);
});
});
});