如何在jest

时间:2017-03-23 13:48:52

标签: javascript reactjs unit-testing jestjs

我有一个依赖导出的const变量的文件。此变量设置为true但如果需要,可以手动设置为false,以防止下游服务请求时出现某些行为。

我不确定如何在Jest中模拟const变量,以便我可以更改它以测试truefalse条件的值。

示例:

//constants module
export const ENABLED = true;

//allowThrough module
import { ENABLED } from './constants';

export function allowThrough(data) {
  return (data && ENABLED === true)
}

// jest test
import { allowThrough } from './allowThrough';
import { ENABLED } from './constants';

describe('allowThrough', () => {
  test('success', () => {
    expect(ENABLED).toBE(true);
    expect(allowThrough({value: 1})).toBe(true);
  });

  test('fail, ENABLED === false', () => {
    //how do I override the value of ENABLED here?

    expect(ENABLED).toBe(false) // won't work because enabled is a const
    expect(allowThrough({value: 1})).toBe(true); //fails because ENABLED is still true
  });
});

9 个答案:

答案 0 :(得分:22)

如果将ES6模块语法编译到ES5中,此示例将起作用,因为最后,所有模块导出都属于同一个对象,可以对其进行修改。

import { allowThrough } from './allowThrough';
import { ENABLED } from './constants';
import * as constants from './constants';

describe('allowThrough', () => {
    test('success', () => {
        constants.ENABLED = true;

        expect(ENABLED).toBe(true);
        expect(allowThrough({ value: 1 })).toBe(true);
    });

    test('fail, ENABLED === false', () => {
        constants.ENABLED = false;

        expect(ENABLED).toBe(false);
        expect(allowThrough({ value: 1 })).toBe(false);
    });
});

或者,你可以切换到原始的commonjs require函数,并在jest.mock(...)的帮助下这样做:

const mockTrue = { ENABLED: true };
const mockFalse = { ENABLED: false };

describe('allowThrough', () => {
    beforeEach(() => {
        jest.resetModules();
    });

    test('success', () => {
        jest.mock('./constants', () => mockTrue)
        const { ENABLED } = require('./constants');
        const { allowThrough } = require('./allowThrough');

        expect(ENABLED).toBe(true);
        expect(allowThrough({ value: 1 })).toBe(true);
    });

    test('fail, ENABLED === false', () => {
        jest.mock('./constants', () => mockFalse)
        const { ENABLED } = require('./constants');
        const { allowThrough } = require('./allowThrough');

        expect(ENABLED).toBe(false);
        expect(allowThrough({ value: 1 })).toBe(false);
    });
});

答案 1 :(得分:5)

由于@Luke,我能够根据我的需要扩展他的答案。我有以下要求:

  • 仅嘲笑文件中的某些值-并非全部
  • 仅在单个测试中运行模拟。

事实证明,doMock()mock()相似,但不会被吊起。另外,requireActual()可用于获取原始数据。

我的config.js文件-我只需要模拟其中的一部分

export const SOMETHING = 'blah'
export const OTHER = 'meh'

我的测试文件

// import { someFunc } from  'some/file' // This won't work with doMock - see below
describe('My test', () => {

  test('someFunc() does stuff', async () => {

    // Here I mock the config file which gets imported somewhere deep in my code
    jest.doMock('config.js', () => {

      // Grab original
      const originalModule = jest.requireActual('config')

      // Return original but override some values
      return {
        __esModule: true, // Depends on your setup
        ...originalModule,
        SOMETHING: 'boom!'
      }
    })

    // Because `doMock` doesn't get hoisted we need to import the function after
    const { someFunc } = await import(
      'some/file'
    )

    // Now someFunc will use the original config values but overridden with SOMETHING=boom!
    const res = await someFunc()
  })
})

根据其他测试,您可能还需要在resetModules()beforeAll之类的地方使用afterAll

文档:

答案 2 :(得分:4)

因为我们不能直接覆盖/模拟该值。我们可以使用下面的hack

// foo.js
export const foo = true; // could be expression as well

// spec file
import * as constants from './foo'

Object.defineProperty(constant, 'foo', {value: 1})

对于函数:

Object.defineProperty(store, 'doOneThing', {value: jest.fn()})

答案 3 :(得分:3)

不幸的是,没有一个发布的解决方案对我有用,或者更确切地说,有些解决方案确实起作用,但是抛出了掉毛,TypeScript或编译错误,因此我将发布我的解决方案,该解决方案对我来说都适用,并且符合当前的编码标准:

// constants.ts
// configuration file with defined constant(s)
export const someConstantValue = true;
// module.ts
// this module uses the defined constants
import { someConstantValue } from './constants';

export const someCheck = () => someConstantValue ? 'true' : 'false';
// module.test.ts
// this is the test file for module.ts
import { someCheck } from './module';

const mockSomeConstantValueGetter = jest.fn();
jest.mock('./constants', () => ({
  get someConstantValue() {
    return mockSomeConstantValueGetter();
  },
}));

describe('someCheck', () => {
  it('returns "true" if someConstantValue is true', () => {
    mockSomeConstantValueGetter.mockReturnValue(true);
    expect(someCheck()).toEqual('true');
  });

  it('returns "false" if someConstantValue is false', () => {
    mockSomeConstantValueGetter.mockReturnValue(false);
    expect(someCheck()).toEqual('false');
  });
});

答案 4 :(得分:2)

由于使用了getter和spyOn,在ES6 +和jest 22.1.0+中还有另一种方法。

默认情况下,您无法监视布尔值或数字之类的原始类型。您可以用自己的模拟代替导入的文件。 getter方法仍然像原始成员一样工作,但允许我们对其进行监视。监视我们的目标成员,基本上可以随便使用它,就像使用jest.fn()模拟一样。

