Попытка имитировать состояние Stenciljs store (@ stencil / store) (несколько раз) при написании модульного теста для веб-компонентов

Я работаю над написанием модульных тестов для веб-компонентов. Эти веб-компоненты созданы с использованием 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 в этот компонент и выполняем любое действие. Например, рассмотрим веб-компонент 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>
    }
    }

Как вы можете заметить в приведенном выше коде, этот веб-компонент считывает значения из state и применяет classes на основе этого. Теперь, чтобы протестировать применение различных классов, я подумал, что было бы неплохо mock the state и предоставить разные значения states для разных тестов.

Помня об этом, я начал писать тест для этого веб-компонента. Я сослался на издевательский документ из StencilJS и тестовый документ в целом. Вот файл 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(). Поэтому, когда я запускаю этот тест, я получаю фиктивные значения состояния, как определено в этом файле спецификации. Все хорошо!

Проблема

Однако я хотел переопределить / обновить фиктивные значения состояния для разных describe в одном и том же spec файле, чтобы я мог предоставить 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... 
        });
      });
    })
;

Как вы могли заметить в приведенном выше фрагменте кода, я пытаюсь mock state дважды, используя jest.mock(). Однако я не могу снова издеваться над государством.

Вопрос Каков правильный способ решения моей проблемы, т. е. возможность многократно имитировать состояние в одном файле спецификации?

Любой вклад будет оценен. Спасибо!


person mrsan22    schedule 03.06.2020    source источник


Ответы (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 сейчас, единственное, что мне нужно сделать, это вызвать dispose, прежде чем я создам page.

Обновляя состояние таким образом, мне не нужно имитировать состояние.

Надеюсь, это будет полезно многим новичкам (вроде меня) в StencilJS :)

person mrsan22    schedule 05.06.2020