Jest + Material-UI:正确模拟useMediaQuery

时间:2019-05-17 06:30:11

标签: jestjs material-ui

我正在其中一个组件中使用Material-UI的useMediaQuery()函数来确定要在组件中的size中使用的<Button>道具。

我正在尝试在玩笑测试中测试它是否按预期工作,但是我当前的实现无法正常工作:

describe("Unit: <Navbar> On xs screens", () => {

  // Incorrectly returns `matches` as `false` ****************************
  window.matchMedia = jest.fn().mockImplementation(
    query => {
      return {
        matches: true,
        media: query,
        onchange: null,
        addListener: jest.fn(),
        removeListener: jest.fn()
      };
    }
  );

  it("renders as snapshot", async () => {
    const width = theme.breakpoints.values.sm - 1;
    const height = Math.round((width * 9) / 16);
    Object.defineProperty(window, "innerWidth", {
      writable: true,
      configurable: true,
      value: width
    });
    const { asFragment } = render(
      <Container backgroundColor={"#ffffff"}>
        <Navbar />
      </Container>
    );
    expect(asFragment()).toMatchSnapshot();
    const screenshot = await generateImage({
      viewport: { width, height }
    });
    expect(screenshot).toMatchImageSnapshot();
  });
});

describe("Unit: <Navbar> On md and up screens", () => {

  // Correctly returns `matches` as `false` ****************************
  window.matchMedia = jest.fn().mockImplementation(
    query => {
      return {
        matches: false,
        media: query,
        onchange: null,
        addListener: jest.fn(),
        removeListener: jest.fn()
      };
    }
  );

  it("renders as snapshot", async () => {
    const width = theme.breakpoints.values.md;
    const height = Math.round((width * 9) / 16);
    Object.defineProperty(window, "innerWidth", {
      writable: true,
      configurable: true,
      value: width
    });
    const { asFragment } = render(
      <Container backgroundColor={"#ffffff"}>
        <Navbar />
      </Container>
    );
    expect(asFragment()).toMatchSnapshot();
    const screenshot = await generateImage({
      viewport: { width, height }
    });
    expect(screenshot).toMatchImageSnapshot();
  });
});

我正在测试的组件(删除了不相关的部分):

const Navbar = () => {
  const theme = useTheme();
  const matchXs = useMediaQuery(theme.breakpoints.down("xs"));
  return (
    <Button size={matchXs ? "medium" : "large"}>
      Login
    </Button>
  );
};
export default Navbar;

尽管我已将其设置为返回true,但它在第一个测试中仍以matches的形式返回false。我知道这是因为它正在生成屏幕截图,并且可以看到在第一次测试中将按钮大小设置为large时,按钮大小设置为medium

它在生产中按预期工作。

如何在玩笑测试中正确获得模拟useMediaQuery()

2 个答案:

答案 0 :(得分:0)

我知道了...

useMediaQuery()需要重新渲染组件才能正常工作,因为第一个渲染将返回您在options.defaultMatches(默认为false)中定义的内容。

此外,该模拟还需要在每个测试(it)范围内,而不是在describe中。

当我使用react-testing-library时,我要做的就是再次重新渲染该组件并更改模拟的范围,并且可以正常工作。

这是工作示例:

const initTest = width => {
  Object.defineProperty(window, "innerWidth", {
    writable: true,
    configurable: true,
    value: width
  });
  window.matchMedia = jest.fn().mockImplementation(
    query => {
      return {
        matches: width >= theme.breakpoints.values.sm ? true : false,
        media: query,
        onchange: null,
        addListener: jest.fn(),
        removeListener: jest.fn()
      };
    }
  );
  const height = Math.round((width * 9) / 16);
  return { width, height };
};

describe("Unit: <Navbar> On xs screens", () => {
  it("renders as snapshot", async () => {
    const { width, height } = initTest(theme.breakpoints.values.sm - 1);
    const { asFragment, rerender} = render(
      <Container backgroundColor={"#ffffff"}>
        <Navbar />
      </Container>
    );
    rerender(
      <Container backgroundColor={"#ffffff"}>
        <Navbar />
      </Container>
    );
    expect(asFragment()).toMatchSnapshot();
    const screenshot = await generateImage({
      viewport: { width, height }
    });
    expect(screenshot).toMatchImageSnapshot();
  });
});

describe("Unit: <Navbar> On md and up screens", () => {
  it("renders as snapshot", async () => {
    const { width, height } = initTest(theme.breakpoints.values.md);
    const { asFragment } = render(
      <Container backgroundColor={"#ffffff"}>
        <Navbar />
      </Container>
    );
    rerender(
      <Container backgroundColor={"#ffffff"}>
        <Navbar />
      </Container>
    );
    expect(asFragment()).toMatchSnapshot();
    const screenshot = await generateImage({
      viewport: { width, height }
    });
    expect(screenshot).toMatchImageSnapshot();
  });
});

答案 1 :(得分:0)

一种推荐的方式是使用css-mediaquery,现在以the MUI docs编写:

import mediaQuery from 'css-mediaquery';

function createMatchMedia(width) {
  return query => ({
    matches: mediaQuery.match(query, { width }),
    addListener: () => {},
    removeListener: () => {},
  });
}

describe('MyTests', () => {
  beforeAll(() => {
    window.matchMedia = createMatchMedia(window.innerWidth);
  });
});