首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用vue- test -utils和Jest在Nuxt中使用Vuex模块-装饰器语法定义vuex模块?

如何使用vue- test -utils和Jest在Nuxt中使用Vuex模块-装饰器语法定义vuex模块?
EN

Stack Overflow用户
提问于 2021-09-24 12:34:17
回答 1查看 2.3K关注 0票数 2

我在任何地方都找不到这个问题的答案。

我浏览了Nuxt的官方文档,以及现有的Stack溢出和Github问题讨论。

AuthModule的实现:

代码语言:javascript
复制
@Module({
  stateFactory: true,
  namespaced: true,
})
export default class AuthModule extends VuexModule {
  userData?: UserData | undefined = undefined;
  prevRouteList: Routes[] = [];
  error?: services.ICognitoError | undefined = undefined;
  isLoading = false;
  ...

  @VuexMutation
  setIsLoading(isLoading: boolean) {
    this.isLoading = isLoading;
  }
 
  ...

   @VuexAction({ rawError: true })
  async register(registerData: { email: string; password: string }): Promise<any> {
    this.context.commit('setIsLoading', true);
    this.context.commit('setError', undefined);
    this.context.commit('setInitiateRegistration', false);
    this.context.dispatch('setEmail', registerData.email);

    try {
      const { user } = await services.register(registerData.email, registerData.password);

      if (user) {
        this.context.dispatch('pushPrevRoute', Routes.emailVerification);
        this.context.commit('setInitiateRegistration', true);
      }
    } catch (error: any) {
      this.context.commit('setError', error);
      this.context.commit('setInitiateRegistration', false);
    }

    this.context.commit('setIsLoading', false);
  }

  ...

  @MutationAction
  setEmail(email: string)  { ... }

  ... 

  get getEmail() {
    return this.email;
  }

  ... 

}

我的/store目录只包含Vuex模块(如示例AuthModule)。在我声明和实例化存储的地方没有index.ts。此外,这些模块不是动态的。

所以我的问题是:

  1. vuex模块-装饰器 synax定义的Nuxt模块使用取笑vue-test-utils编写单元测试的正确模式是什么?
  2. 如何对VuexMutations、VuexActions、MutationActions、getter等进行单元测试?

我尝试在测试文件中实例化AuthModule类,但无法让它工作。

代码语言:javascript
复制
describe('AuthModule', () => {
  const authModule = new AuthModule({...});

  it('test', () => {
   console.log(authModule);

   /* 
     AuthModule {
      actions: undefined,
      mutations: undefined,
      state: undefined,
      getters: undefined,
      namespaced: undefined,
      modules: undefined,
      userData: undefined,
      prevRouteList: [],
      error: undefined,
      isLoading: false,
      registrationInitiated: false,
      registrationConfirmed: false,
      forgotPasswordEmailSent: false,
      forgottenPasswordReset: false,
      email: '',
      maskedEmail: ''
    }*/
  });

我还尝试了这里解释的方法:

https://medium.com/@brandonaaskov/how-to-test-nuxt-stores-with-jest-9a5d55d54b28

在这里:

用Jest测试NUXT.js和Vue.js应用程序。获取“[vuex]模块命名空间在mapState()”和“[vuex]未知操作类型”中找不到

以下是基于这些文章/链接中的建议的设置:

代码语言:javascript
复制
// jest.config.js

module.exports = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  roots: [
    '<rootDir>/components',
    '<rootDir>/pages',
    '<rootDir>/middleware',
    '<rootDir>/layouts',
    '<rootDir>/services',
    '<rootDir>/store',
    '<rootDir>/utils',
  ],
  reporters: ['default', 'jest-sonar'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js',
  },
  moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
  testEnvironment: 'jsdom',
  transform: {
    '^.+\\.ts$': 'ts-jest',
    '.*\\.(vue)$': 'vue-jest',
    '^.+\\.(js|jsx)$': 'babel-jest-amcharts',
  },
  collectCoverage: true,
  collectCoverageFrom: [
    '<rootDir>/components/**/*.vue',
    '<rootDir>/pages/**/*.vue',
    '<rootDir>/layouts/**/*.vue',
    '<rootDir>/middleware/**/*.ts',
    '<rootDir>/store/**/*.ts',
    '<rootDir>/mixins/**/*.ts',
    '<rootDir>/services/**/*.ts',
  ],
  transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\](?!(@amcharts)\\/).+\\.(js|jsx|ts|tsx)$'],
  forceExit: !!process.env.CI,
};

// jest.setup.js

