【问题标题】:Shallow jest-enzyme unit test generates coverage for entire application tree浅玩笑酶单元测试生成整个应用程序树的覆盖率
【发布时间】:2018-10-14 07:58:29
【问题描述】:

我有(用于演示目的)一个非常简单的 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 的一个子文件导入的,而不是由桶文件本身导入的,这意味着覆盖率在某种程度上是沿着依赖关系树走得更远。

一旦我删除导入操作的行,覆盖文件立即再次表现自己并仅反映正在测试的组件。

如果我导入一个(可能)会在布局中某处使用的组件,也会发生这种情况;我立即开始看到该组件及其子组件的覆盖率指示,即使我只是在我的被测组件中进行浅层渲染。

在运行 shallow() 方法之前,我尝试使用 jest.mock() 模拟这些导入,但它似乎对覆盖率输出没有任何影响。

所有这些都让我相信我在设置测试环境时做错了;对于可能导致这种影响的任何指导,我将不胜感激。

【问题讨论】:

    标签: reactjs unit-testing code-coverage jestjs enzyme


    【解决方案1】:

    对于那些好奇的人,我做了更多的挖掘并意识到这实际上是预期的行为 - 导入的文件默认在导入时执行,所以虽然导入文件中的函数都没有执行,但由于它们被导入,它们仍然被初始化。

    解决方案是使用 Jest 模拟来模拟被测系统使用的所有导入,作为测试初始化​​的一部分。我将此行添加到测试文件的底部(Jest 在执行期间将其提升到测试的顶部):

    jest.mock('../../../infrastructure/actions', () => ({}));
    

    (即当导入调用actions/index.js 时,导入一个返回空对象的函数)。一切都恢复了正常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-22
      • 1970-01-01
      • 2023-03-27
      • 2021-04-18
      • 2018-10-18
      • 2020-02-17
      相关资源
      最近更新 更多