测试rootReducer中是否已经导入了所有reducer

Test whether all reducers have been imported in the rootReducer

我有一个 rootReducer,它像这样导入所有单独的 reducer:

import { combineReducers } from 'redux';
import departments from './departments';
import indicators from './indicators';
import versions from './versions';
import projects from './projects';

// combine all reducers to a single reducer, state will be linked to a reducer via the key used here
const rootReducer = combineReducers({
  versions,
  departments,
  indicators,
  projects,
});

export default rootReducer;

因为这个 reducer 导入所有东西很重要,我认为测试是否导入了 ./src/reducers 中定义的所有 reducer 是有意义的。我能想到的唯一方法是使用 fs 检查 ./src/reducers 中的文件数(没有 indexrootReducer)并检查 rootReducer 包含那么多减速器。

似乎是一个丑陋的测试,也有点脆弱。但是,当您忘记包含 reducer 时,收到测试失败的通知还是很不错的。测试是否已导入所有减速器的最佳方法是什么?

您建议的方法 可能 有效,但我同意这样的方法很脆弱,甚至可能无法测试您所关心的行为。

退后一步,看看您发布的代码的行为。如果你把它抽象成英文,它可能看起来像这样:

  1. 导入一个名为 combineReducers
  2. 的函数
  3. 导入我想要包含的每个减速器
  4. 调用 combineReducers,并传递一个包含我想要包含的每个 reducer 的对象
  5. 导出调用结果combineReducers

看来您只关心#3,这取决于#1 和#2 是否正确。如果你想测试 combineReducers 是用你期望的参数调用的,为什么不存根这个函数并断言那个行为呢?

您可以使用 SinonJS 来 stub combineReducers,并且在您 require() 您的根减速器之后,您可以断言 combineReducers 被调用了一次,并且它是使用包含您期望的键和值的对象调用。这是我可能会做的事情(使用 mocha 测试框架,chai 用于断言):

import * as Redux from 'redux';
import sinon from 'sinon';
import chai, {expect} from 'chai';
import sinonChai from 'sinon-chai';

import departments from './departments';
import indicators from './indicators';
import versions from './versions';
import projects from './projects';

chai.use(sinonChai);

describe('Root Reducer', () => {
    before(() => {
        sinon.stub(Redux, 'combineReducers');
        require('./path/to/your/root/reducer');
    });

    after(() => {
        Redux.combineReducers.restore();
    });

    it('should use all of the created reducers', () => {
        const expectedReducers = {
            versions,
            departments,
            indicators,
            projects
        };

        expect(Redux.combineReducers).to.have.callCount(1);
        expect(Redux.combineReducers).to.be.calledWith(expectedReducers);
    });
});

与您建议的方法相比,这种方法的好处是它不依赖于文件夹中存在的任意数量的文件,并且负责保持测试中预期值的变量就是全部如果要添加新减速器或删除旧减速器,则需要进行修改。

我听说你来自哪里,但我认为你不想在测试中处理这个问题。随着应用程序的增长,您可能需要更深的 reducer 树。例如。您的 projects 减速器可能由多个子减速器组成,您必须管理需要从何处导入哪些减速器。文件系统测试会越来越脆弱。通常,导入和文件结构是项目的实现细节,您希望测试项目的行为。

但是想要在连接减速器失败时立即得到反馈是完全有道理的。我会尝试在构建级别处理它。假设您正在使用 Webpack,也许看看 this plugin 是否可以解决您的问题。它检测未使用的出口。将它添加到您的插件

new UnusedFilesWebpackPlugin({ pattern: 'src/reducers/**' })

当您有一个未被导入的文件时,您的构建将警告您。由于它适用于模块,因此您不必担心目录结构(除了确保模式匹配您要检查的所有文件之外)。