注意:这不是真正的应用,而是问题的简化版本。
我有一个简单的伪造的Todo应用程序,它使用@computed
来获取TodoItemComputed.isSelected
的值:
import { computed, observable, action } from 'mobx';
export class TodoItemComputed {
readonly id: string;
readonly list: TodoListComputed;
constructor(id: string, list: TodoListComputed) {
this.id = id;
this.list = list;
}
// ** This is the attribute we are interested in and we want to test!
@computed
get isSelected() {
return this.id === this.list.selectedId;
}
}
export class TodoListComputed {
@observable.shallow
private serverData: string[] = [];
@observable
selectedId = '';
@action
setServerData(data: string[]) {
this.serverData = data;
}
@action
setSelected(id: string) {
this.selectedId = id;
}
@computed
get todoItems() {
return this.serverData.map(d => new TodoItemComputed(d, this));
}
}
测试TodoItemComputed.isSelected
我可以做这样的事情:
import { TodoListComputed } from '../TodoExample';
// Using jesthere, but the test framework should not matter...
describe('isSelected', function() {
it('should have no Item selected initially', function() {
const todoList = new TodoListComputed();
todoList.setServerData(['foo', 'bar']);
expect(todoList.todoItems[0].isSelected).toBe(false);
expect(todoList.todoItems[1].isSelected).toBe(false);
});
it('should select the correct item', function() {
const todoList = new TodoListComputed();
todoList.setServerData(['foo', 'bar']);
todoList.setSelected('bar');
expect(todoList.todoItems[0].isSelected).toBe(false);
expect(todoList.todoItems[1].isSelected).toBe(true);
});
});
由于某些原因,我必须重构我的应用,并且我使用反应而不是计算(例如,因为我必须显示10,000个项目,并且选择迅速改变,并且调用TodoListComputed.setSelected
导致重新运行所有计算)所有项目)。
因此,我将其更改为使用reaction
(看来,当我使用autorun
时,可以使用与computed
版本相同的测试):
import { autorun, computed, observable, action } from 'mobx';
export class TodoItemReaction {
readonly id: string;
@observable
isSelected = false;
constructor(id: string) {
this.id = id;
}
@action
setSelected(isSelected: boolean) {
this.isSelected = isSelected;
}
}
export class TodoListReaction {
@observable.shallow
private serverData: string[] = [];
@observable
selectedId = '';
constructor() {
reaction(
() => [this.setSelected, this.todoItems],
() => {
this.todoItems.forEach(t => {
t.setSelected(t.id === this.selectedId);
});
}
);
}
@action
setServerData(data: string[]) {
this.serverData = data;
}
@action
setSelected(id: string) {
this.selectedId = id;
}
@computed
get todoItems() {
return this.serverData.map(d => new TodoItemReaction(d));
}
}
问题:如何测试autorun
版本?
奖励问题:如何测试是否使用autorun
或computed
的代码?
答案 0 :(得分:0)
无论您如何实施,您都应该能够测试这两种实施方式。顺便说一句,我认为这应该适用于任何测试,避免“了解”实现,测试需求。
当您使用 @computed
在反应中设置项目的 todoItems
时,您的实现问题在于 isSelected
列表中 setSelected
的使用。这将导致重新计算 @computed
todoItems
。
这是因为您在初始化类时将 isSelected
设置为 false
。
换句话说,@computed
todoItems
正在“聆听”每个项目的 @observable
isSelected
的变化。
这是一个非常简单且快速的实现(没有 reaction
):
export class TodoItem {
readonly id: string;
@observable isSelected = false;
constructor(id: string) {
this.id = id;
}
@action
setSelected(isSelected: boolean) {
this.isSelected = isSelected;
}
}
export class TodoList {
@observable.shallow
todoItems: TodoItem[] = [];
@action
setServerData(data: string[]) {
this.todoItems = data.map((d) => new TodoItem(d));
}
@action
setSelected(id: string) {
this.todoItems.forEach((t) => t.setSelected(t.id === id));
}
}