在 Enzyme 中测试延迟加载的组件
Test lazy loaded components in Enzyme
给定一个包含多个延迟加载路由的简单应用程序,
import React, { lazy, Suspense } from "react";
import { Route } from "react-router-dom";
import "./styles.css";
const Component = lazy(() => import("./Component"));
const PageNotFound = lazy(() => import("./PageNotFound"));
export default function App() {
return (
<div className="App">
<Route
path="/component"
exact
render={() => (
<Suspense fallback={<div>Loading..</div>}>
<Component />
</Suspense>
)}
/>
<Route
path="*"
render={() => (
<Suspense fallback={<div>Loading..</div>}>
<PageNotFound />
</Suspense>
)}
/>
</div>
);
}
如何进行测试以检查这些组件是否在该特定路由上呈现?
这是我试过的App.test:
import { configure, shallow, mount } from "enzyme";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import React from "react";
import { MemoryRouter } from "react-router-dom";
import App from "./App";
import Component from "./Component";
import PageNotFound from "./PageNotFound";
configure({ adapter: new Adapter() });
describe("App", () => {
it("renders without crashing", () => {
shallow(<App />);
});
it("renders lazy loaded PageNotFound route", () => {
// Act
const wrapper = mount(
<MemoryRouter initialEntries={["/random"]}>
<App />
</MemoryRouter>
);
// Assert
// expect(wrapper.containsMatchingElement(<PageNotFound />)).toEqual(true);
// expect(wrapper.find(PageNotFound)).toHaveLength(1);
expect(wrapper.exists(PageNotFound)).toEqual(true);
});
});
由于 Suspense,所有 3 个断言似乎都不起作用;可在 codesandbox here 找到工作片段 - 确保继续 'tests' 选项卡以查看失败的测试。
非常感谢任何建议,提前致谢!
这是一个有趣的问题,很难找到最好的模拟方法,因为 lazy(() => import('path/to/file'))
将函数作为参数,因此我们无法检测匿名函数的值。
但我想我有适合您的解决方案,但最好不是测试所有情况,而是测试特定情况。你会嘲笑如下:
jest.mock('react', () => {
const React = jest.requireActual('react');
// Always render children as our lazy mock component
const Suspense = ({ children }) => {
return children;
};
const lazy = () => {
// `require` component directly as we want to see
// Why? Above reason
return require('./PageNotFound').default;
}
return {
...React,
lazy,
Suspense
};
});
更新模拟 lazy
函数的新方法
我想我有一个更好的主意来调用 lazy
参数然后 return 作为组件如下:
jest.mock('react', () => {
const React = jest.requireActual('react');
const Suspense = ({ children }) => {
return children;
};
const lazy = jest.fn().mockImplementation((fn) => {
const Component = (props) => {
const [C, setC] = React.useState();
React.useEffect(() => {
fn().then(v => {
setC(v)
});
}, []);
return C ? <C.default {...props} /> : null;
}
return Component;
})
return {
...React,
lazy,
Suspense
};
});
然后你必须等待组件更新returned in mock lazy
所以我们等待组件重新绘制如下:
// keep warning `act` removed
import { act } from 'react-dom/test-utils';
// A helper to update wrapper
const waitForComponentToPaint = async (wrapper) => {
await act(async () => {
await new Promise(resolve => setTimeout(resolve));
wrapper.update();
});
};
it("renders PageNotFound", async () => {
const wrapper = mount(
<MemoryRouter initialEntries={["/random"]}>
<App />
</MemoryRouter>
);
await waitForComponentToPaint(wrapper);
expect(wrapper.exists(PageNotFound)).toEqual(true);
});
it("renders Component", async () => {
const wrapper = mount(
<MemoryRouter initialEntries={["/component"]}>
<App />
</MemoryRouter>
);
await waitForComponentToPaint(wrapper);
expect(wrapper.exists(Component)).toEqual(true);
});
link
的另一个更新
我创建了一个 repl.it
link 供您检查其工作原理:https://repl.it/@tmhao2005/js-cra
您可以运行 测试:yarn test -- lazy
。并浏览 src/Lazy
.
下的代码
以下是我的工作版本:
import { act, } from 'react-dom/test-utils';
const waitForComponentToPaint = async (wrapper) => {
await act(async () => {
await new Promise((resolve) => setTimeout(resolve));
wrapper.update();
});
};
jest.mock('react', () => {
const ReactActual = jest.requireActual('react');
// Always render children as our lazy mock component
const Suspense = ({
children,
}) => children;
const lazyImport = jest.fn().mockImplementation(() => {
class SpyComponent extends ReactActual.Component {
componentDidMount() {}
render() {
const {
path,
} = this.props;
const LazyComponent = require(path).default;
return (
<>
{LazyComponent ? <LazyComponent {...this.props} /> : null}
</>
);
}
}
return SpyComponent;
});
return {
...ReactActual,
lazy: lazyImport,
Suspense,
};
});
describe('Render <Header />', () => {
it('should render a Header', async () => {
const wrapper = mount(
<Header />
);
await waitForComponentToPaint(wrapper);
expect(wrapper.find('XXXXXX')).to.have.length(1);
});
});
并且我在调用惰性组件时添加了一个 path
props :
<CustomLazyComponent
path="./CustomLazyComponent"
/>
给定一个包含多个延迟加载路由的简单应用程序,
import React, { lazy, Suspense } from "react";
import { Route } from "react-router-dom";
import "./styles.css";
const Component = lazy(() => import("./Component"));
const PageNotFound = lazy(() => import("./PageNotFound"));
export default function App() {
return (
<div className="App">
<Route
path="/component"
exact
render={() => (
<Suspense fallback={<div>Loading..</div>}>
<Component />
</Suspense>
)}
/>
<Route
path="*"
render={() => (
<Suspense fallback={<div>Loading..</div>}>
<PageNotFound />
</Suspense>
)}
/>
</div>
);
}
如何进行测试以检查这些组件是否在该特定路由上呈现?
这是我试过的App.test:
import { configure, shallow, mount } from "enzyme";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import React from "react";
import { MemoryRouter } from "react-router-dom";
import App from "./App";
import Component from "./Component";
import PageNotFound from "./PageNotFound";
configure({ adapter: new Adapter() });
describe("App", () => {
it("renders without crashing", () => {
shallow(<App />);
});
it("renders lazy loaded PageNotFound route", () => {
// Act
const wrapper = mount(
<MemoryRouter initialEntries={["/random"]}>
<App />
</MemoryRouter>
);
// Assert
// expect(wrapper.containsMatchingElement(<PageNotFound />)).toEqual(true);
// expect(wrapper.find(PageNotFound)).toHaveLength(1);
expect(wrapper.exists(PageNotFound)).toEqual(true);
});
});
由于 Suspense,所有 3 个断言似乎都不起作用;可在 codesandbox here 找到工作片段 - 确保继续 'tests' 选项卡以查看失败的测试。
非常感谢任何建议,提前致谢!
这是一个有趣的问题,很难找到最好的模拟方法,因为 lazy(() => import('path/to/file'))
将函数作为参数,因此我们无法检测匿名函数的值。
但我想我有适合您的解决方案,但最好不是测试所有情况,而是测试特定情况。你会嘲笑如下:
jest.mock('react', () => {
const React = jest.requireActual('react');
// Always render children as our lazy mock component
const Suspense = ({ children }) => {
return children;
};
const lazy = () => {
// `require` component directly as we want to see
// Why? Above reason
return require('./PageNotFound').default;
}
return {
...React,
lazy,
Suspense
};
});
更新模拟 lazy
函数的新方法
我想我有一个更好的主意来调用 lazy
参数然后 return 作为组件如下:
jest.mock('react', () => {
const React = jest.requireActual('react');
const Suspense = ({ children }) => {
return children;
};
const lazy = jest.fn().mockImplementation((fn) => {
const Component = (props) => {
const [C, setC] = React.useState();
React.useEffect(() => {
fn().then(v => {
setC(v)
});
}, []);
return C ? <C.default {...props} /> : null;
}
return Component;
})
return {
...React,
lazy,
Suspense
};
});
然后你必须等待组件更新returned in mock lazy
所以我们等待组件重新绘制如下:
// keep warning `act` removed
import { act } from 'react-dom/test-utils';
// A helper to update wrapper
const waitForComponentToPaint = async (wrapper) => {
await act(async () => {
await new Promise(resolve => setTimeout(resolve));
wrapper.update();
});
};
it("renders PageNotFound", async () => {
const wrapper = mount(
<MemoryRouter initialEntries={["/random"]}>
<App />
</MemoryRouter>
);
await waitForComponentToPaint(wrapper);
expect(wrapper.exists(PageNotFound)).toEqual(true);
});
it("renders Component", async () => {
const wrapper = mount(
<MemoryRouter initialEntries={["/component"]}>
<App />
</MemoryRouter>
);
await waitForComponentToPaint(wrapper);
expect(wrapper.exists(Component)).toEqual(true);
});
link
的另一个更新我创建了一个 repl.it
link 供您检查其工作原理:https://repl.it/@tmhao2005/js-cra
您可以运行 测试:yarn test -- lazy
。并浏览 src/Lazy
.
以下是我的工作版本:
import { act, } from 'react-dom/test-utils';
const waitForComponentToPaint = async (wrapper) => {
await act(async () => {
await new Promise((resolve) => setTimeout(resolve));
wrapper.update();
});
};
jest.mock('react', () => {
const ReactActual = jest.requireActual('react');
// Always render children as our lazy mock component
const Suspense = ({
children,
}) => children;
const lazyImport = jest.fn().mockImplementation(() => {
class SpyComponent extends ReactActual.Component {
componentDidMount() {}
render() {
const {
path,
} = this.props;
const LazyComponent = require(path).default;
return (
<>
{LazyComponent ? <LazyComponent {...this.props} /> : null}
</>
);
}
}
return SpyComponent;
});
return {
...ReactActual,
lazy: lazyImport,
Suspense,
};
});
describe('Render <Header />', () => {
it('should render a Header', async () => {
const wrapper = mount(
<Header />
);
await waitForComponentToPaint(wrapper);
expect(wrapper.find('XXXXXX')).to.have.length(1);
});
});
并且我在调用惰性组件时添加了一个 path
props :
<CustomLazyComponent
path="./CustomLazyComponent"
/>