You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用React Testing Library测试Mobx+useContext侧边导航?

解决React + Mobx-lite测试中注入自定义Store的问题

我刚看完你的问题,核心问题在于测试时没有正确把自定义Store传递给组件——因为你的Navbar是通过useContext获取Store的,而默认情况下它会使用你在createContext(new Store())里定义的默认Store(toggle默认是false,所以Drawer处于关闭状态,里面的列表自然不会渲染,导致找不到文本)。下面一步步给你解决方法,再聊聊最佳实践:

第一步:修复测试代码,用Context Provider注入测试Store

你不需要用旧的Provider/Inject(那确实是mobx-react里的旧API),直接用React原生的Context.Provider就可以,完全兼容mobx-react-lite。修改你的测试代码如下:

import { render, cleanup } from '@testing-library/react';
import Navbar from './Navbar';
import StoreContext from './path/to/your/store/file'; // 导入你的Context

describe('Navbar Interaction', () => {
  describe('Inspecting Navbar Contents', () => {
    beforeEach(cleanup);

    // 定义测试专用的Store,结构和真实Store保持一致
    class TestStore {
      toggle = true; // 让Drawer默认打开,这样列表才会渲染
      setToggle(bool) { this.toggle = bool; }
    }
    // 装饰测试Store,确保observable和action生效
    const DecoratedTestStore = decorate(TestStore, {
      toggle: observable,
      setToggle: action
    });

    // 封装带Store的渲染函数,用Provider包裹组件
    const renderWithStore = (store) => {
      return render(
        <StoreContext.Provider value={store}>
          <Navbar />
        </StoreContext.Provider>
      );
    };

    it('Expect links are present', () => {
      const testStore = new DecoratedTestStore();
      const { getByText } = renderWithStore(testStore);
      // 推荐用react-testing-library的toBeInTheDocument,比toBeTruthy更精准
      expect(getByText("My Dashboard")).toBeInTheDocument();
    });
  });
});

关键修改点:

  1. StoreContext.Provider包裹Navbar,把测试Store作为value传入,这样组件里的useContext就会拿到这个测试Store,而不是默认的Store。
  2. 测试Store的结构要和真实Store对齐(比如用toggle而不是sideNavToggle),避免组件里取错属性。
  3. 给测试Store加上正确的装饰(observableaction),确保Mobx的响应式逻辑正常工作。

第二步:React + Mobx的最佳实践

针对你的代码,还有几个可以优化的点,让项目结构更清晰、更易维护:

1. 不要在createContext时直接实例化Store

原来的写法:

export default createContext(new Store());

改成:

export const StoreContext = createContext(null); // 先不传默认值
export const store = new Store(); // 单独实例化Store

然后在App.js里用Provider包裹整个应用:

import { StoreContext, store } from './path/to/store';

export default () => {
  return (
    <StoreContext.Provider value={store}>
      <div className="App">
        <Navbar />
      </div>
    </StoreContext.Provider>
  );
};

这样做的好处:

  • 测试时可以轻松替换Store,不需要依赖默认值。
  • 后续如果需要动态创建Store(比如用户登录后初始化不同的Store),灵活性更高。

2. 拆分Store,避免单一Store过大

如果你的应用规模变大,不要把所有状态都放在一个Store里。可以拆分成多个专注的Store(比如NavStoreUserStoreDashboardStore),然后在顶层组合后通过Provider传递,或者分别用不同的Context传递。

3. 严格用Action修改Observable状态

你已经做到了(setToggle被标记为action),这点非常重要——所有修改Mobx observable的操作都要放在action里,确保状态变更被正确追踪,避免不可预测的bug。

4. 测试时按需Mock Store方法

如果你的Store里有异步操作(比如调用API),测试时可以Mock这些方法,只测试组件的UI行为。比如:

const testStore = new DecoratedTestStore();
testStore.setToggle = jest.fn(); // Mock setToggle方法
// 然后在测试里验证它是否被调用
expect(testStore.setToggle).toHaveBeenCalledWith(false);

内容的提问来源于stack exchange,提问作者medev21

火山引擎 最新活动