下面是一个例子

// foo.js
export const foo = true; // could be expression as well
// subject.js
import { foo } from './foo'

export default () => foo
// subject.spec.js
import subject from './subject'

jest.mock('./foo', () => ({
  get foo () {
    return true // set some default value
  }
}))

describe('subject', () => {
  const mySpy = jest.spyOn(subject.default, 'foo', 'get')

  it('foo returns true', () => {
    expect(subject.foo).toBe(true)
  })

  it('foo returns false', () => {
    mySpy.mockReturnValueOnce(false)
    expect(subject.foo).toBe(false)
  })
})

Read more in the docs.

答案 5 :(得分:1)

面对同一问题,我发现此博客文章非常有用,并且比@cyberwombat用例简单得多:

https://remarkablemark.org/blog/2018/06/28/jest-mock-default-named-export/

// esModule.js
export default 'defaultExport';
export const namedExport = () => {};
// esModule.test.js
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
  namedExport: jest.fn(),
}));

import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function

答案 6 :(得分:0)

您还可以使用"Object.defineProperty"

来重新定义您的财产,而不是开玩笑并且没有吊装等麻烦。

可以轻松地为每个测试用例重新定义它。

这是一个基于我拥有的某些文件的伪代码示例:

来自本地化文件:

export const locale = 'en-US';

在另一个文件中,我们使用的是语言环境:

import { locale } from 'src/common/localization';
import { format } from 'someDateLibrary';

// 'MMM' will be formatted based on locale
const dateFormat = 'dd-MMM-yyyy';

export const formatDate = (date: Number) => format(date, dateFormat, locale)

如何在测试文件中进行模拟

import * as Localization from 'src/common/localization';
import { formatDate } from 'src/utils/dateUtils';

describe('format date', () => {
        test('should be in Danish format', () => {
            Object.defineProperty(Localization, 'locale', {
                value: 'da-DK'
            });
            expect(formatDate(1589500800000)).toEqual('15-maj-2020');
        });
        test('should be in US format', () => {
            Object.defineProperty(Localization, 'locale', {
                value: 'en-US'
            });
            expect(formatDate(1589500800000)).toEqual('15-May-2020');
        });
});

答案 7 :(得分:0)

我最需要的场景是模拟一个类使用的常量(在我的情况下,是一个React组件,但实际上可以是任何ES6类)。

@Luke的答案对此非常有效,花了我一分钟的时间将它包裹起来,所以我想将其改写为更明确的示例。

关键是您的常量需要保存在import的单独文件中,这样import本身就可以被jest插入/模拟。

以下内容对我来说很完美。

首先,定义常量:

// src/my-component/constants.js

const MY_CONSTANT = 100;

export { MY_CONSTANT };

接下来,我们有实际使用常量的类:

// src/my-component/index.jsx

import { MY_CONSTANT } from './constants';

// This could be any class (e.g. a React component)
class MyComponent {
  constructor() {
    // Use the constant inside this class
    this.secret = MY_CONSTANT;
    console.log(`Current value is ${this.secret}`);
  }
}

export default MyComponent

最后,我们进行了测试。我们要在这里处理2个用例:

  1. 模拟此文件中所有测试的MY_CONSTANT生成值
  2. 允许特定测试的功能进一步覆盖该单个测试的MY_CONSTANT的值

使用测试文件顶部的jest.mock可以实现第一部分。

通过使用jest.spyOn进一步监视导出的常量列表,可以实现第二个。几乎就像是模拟之上的模拟。

// test/components/my-component/index.js

import MyComponent from 'src/my-component';
import allConstants from 'src/my-component/constants';

jest.mock('src/my-component/constants', () => ({
  get MY_CONSTANT () {
    return 30;
  }
}));

it('mocks the value of MY_CONSTANT', () => {
  // Initialize the component, or in the case of React, render the component
  new MyComponent();

  // The above should cause the `console.log` line to print out the 
  // new mocked value of 30
});

it('mocks the value of MY_CONSTANT for this test,', () => {
  // Set up the spy. You can then use any jest mocking method
  // (e.g. `mockReturnValue()`) on it
  const mySpy = jest.spyOn(allConstants, 'MY_CONSTANT', 'get')
  mySpy.mockReturnValue(15);

  new MyComponent();

  // The above should cause the `console.log` line to print out the 
  // new mocked value of 30
});

答案 8 :(得分:-1)

我通过在减速器中初始化ContstantsFile.js中的常量来解决此问题。并将其放在redux存储中。由于jest.mock无法模拟contstantsFile.js

constantsFile.js
-----------------
const MY_CONSTANTS = {
MY_CONSTANT1: "TEST",
MY_CONSTANT2: "BEST",
};
export defualt MY_CONSTANTS;

reducers/index.js
-----------------
import MY_CONST from "./constantsFile";

const initialState = {
...MY_CONST
}
export const AbcReducer = (state = initialState, action) => {.....}

ABC.jsx
------------
import { useSelector } from 'react-redux';
const ABC = () => {
const const1 = useSelector(state) => state. AbcReducer. MY_CONSTANT1:
const const2 = useSelector(state) => state. AbcReducer. MY_CONSTANT2:
.......

现在,我们可以轻松地在test.jsx中模拟存储并将值提供给所需的常量。

Abc.text.jsx
-------------
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';

describe('Abc mock constants in jest', () => {
const mockStore = configureMockStore([thunk]);
let store = mockStore({
   AbcReducer: {
      MY_CONSTANT1 ="MOCKTEST",
      MY_CONSTANT2 = "MOCKBEST",
   }
});

test('your test here', () => { .....

现在,当测试运行时,它将始终从模拟存储中选择常量值。