首页 > 解决方案 > 如何测试mobx反应?

问题描述

注意:这不是真正的应用程序,而是问题的提炼版本。

我有一个简单的应用程序假 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.selectedId, 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

标签: typescriptunit-testingmobx

解决方案


无论您如何实现,您都应该能够测试这两种实现。顺便说一句,我认为这应该适用于任何测试,避免“了解”实现,测试需求。

您的实现的问题是列表@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));
  }
}


推荐阅读