【问题标题】:Trying to mock Stenciljs store (@stencil/store) state (multiple times) while writing unit test for Web Components在为 Web 组件编写单元测试时尝试模拟 Stenciljs 存储(@stencil/store)状态(多次)
【发布时间】:2020-06-03 05:26:43
【问题描述】:

我正在为 Web 组件编写单元测试。这些 Web 组件是使用 Stenciljs 构建的。 StencilJS 使用 Jest 来定义和运行这些单元测试。另外,我是 Jest 和 StencilJS 的新手,所以我的方法可能不符合某些定义的标准。让我描述一下应用程序和我的问题。

该项目具有使用Stencil-Store 在文件中定义的全局状态。文件内容如下所示:

global/store.ts


    import { createStore } from '@stencil/store';

      const { state } = createStore({
        appName: 'Application Name',
        platformName: 'Platform Name',
        mode: 'default', // light, dark, default
        appActionBar: 'closed', // open, closed 
      });

    export default state;

因此,在我们需要来自状态的信息的任何组件中,或者如果该组件需要更新状态中的信息,我们在该组件中导入这个state 并执行任何操作。例如,考虑一个 Web 组件app-shell

app.shell.tsx


    import { Component, h, Host } from '@stencil/core';
    import state from './global/store';

    @Component({
      tag: 'app-shell',
      styleUrl: 'app-shell.scss',
      shadow: true,
    })
    export class AppShell {
    render() {
        return (
          <Host
            data-mode={state.mode}
            class={{
              'action-bar-closed': state.appActionBar === 'closed',
              'action-bar-open': state.appActionBar === 'open',
            }}
          >
            {/* Header */}
            <div
              class={{
                'app-shell-header': true,
                'side-panel-open': state.sidepanel === true,
              }}
            >
              <slot name="app-shell-header"></slot>
            </div>
            {/* Shell Content */}
            <div class="app-shell-content">
              <slot name="app-shell-content"></slot>
            </div>
          </Host>
    }
    }

您可以在上面的代码中注意到,此 Web 组件从 state 读取值并基于此应用 classes。现在为了测试不同的类被应用,我认为mock the state 并为不同的测试提供不同的states 值是个好主意。

考虑到这一点,我开始为这个 Web 组件编写测试。我提到了来自 StencilJS 的mocking doc 和一般的测试文档。这是一个spec 文件:

app-shell.spec.ts


    // Mock the global state for testing purposes.
    jest.mock('./global/store', () => {
      const { state } = createStore({
        appName: 'Mocked Name',
        platformName: 'Mocked Platform Name',
        mode: 'default',
        appActionBar: 'closed',
      });
      return state;
    });
    import { newSpecPage, SpecPage } from '@stencil/core/testing';
    import { createStore } from '@stencil/store';
    import { AppShell } from './app-shell';

    describe('app-shell', () => {
      let page: SpecPage;
      let element: any;
      let componentInstance: any;

      beforeEach(async () => {
        page = await newSpecPage({
          components: [AppShell],
          html: `<app-shell></app-shell>`,
          supportsShadowDom: true,
        });
        element = page.doc.querySelector('app-shell');
        componentInstance = page.rootInstance;
      });
      describe('build and render', () => {
        it('should build', async () => {
          expect(page).toBeTruthy();
        });
        it('should render', async () => {
          expect(page.root).toEqualHtml(`
          <app-shell class="action-bar-closed" data-mode="default">
            <mock:shadow-root>
              <div class="app-shell-header">
                <slot name="app-shell-header"></slot>
              </div>
              <div class="app-shell-content">
                <slot name="app-shell-content"></slot>
              </div>
            </mock:shadow-root>
          </app-shell>
        `);
        });
      });
    })

;

因此,通过遵循 StencilJS 模拟文档,我能够使用 jest.mock() 模拟状态值(如上面的规范文件中所示)。所以当我运行这个测试时,我得到了这个规范文件中定义的模拟状态值。一切顺利!

问题

但是,我想覆盖/更新同一 spec 文件下不同 describe 的模拟状态值,以便我可以提供 different 状态值来测试不同的案例。

例如,我想实现这样的目标。



// Mock the global state for testing purposes.
    jest.mock('./global/store', () => {
      const { state } = createStore({
        appName: 'Mocked Name',
        platformName: 'Mocked Platform Name',
        mode: 'default',
        appActionBar: 'closed',
      });
      return state;
    });
    import { newSpecPage, SpecPage } from '@stencil/core/testing';
    import { createStore } from '@stencil/store';
    import { AppShell } from './app-shell';

    describe('app-shell', () => {
      let page: SpecPage;
      let element: any;
      let componentInstance: any;

      beforeEach(async () => {
        page = await newSpecPage({
          components: [AppShell],
          html: `<app-shell></app-shell>`,
          supportsShadowDom: true,
        });
        element = page.doc.querySelector('app-shell');
        componentInstance = page.rootInstance;
      });
      describe('build and render', () => {
        it('should build', async () => {
          expect(page).toBeTruthy();
        });
        it('should render', async () => {
          expect(page.root).toEqualHtml(`
          <app-shell class="action-bar-closed" data-mode="default">
            <mock:shadow-root>
              <div class="app-shell-header">
                <slot name="app-shell-header"></slot>
              </div>
              <div class="app-shell-content">
                <slot name="app-shell-content"></slot>
              </div>
            </mock:shadow-root>
          </app-shell>
        `);
        });
      });
      describe('mock state again', () => {
        it('should have new mocked values', async () => {
           // Mock the global state for testing purposes.
           jest.mock('./global/store', () => {
           const { state } = createStore({
             appName: 'Mocked Name',
             platformName: 'Mocked Platform Name',
             mode: 'dark',
             appActionBar: 'open',
         });
      return state;
    });
          // Have expect statement here... 
        });
      });
    })
;

正如您在上面的代码 sn-p 中所注意到的,我尝试使用 jest.mock() 两次 mockstate。但是,我无法再次模拟该状态。

问题 解决我的问题的正确方法是什么,即能够在单个规范文件中多次模拟状态?

任何意见将不胜感激。谢谢!

【问题讨论】:

    标签: unit-testing mocking jestjs web-component stenciljs


    【解决方案1】:

    让我在这里回答这个问题。

    所以我在发布此问题链接后的几分钟内就在 StencilJS Slack 小组上得到了答案。

    谢谢@simon-hänisch

    首先,您根本不需要模拟商店,可以直接从Spec 文件更新状态本身。

    我不得不做出一些改变:

    1) 所以我将store.ts 更改为导出dispose() 以及状态。

    export const { state, dispose } = createStore({ ... })
    

    2) 接下来,我将spec 文件更新为如下内容:

    import {state, dispose} from '../global/store';
    
      beforeEach(async () => {
        dispose();
        page = await newSpecPage({
          components: [AppShell],
          html: `<app-shell></app-shell>`,
          supportsShadowDom: true,
        });
        element = page.doc.querySelector('app-shell');
        componentInstance = page.rootInstance;
      });
    
    describe('my test', () => {
      it('light mode', async () => {
        // update state
        state.mode = 'light';
        // expect statement
        ...
      });
      it('dark mode', async () => {
        // update state
        store.mode = 'dark';
        // expect statement
        ...
      });
    });
    

    我现在在beforeEach 中进行所有设置,我唯一需要做的就是在创建page 之前调用dispose

    通过这种方式更新状态,我不需要模拟状态。

    希望这对 StencilJS 的许多新手(比如我)有用:)

    【讨论】:

      猜你喜欢
      • 2018-03-01
      • 1970-01-01
      • 2019-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多