import { config } from '@vue/test-utils';
import { Nuxt, Builder } from 'nuxt';
import TsBuilder from '@nuxt/typescript-build';
import nuxtConfig from './nuxt.config';

config.stubs.nuxt = { template: '<div />' };
config.stubs['nuxt-link'] = { template: '<a><slot></slot></a>' };
config.mocks.$t = (msg) => msg;

const nuxtResetConfig = {
  loading: false,
  loadingIndicator: false,
  fetch: {
    client: false,
    server: false,
  },
  features: {
    store: true,
    layouts: false,
    meta: false,
    middleware: false,
    transitions: false,
    deprecations: false,
    validate: false,
    asyncData: false,
    fetch: false,
    clientOnline: false,
    clientPrefetch: false,
    clientUseUrl: false,
    componentAliases: false,
    componentClientOnly: false,
  },
  build: {
    indicator: false,
    terser: false,
  },
};

const nuxtBuildConfig = {
  ...nuxtConfig,
  ...nuxtResetConfig,
  dev: false,
  extensions: ['ts'],
  ssr: false,
  srcDir: nuxtConfig.srcDir,
  ignore: ['**/components/**/*', '**/layouts/**/*', '**/pages/**/*'],
};

const buildNuxt = async () => {
  const nuxt = new Nuxt(nuxtBuildConfig);
  await nuxt.moduleContainer.addModule(TsBuilder);

  try {
    await new Builder(nuxt).build();
    return nuxt;
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

module.exports = async () => {
  const nuxt = await buildNuxt();
  process.env.buildDir = nuxt.options.buildDir;
};


// jest.utils.js

import Vuex from 'vuex';
import VueRouter from 'vue-router';
import VueFormulate from '@braid/vue-formulate';
import { mount, createLocalVue } from '@vue/test-utils';

const createStore = (storeOptions = {}) => new Vuex.Store({ ...storeOptions });
const createRouter = () => new VueRouter({});

const setup = (storeOptions) => {
  const localVue = createLocalVue();
  localVue.use(VueRouter);
  localVue.use(Vuex);
  localVue.use(VueFormulate);

  const store = createStore(storeOptions);
  const router = createRouter();
  return { store, router, localVue };
};

export const createNuxtStore = async () => {
  const storePath = `${process.env.buildDir}/store.js`;

  // console.log(storePath);
  const NuxtStoreFactory = await import(storePath);
  const nuxtStore = await NuxtStoreFactory.createStore();

  return { nuxtStore };
};

export const createTestBed =
  (component, componentOptions = {}, storeOptions = {}) =>
  (renderer = mount) => {
    const { localVue, store, router } = setup(storeOptions);

    return renderer(component, {
      store,
      router,
      localVue,
      ...componentOptions,
    });
  };

// auth.spec.js

import { createNuxtStore } from '@/jest.utils';

describe('AuthModule', () => {
  let store: any;

  beforeAll(() => {
    store = createNuxtStore();
  });

  it('should create', () => {
    console.log(store);
  });
});

运行此操作后,我在控制台中得到以下错误:

代码语言:javascript
复制
 RUNS  store/auth.spec.ts
node:internal/process/promises:245
          triggerUncaughtException(err, true /* fromPromise */);
          ^

ModuleNotFoundError: Cannot find module 'undefined/store.js' from 'jest.utils.js'
    at Resolver.resolveModule (/Users/ivan.spoljaric/Documents/.../node_modules/jest-resolve/build/index.js:306:11)
    at Resolver._getVirtualMockPath (/Users/ivan.spoljaric/Documents/.../node_modules/jest-resolve/build/index.js:445:14)
    at Resolver._getAbsolutePath (/Users/ivan.spoljaric/Documents/.../node_modules/jest-resolve/build/index.js:431:14)
    at Resolver.getModuleID (/Users/ivan.spoljaric/Documents/.../node_modules/jest-resolve/build/index.js:404:31)
    at Runtime._shouldMock (/Users/ivan.spoljaric/Documents/.../node_modules/jest-runtime/build/index.js:1521:37)
    at Runtime.requireModuleOrMock (/Users/ivan.spoljaric/Documents/.../node_modules/jest-runtime/build/index.js:916:16)
    at /Users/ivan.spoljaric/Documents/.../jest.utils.js:24:28
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
    at Object.createNuxtStore (/Users/ivan.spoljaric/Documents/.../jest.utils.js:24:28) {
  code: 'MODULE_NOT_FOUND',
  hint: '',
  requireStack: undefined,
  siblingWithSimilarExtensionFound: false,
  moduleName: 'undefined/store.js',
  _originalMessage: "Cannot find module 'undefined/store.js' from 'jest.utils.js'"
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-28 13:04:16

经过反复试验,我终于找到了我问题的答案。

如果你和我一样,只是和Vue,Nuxt &vuex模组装饰师一起开始你的旅程,而你却被困在处理这个完全相同的问题上,我希望这个小小的单独QA乒乓能找到你!

我的解决方案如下:

代码语言:javascript
复制
// auth.spec.ts

import Vuex, { Store } from 'vuex';
import { createLocalVue } from '@vue/test-utils';

import AuthModule, { IState } from './auth';

jest.mock('@/services');

const localVue = createLocalVue();
localVue.use(Vuex);

const storeOptions = {
  modules: {
    auth: AuthModule,
  },
};

const createStore = (storeOptions: any = {}): Store<{ auth: IState }> => new Vuex.Store({ ...storeOptions });

describe('AuthModule', () => {
  let store: Store<{ auth: IState }>;

  beforeEach(() => {
    store = createStore(storeOptions);
  });

  describe('mutations', () => {
    // ...

    it('auth/setIsLoading', () => {
      expect(store.state.auth.isLoading).toBe(false);
      store.commit('auth/setIsLoading', true);
      expect(store.state.auth.isLoading).toBe(true);
    });

    // ...
  });

  describe('actions', () => {
    // ...

    it('register success', async () => {
      const registerData = {
        email: 'dummy@email.com',
        password: 'dummy',
      };

      expect(store.state.auth.registrationInitiated).toBe(false);

      try {
        await store.dispatch('auth/register', registerData);
        expect(store.state.auth.registrationInitiated).toBe(true);
      } catch (error) {}
    });

    // ...
  });

  describe('mutation-actions', () => {
    // ...

    it('setEmail', async () => {
      const dummyEmail = 'dummy@email.com';

      expect(store.state.auth.email).toBe('');
      await store.dispatch('auth/setEmail', dummyEmail);
      expect(store.state.auth.email).toBe(dummyEmail);
    });

    // ...
  });

  describe('getters', () => {
    // ...

    it('auth/getError', () => {
      expect(store.state.auth.error).toBe(undefined);
      expect(store.getters['auth/getError']).toBe(undefined);

      (store.state.auth.error as any) = 'Demmo error';
      expect(store.getters['auth/getError']).toBe('Demmo error');
    });

    // ...
  });
});

// services/auth

export async function register(email: string, password: string, attr: any = {}): Promise<any> {
  try {
    return await Auth.signUp({
      username: email,
      password,
      attributes: {
        ...attr,
      },
    });
  } catch (err: any) {
    return Promise.reject(createError(err, 'register'));
  }
}

// createError is just a util method for formatting the error message and wiring to the correct i18n label

// services/__mock__/auth

import { createError } from '../auth';

export const register = (registerData: { email: string; password: string }) => {
  try {
    if (!registerData) {
      throw new Error('dummy error');
    }

    return new Promise((resolve) => resolve({ response: { user: registerData.email } }));
  } catch (err) {
    return Promise.reject(createError(err, 'register'));
  }
};

// 

需要认识到的最重要的一点是,基于vuex模块-装饰器类的模块的行为就像遮罩下的vue-类组件

所有的vuex模块装饰器都只是语法糖--一个包装vue类组件API的包装器。

引用医生们

在您的商店中,您将MyModule类本身作为module...The使用,我们使用的MyModule类不同于传统的面向对象编程,类似于vue类组件的工作方式。我们使用类本身作为模块,而不是类构造的对象。

要记住的另一件事是使用createLocalVue,它使我们能够使用Vue类、插件、组件等,而不会污染全局Vue类。

createLocalVue中添加Vuex插件

localVue.use(Vuex);

AuthModule类在Vuex.Store构造函数中被声明为Vuex (名称空间)模块(根据文档)。

代码语言:javascript
复制
const storeOptions = {
  modules: {
    auth: AuthModule,
  },
};

const createStore = (storeOptions: any = {}): Store<{ auth: IState }> => new Vuex.Store({ ...storeOptions });

在上面的实现中,AuthModule (包括。存储、操作、突变、获取器.)是在beforeEach钩子的帮助下为每个测试用例重新创建的(因此每个测试都有一个干净的存储库)。

其余的都很简单。您可以看到我如何测试AuthModule的每个部分(动作、突变、getter.)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69315107

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档