我(为了演示目的)是一个非常简单的React组件:
import React, { Component } from 'react';
import { fetchUser, fetchNews } from '../../infrastructure/actions';
class Layout extends Component {
render() {
return (
<div />
);
}
}
export default Layout;
和一个简单的Jest快照测试:
import React from 'react';
import { shallow } from 'enzyme';
import Layout from '../Layout';
describe('rendering', () => {
it('should render valid snapshot when loading', () => {
const jsx = (<Layout />);
const element = shallow(jsx);
expect(element).toMatchSnapshot();
});
});
这里的相关行是
import { fetchUser, fetchNews } from '../../infrastructure/actions';
infrastructure/actions/index.js
是一个充满Redux操作的桶文件,因此:
export { fetchNews, FETCH_NEWS } from './news/fetchNews';
export { fetchUser, FETCH_USER} from './user/fetchUser';
// ...etc
我的问题是,即使在浅层渲染组件中没有使用import语句中的任何内容,Jest的代码覆盖率报告也将infrastructure/actions/index.js
文件中的每个模块视为已导入并执行,留下我一个无用的代码覆盖率报告,看起来像这样。
--------------------------------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------------------------------------------|----------|----------|----------|----------|-------------------|
All files | 56.08 | 38.46 | 5.17 | 56.08 | |
display/containers | 100 | 100 | 100 | 100 | |
Layout.js | 100 | 100 | 100 | 100 | |
infrastructure/actions | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
infrastructure/actions/characters | 50 | 0 | 0 | 50 | |
fetchCharacters.js | 50 | 100 | 0 | 50 | 3 |
fetchedCharactersFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedCharactersSuccess.js | 50 | 100 | 0 | 50 | 3 |
untrackCharacter.js | 50 | 100 | 0 | 50 | 5 |
untrackCharacterFailure.js | 50 | 100 | 0 | 50 | 3 |
untrackCharacterSuccess.js | 50 | 100 | 0 | 50 | 3 |
upsertCharacter.js | 50 | 0 | 0 | 50 | 5 |
upsertCharacterFailure.js | 50 | 100 | 0 | 50 | 3 |
upsertCharacterSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/help | 50 | 100 | 0 | 50 | |
submitContactForm.js | 50 | 100 | 0 | 50 | 5 |
submitContactFormFailure.js | 50 | 100 | 0 | 50 | 3 |
submitContactFormSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/news | 50 | 100 | 0 | 50 | |
fetchNews.js | 50 | 100 | 0 | 50 | 3 |
fetchedNewsSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/public | 50 | 0 | 0 | 50 | |
fetchPublicThreads.js | 50 | 100 | 0 | 50 | 3 |
fetchPublicViews.js | 50 | 100 | 0 | 50 | 3 |
fetchedPublicThreadsFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedPublicThreadsSuccess.js | 50 | 100 | 0 | 50 | 3 |
fetchedPublicViewsFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedPublicViewsSuccess.js | 50 | 100 | 0 | 50 | 3 |
untrackPublicView.js | 50 | 100 | 0 | 50 | 5 |
untrackPublicViewFailure.js | 50 | 100 | 0 | 50 | 3 |
untrackPublicViewSuccess.js | 50 | 100 | 0 | 50 | 3 |
upsertPublicView.js | 50 | 0 | 0 | 50 | 5 |
upsertPublicViewFailure.js | 50 | 100 | 0 | 50 | 3 |
upsertPublicViewSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/tags | 50 | 100 | 0 | 50 | |
fetchTags.js | 50 | 100 | 0 | 50 | 3 |
fetchedTagsSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/threads | 50 | 0 | 0 | 50 | |
bulkUntrackThreads.js | 50 | 100 | 0 | 50 | 5 |
bulkUntrackThreadsFailure.js | 50 | 100 | 0 | 50 | 3 |
bulkUntrackThreadsSuccess.js | 50 | 100 | 0 | 50 | 3 |
bulkUpdateThreads.js | 50 | 100 | 0 | 50 | 5 |
bulkUpdateThreadsFailure.js | 50 | 100 | 0 | 50 | 3 |
bulkUpdateThreadsSuccess.js | 50 | 100 | 0 | 50 | 3 |
exportThreads.js | 50 | 100 | 0 | 50 | 5 |
exportThreadsFailure.js | 50 | 100 | 0 | 50 | 3 |
exportThreadsSuccess.js | 50 | 100 | 0 | 50 | 3 |
fetchActiveThreads.js | 50 | 100 | 0 | 50 | 3 |
fetchActiveThreadsStatus.js | 50 | 100 | 0 | 50 | 3 |
fetchArchivedThreads.js | 50 | 100 | 0 | 50 | 3 |
fetchedActiveThreadsFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedActiveThreadsStatusChunkFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedActiveThreadsStatusChunkSuccess.js | 50 | 100 | 0 | 50 | 3 |
fetchedActiveThreadsStatusFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedActiveThreadsStatusSuccess.js | 50 | 100 | 0 | 50 | 3 |
fetchedActiveThreadsSuccess.js | 50 | 100 | 0 | 50 | 3 |
fetchedArchivedThreadsFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedArchivedThreadsSuccess.js | 50 | 100 | 0 | 50 | 3 |
generateRandomThread.js | 50 | 100 | 0 | 50 | 5 |
generatedRandomThreadSuccess.js | 50 | 100 | 0 | 50 | 3 |
setFilteredTag.js | 50 | 100 | 0 | 50 | 3 |
untrackThread.js | 50 | 100 | 0 | 50 | 5 |
untrackThreadFailure.js | 50 | 100 | 0 | 50 | 3 |
untrackThreadSuccess.js | 50 | 100 | 0 | 50 | 3 |
upsertThread.js | 50 | 0 | 0 | 50 | 5 |
upsertThreadFailure.js | 50 | 100 | 0 | 50 | 3 |
upsertThreadSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/ui | 50 | 0 | 0 | 50 | |
closeBulkUntrackThreadsModal.js | 50 | 100 | 0 | 50 | 3 |
closeUntrackCharacterModal.js | 50 | 100 | 0 | 50 | 3 |
closeUntrackPublicViewModal.js | 50 | 100 | 0 | 50 | 3 |
closeUntrackThreadModal.js | 50 | 100 | 0 | 50 | 3 |
closeUpsertCharacterModal.js | 50 | 100 | 0 | 50 | 3 |
closeUpsertPublicViewModal.js | 50 | 100 | 0 | 50 | 3 |
closeUpsertThreadModal.js | 50 | 100 | 0 | 50 | 3 |
openBulkUntrackThreadsModal.js | 50 | 100 | 0 | 50 | 3 |
openUntrackCharacterModal.js | 50 | 100 | 0 | 50 | 3 |
openUntrackPublicViewModal.js | 50 | 100 | 0 | 50 | 3 |
openUntrackThreadModal.js | 50 | 100 | 0 | 50 | 3 |
openUpsertCharacterModal.js | 50 | 0 | 0 | 50 | 5 |
openUpsertPublicViewModal.js | 50 | 0 | 0 | 50 | 5 |
openUpsertThreadModal.js | 50 | 0 | 0 | 50 | 5 |
setActiveHelpTab.js | 50 | 100 | 0 | 50 | 5 |
setActiveSettingsTab.js | 50 | 100 | 0 | 50 | 5 |
setActiveToolsTab.js | 50 | 100 | 0 | 50 | 5 |
setMaintenanceModeOn.js | 50 | 100 | 0 | 50 | 3 |
toggleHeaderDropdown.js | 50 | 100 | 0 | 50 | 5 |
toggleMobileSidebar.js | 50 | 100 | 0 | 50 | 5 |
toggleNewsAside.js | 50 | 100 | 0 | 50 | 5 |
toggleSidebar.js | 50 | 100 | 0 | 50 | 5 |
infrastructure/actions/user | 50 | 100 | 0 | 50 | |
fetchUser.js | 50 | 100 | 0 | 50 | 3 |
fetchedUserFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedUserSuccess.js | 50 | 100 | 0 | 50 | 3 |
submitUserAccountInfo.js | 50 | 100 | 0 | 50 | 5 |
submitUserChangePassword.js | 50 | 100 | 0 | 50 | 5 |
submitUserForgotPassword.js | 50 | 100 | 0 | 50 | 5 |
submitUserLogin.js | 50 | 100 | 0 | 50 | 5 |
submitUserLogout.js | 50 | 100 | 0 | 50 | 5 |
submitUserRegistration.js | 50 | 100 | 0 | 50 | 5 |
submitUserResetPassword.js | 50 | 100 | 0 | 50 | 5 |
userAccountInfoFailure.js | 50 | 100 | 0 | 50 | 3 |
userAccountInfoSuccess.js | 50 | 100 | 0 | 50 | 3 |
userChangePasswordFailure.js | 50 | 100 | 0 | 50 | 3 |
userChangePasswordSuccess.js | 50 | 100 | 0 | 50 | 3 |
userForgotPasswordFailure.js | 50 | 100 | 0 | 50 | 3 |
userForgotPasswordSuccess.js | 50 | 100 | 0 | 50 | 3 |
userLoginFailure.js | 50 | 100 | 0 | 50 | 3 |
userLoginSuccess.js | 50 | 100 | 0 | 50 | 3 |
userRegistrationFailure.js | 50 | 100 | 0 | 50 | 3 |
userRegistrationSuccess.js | 50 | 100 | 0 | 50 | 3 |
userResetPasswordFailure.js | 50 | 100 | 0 | 50 | 3 |
userResetPasswordSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/actions/userSettings | 50 | 100 | 0 | 50 | |
fetchUserSettings.js | 50 | 100 | 0 | 50 | 3 |
fetchedUserSettingsFailure.js | 50 | 100 | 0 | 50 | 3 |
fetchedUserSettingsSuccess.js | 50 | 100 | 0 | 50 | 3 |
setShowDashboardThreadDistribution.js | 50 | 100 | 0 | 50 | 5 |
updateUserSettings.js | 50 | 100 | 0 | 50 | 5 |
updatedUserSettingsFailure.js | 50 | 100 | 0 | 50 | 3 |
updatedUserSettingsSuccess.js | 50 | 100 | 0 | 50 | 3 |
infrastructure/constants | 100 | 100 | 100 | 100 | |
analytics.js | 100 | 100 | 100 | 100 | |
utility | 62.5 | 100 | 50 | 62.5 | |
testHelpers.js | 62.5 | 100 | 50 | 62.5 | 12,13,16 |
--------------------------------------------------|----------|----------|----------|----------|-------------------|
毋庸置疑,这否定了代码覆盖率报告的目的,因为除了其中两个文件之外,这些文件都没有与被测试的组件相关 - 即使是那些文件,它们的代码也不应该通过这个测试来执行。
同样值得注意的是 - 在代码覆盖率报告的底部,它引用analytics.js
,它实际上由infrastructure/actions/index.js
的一个子文件导入,而不是由桶文件本身导入,这意味着覆盖范围以某种方式在依赖树中进一步传播。
只要删除导入操作的行,coverage文件就会立即再次自行运行并仅反映正在测试的组件。
如果我导入一个可能(可能)在布局中某处使用的组件,也会发生这种情况;我立即开始看到该组件及其子组件的覆盖指示一直向下,即使我只在我测试的组件中进行浅渲染。
在运行jest.mock()
方法之前,我曾尝试使用shallow()
来模拟这些导入,但它似乎没有对coverage输出产生任何影响。
所有这些让我相信我在设置我的测试环境时做错了什么;我对任何可能导致这种影响的指导表示感谢。
答案 0 :(得分:0)
对于那些好奇的人,我做了一些挖掘,并意识到这实际上是预期的行为 - 默认情况下导入的文件在导入时执行,所以虽然导入的文件中没有任何函数被执行,但是他们被初始化仍然因为他们的进口而受到打击。
这个解决方案是使用Jest模拟来模拟被测系统使用的所有导入,作为测试初始化的一部分。我将此行添加到测试文件的底部(Jest在执行期间将其提升到测试的顶部):
jest.mock('../../../infrastructure/actions', () => ({}));
(即当导入调用actions/index.js
时,则导入一个返回空对象的函数)。一切都恢复正常。