Jest 的模拟常量不起作用(同一测试文件中的多个模拟)

IT技术 reactjs jestjs mocking react-testing-library
2021-05-20 18:42:26

我遇到的问题是模拟常量在jest.doMock.

看看最小的 repo

我试过用mock而不是doMock- 同样的错误。

应用程序.test.js

import React from "react"
import App from './App'
import '@testing-library/jest-dom'
import { render } from "@testing-library/react";

describe('testing app.js', () => {

  // To reset manually mocked values
  beforeEach(() => {
    jest.resetModules()
  });

  test("SET CONSTANT TO 1", () => {
    jest.doMock('./myConstants.js', () => ({
      CONSTANT: {
        NUMBER: 1
      }
    }))
    const { getByText, getByLabelText } = render(<App />)
    expect(getByText('1')).toBeInTheDocument()

  })

  test("SET CONSTANT TO 3", () => {
    jest.doMock('./myConstants.js', () => ({
      CONSTANT: {
        NUMBER: 3
      }
    }))
    const { getByText, getByLabelText } = render(<App />)
    expect(getByText('3')).toBeInTheDocument()

  })
})

应用程序.js

import React from "react"
import { CONSTANT } from './myConstants.js'

console.log(CONSTANT)

const App = () => {
  return (
    <div>
      {CONSTANT.NUMBER}
    </div>
  );
}
export default App;

myConstants.js

export const CONSTANT = { NUMBER: 2 }

上面的两个测试都失败了。其中之一的输出是:

TestingLibraryElementError: Unable to find an element with the text: 3. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

    <body>
      <div>
        <div>
          2
        </div>
      </div>
    </body>

      29 |     }))
      30 |     const { getByText, getByLabelText } = render(<App />)
    > 31 |     expect(getByText('3')).toBeInTheDocument()

扩展解决方案

尽管提供的解决方案运行良好,但我不想重写我要测试的每个组件(通过添加require(...))。一种解决方法是使用import("./App").then((module)

import React from "react"
import App from './App'
import '@testing-library/jest-dom'
import { render } from "@testing-library/react";

describe('testing app.js', () => {

  // To reset manually mocked values
  beforeEach(() => {
    jest.resetModules()
  });

  jest.doMock('./myConstants.js', () => {
    return {
      __esModule: true,
      CONSTANT: {
        NUMBER: 1
      }
    }
  })

  test("SET CONSTANT TO 1", () => {


    // Wait for mock done
    return import('./myConstants.js').then((constants) => {
      console.log(constants.CONSTANT.NUMBER)
      expect(constants.CONSTANT.NUMBER).toBe(1)
      import("./App").then((module) => {
        const { getByText, getByLabelText } = render(<module.default />)
        expect(getByText('1')).toBeInTheDocument()
      })
    })
  })
})
2个回答

使用jest.doMock比你想象的要复杂。基本上它需要您按照https://jestjs.io/docs/en/jest-object#jestdomockmodulename-factory-options 的描述执行几个步骤

这意味着您必须更改测试以满足上述要求:

test("SET CONSTANT TO 1", () => {
  jest.doMock('./myConstants.js', () => {
    return {
      __esModule: true,
      CONSTANT: {
        NUMBER: 1
      }
    }
  })
  
  // Wait for mock done
  return import('./myConstants.js').then(() => {
    const { getByText, getByLabelText } = render(<App />)
    expect(getByText('1')).toBeInTheDocument()  
  })
})

// While you also require your code right before using instead of `require` at the top level:

const App = () => {
  // Require to use it right before using it
  const { CONSTANT } = require('./myConstants.js');

  return (
    <div>
      {CONSTANT.NUMBER}
    </div>
  );
}

我猜我们还可以通过仍然使用jest.mock以下方法直接改变模拟值

import React from "react"
import App from './App'
import '@testing-library/jest-dom'
import { render } from "@testing-library/react";
import { CONSTANT} from "./myConstants";

jest.mock('./myConstants.js', () => ({
  CONSTANT: {
    NUMBER: 0,
  }
}))

describe('testing app.js', () => {

  // To reset manually mocked values
  beforeEach(() => {
    jest.resetModules()
  });

  test("SET CONSTANT TO 1", () => {
    // Mutate directly mocked value by setting our desired value
    CONSTANT.NUMBER = 1;
    const { getByText, getByLabelText } = render(<App />)
    expect(getByText('1')).toBeInTheDocument()
  })

  test("SET CONSTANT TO 3", () => {
    // Likewise we set it as 3
    CONSTANT.NUMBER = 3;
    const { getByText, getByLabelText } = render(<App />)
    expect(getByText('4')).toBeInTheDocument()
  })
})