Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 有没有一种方法可以正确模拟重新选择选择器进行单元测试?_Javascript_Unit Testing_Jestjs_Reselect - Fatal编程技术网

Javascript 有没有一种方法可以正确模拟重新选择选择器进行单元测试?

Javascript 有没有一种方法可以正确模拟重新选择选择器进行单元测试?,javascript,unit-testing,jestjs,reselect,Javascript,Unit Testing,Jestjs,Reselect,我的项目中有一个非常复杂的选择器结构(一些选择器可能有多达5个嵌套级别),因此其中一些选择器很难通过输入状态进行测试,我想模拟输入选择器。然而,我发现这是不可能的 以下是最简单的示例: // selectors1.js export const baseSelector = createSelector(...); - 我希望在测试套件中包含的内容: beforeEach(() => { jest.spyOn(selectors1, 'baseSelector').mockRetur

我的项目中有一个非常复杂的选择器结构(一些选择器可能有多达5个嵌套级别),因此其中一些选择器很难通过输入状态进行测试,我想模拟输入选择器。然而,我发现这是不可能的

以下是最简单的示例:

// selectors1.js
export const baseSelector = createSelector(...);
-

我希望在测试套件中包含的内容:

beforeEach(() => {
  jest.spyOn(selectors1, 'baseSelector').mockReturnValue('some value');
});

test('My test', () => {
  expect(selectors2.targetSelector()).toEqual('some value');
});
但是,这种方法不起作用,因为
targetSelector
selectors2.js
的初始化过程中获得了对
selectors1.baseSelector
的引用,并且mock被分配给
selectors1.baseSelector

我现在看到了两种可行的解决方案:

  • 使用
    jest.Mock
    模拟整个
    selectors1.js
    模块,但是,如果我需要为某些特定情况更改
    selectors1.baseSelector
    输出,它将无法工作
  • 按如下方式包装每个依赖项选择器:

  • 但我不太喜欢这种方法,原因很明显


    因此,接下来的问题是:是否有机会为单元测试正确模拟重新选择选择器?

    您可以通过模拟整个
    selectors1.js
    模块来实现这一点,但也可以导入内部测试,以便访问模拟的功能

    假设您的
    选择器1.js

    import { createSelector } from 'reselect';
    
    // selector
    const getFoo = state => state.foo;
    
    // reselect function
    export const baseSelector = createSelector(
      [getFoo],
      foo => foo
    );
    
    import { createSelector } from 'reselect';
    import selectors1 from './selectors1';
    
    export const targetSelector = createSelector(
      [selectors1.baseSelector],
      foo => {
        return foo.a;
      }
    );
    
    selectors2.js
    看起来像

    import { createSelector } from 'reselect';
    
    // selector
    const getFoo = state => state.foo;
    
    // reselect function
    export const baseSelector = createSelector(
      [getFoo],
      foo => foo
    );
    
    import { createSelector } from 'reselect';
    import selectors1 from './selectors1';
    
    export const targetSelector = createSelector(
      [selectors1.baseSelector],
      foo => {
        return foo.a;
      }
    );
    
    然后你可以写一些测试,比如

    import { baseSelector } from './selectors1';
    import { targetSelector } from './selectors2';
    
    // This mocking call will be hoisted to the top (before import)
    jest.mock('./selectors1', () => ({
      baseSelector: jest.fn()
    }));
    
    describe('selectors', () => {
      test('foo.a = 1', () => {
        const state = {
          foo: {
            a: 1
          }
        };
        baseSelector.mockReturnValue({ a: 1 });
        expect(targetSelector(state)).toBe(1);
      });
    
      test('foo.a = 2', () => {
        const state = {
          foo: {
            a: 1
          }
        };
        baseSelector.mockReturnValue({ a: 2 });
        expect(targetSelector(state)).toBe(2);
      });
    });
    
    jest.mock
    函数调用将被提升到模块顶部,以模拟
    选择器1.js
    模块
    当您导入
    /
    require
    选择器1.js
    时,您将获得模拟版本的baseSelector,您可以在运行测试之前控制其行为

    问题在于重新选择是基于组合概念的。因此,您可以从许多其他选择器中创建一个选择器。真正需要测试的不是整个选择器,而是完成此任务的最后一个函数。如果没有,测试将彼此重复,就像您有针对selector1的测试,并且selector1在selector2中使用一样,然后自动在selector2测试中测试这两个测试

    为了实现:

    • 少嘲弄
    • 不需要特别模拟组合选择器的结果
    • 无重复测试
    仅测试选择器的结果功能。可通过
    选择器访问它。resultFunc

    例如:

    const selector2 = createSelector(selector1, (data) => ...);
    
    // tests
    
    const actual = selector2.resultFunc([returnOfSelector1Mock]);
    const expected = [what we expect];
    expect(actual).toEqual(expected)
    
    总结
    我们没有测试整个组合,也没有复制相同的断言,也没有模拟特定的选择器输出,而是测试定义选择器的函数,因此createSelector中的最后一个参数可以通过
    resultFunc
    键访问。

    我遇到了同样的问题。最后,我用
    jest.mock
    模拟了
    reselectcreateselector
    ,以在测试时忽略除最后一个参数(您要测试的核心函数)之外的所有参数。总的来说,这种方法对我很有用

    我的问题 我的选择器模块中有一个循环依赖项。我们的代码库太大,我们没有相应的带宽来重构它们

    我为什么使用这种方法? 我们的代码库在选择器中有很多循环依赖项。试图重写和重构它们,使其不具有循环依赖关系,这是太多的工作。因此,我选择模拟
    createSelector
    ,这样我就不必花时间重构

    如果选择器的代码库干净且没有依赖项,请务必使用
    reselect
    resultFunc
    。此处有更多文档:

    我用来模拟createSelector的代码

    // Mock the reselect
    // This mocking call will be hoisted to the top (before import)
    jest.mock('reselect', () => ({
      createSelector: (...params) => params[params.length - 1]
    }));
    
    然后,为了访问创建的选择器,我使用了如下内容

    const myFunc = TargetSelector.IsCurrentPhaseDraft;
    
    整个测试套件代码都在运行

    // Mock the reselect
    // This mocking call will be hoisted to the top (before import)
    jest.mock('reselect', () => ({
      createSelector: (...params) => params[params.length - 1]
    }));
    
    
    import * as TargetSelector from './TicketFormStateSelectors';
    import { FILTER_VALUES } from '../../AppConstant';
    
    describe('TicketFormStateSelectors.IsCurrentPhaseDraft', () => {
      const myFunc = TargetSelector.IsCurrentPhaseDraft;
    
      it('Yes Scenario', () => {
        expect(myFunc(FILTER_VALUES.PHASE_DRAFT)).toEqual(true);
      });
    
      it('No Scenario', () => {
        expect(myFunc(FILTER_VALUES.PHASE_CLOSED)).toEqual(false);
        expect(myFunc('')).toEqual(false);
      });
    });
    

    对于任何试图用Typescript解决这个问题的人来说,这篇文章最终对我起了作用:

    我的问题是,我正在测试一个模块,该模块在创建请求的过程中调用了几个不同的选择器,而我创建的
    redux mock store
    状态在测试执行时对选择器不可见。我最终完全跳过了模拟存储,而是模拟了调用的特定选择器的返回数据

    过程如下:

    • 导入选择器并将其注册为jest函数:
    • 然后使用
      .mockImplementation
      以及
      ts jest\utils
      中的
      mocked
      帮助程序,这样可以包装每个选择器并为每个选择器提供自定义返回数据
    • 如果需要覆盖特定测试中选择器的默认返回值,可以在
      test()
      定义中这样做:

    如果我理解正确,
    selectors2
    正在创建一个新的
    selectors1
    实例,因此它不是您用spy方法模拟的实例,但您希望它是吗?一个选项可能是使用依赖项注入,以便可以使用模拟实例对其进行初始化。另一种可能是模拟selector2的目标选择器以返回模拟的实例1,但出于可伸缩性的原因,您似乎在试图避免这种情况。我觉得我可能没有掌握问题的全部领域,或者可能没有掌握
    createSelector
    正在做什么。您已经接触到使用模块模拟。是的,这正是我想要实现的。谢谢你的帮助!似乎您应该将选择器中输入选择器的数量(而不是集合)作为单个参数传递给
    resultFunc
    。因此,不要使用
    selector2.resultFunc([returnOfSelector1Mock])应该是
    选择器2.resultFunc(返回选择器1锁)嗯,这是一个有趣的解决方案,但是我认为Maciej Sikora的答案是最好的。唯一的t
    
        import { inputsSelector, outputsSelector } from "../store/selectors";         
        import { mockInputsData, mockOutputsData } from "../utils/test-data";
    
        jest.mock("../store/selectors", () => ({
          inputsSelector: jest.fn(),
          outputsSelector: jest.fn(),
        }));
    
        beforeEach(() => {
            mocked(inputsSelector).mockImplementation(() => {
                return mockInputsData;
            });
            mocked(outputsSelector).mockImplementation(() => {
                return mockOutputsData;
            });
        });
    
        test("returns empty list when output data is missing", () => {
            mocked(outputsSelector).mockClear();
            mocked(outputsSelector).mockImplementationOnce(() => {
                return [];
            });
            // ... rest of your test code follows ...
        });