2

I'm new to Cypress component testing, but I was able to set it up easily for my Vue project. I'm currently investigating if we should replace Jest with Cypress to test our Vue components and I love it so far, there is only one major feature I'm still missing: mocking modules. I first tried with cy.stub(), but it simply didn't work which could make sense since I'm not trying to mock the actual module in Node.js, but the module imported within Webpack.

To solve the issue, I tried to use rewiremock which is able to mock Webpack modules, but I'm getting an error when running the test:

enter image description here

I forked the examples from Cypress and set up Rewiremock in this commit. Not sure what I'm doing wrong to be honest.

I really need to find a way to solve it otherwise, we would simply stop considering Cypress and just stick to Jest. If using Rewiremock is not the way, how am I suppose to achieve this? Any help would be greatly appreciated.

pmrotule
  • 9,065
  • 4
  • 50
  • 58
  • were you able to figure this out? we're also using cypress component testing + rewiremock but with React, and we're encountering the same exact error – jchi2241 Sep 02 '21 at 20:57
  • @jchi2241 I didn't unfortunately since Jest won against Cypress when we evaluated the two solutions. Please share if you find something. – pmrotule Sep 03 '21 at 06:35
  • We found a solution -- instead of using rewiremock to mock modules, we aliased the dependencies we wanted to mock in the Cypress webpack config with [webpack aliases][1]. Instead of loading the module, webpack would intercept and load a mock file. In that file we'd have functions that would reset the internal mock that we'd call before each test. e.g., `let mock; export function setupMock(...) { mock = { ... } } export default mock;` [1]: https://webpack.js.org/configuration/resolve/#resolvealias – jchi2241 Sep 09 '21 at 21:22
  • Yeah I also thought about it, but it's not really what I'm looking for unfortunately since this would mock it for all tests and quite often, we need to mock a module for a specific test only which is done very easily with Jest. – pmrotule Sep 10 '21 at 13:52

2 Answers2

2

If you are able to adjust the Vue component to make it more testable, the function can be mocked as a component property.

Webpack

When vue-loader processes HelloWorld.vue, it evaluates getColorOfFruits() and sets the data property, so to mock the function here, you need a webpack re-writer like rewiremock.

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      colorOfFruits: getColorOfFruits(),   // during compile time
    };
  },
  ...

Vue created hook

If you initiialize colorOfFruits in the created() hook, you can stub the getColorOfFruits function after import but prior to mounting.

HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>{{ colorOfFruits.apple }}</h2>
    <p>
    ...
</template>

<script lang="ts">
import { getColorOfFruits } from "@/helpers/fruit.js";  

export default {
  name: "HelloWorld",
  getColorOfFruits,          // add this function to the component for mocking
  props: {
    msg: String,
  },
  data() {
    return {
      colorOfFruits: {}      // initialized empty here
    };
  },
  created() {
    this.colorOfFruits = this.$options.colorOfFruits;  // reference function saved above
  }
});
</script>

HelloWorld.spec.js

import { mount } from "@cypress/vue";
import HelloWorld from "./HelloWorld.vue";

it("mocks an apple", () => {

  const getMockFruits = () => {
    return {
      apple: "green",
      orange: "purple",
    }
  }

  HelloWorld.getColorOfFruits = getMockFruits;

  mount(HelloWorld, {                      // created() hook called as part of mount()
    propsData: {
      msg: "Hello Cypress!",
    },
  });

  cy.get("h1").contains("Hello Cypress!");

  cy.get("h2").contains('green')

});
Richard Matsen
  • 20,671
  • 3
  • 43
  • 77
0

We solved this by using webpack aliases in the Cypress webpack config to intercept the node_module dependency.

So something like...

// cypress/plugins/index.js

const path = require("path");
const { startDevServer } = require('@cypress/webpack-dev-server');

// your project's base webpack config
const webpackConfig = require('@vue/cli-service/webpack.config');

module.exports = (on, config) => {
  on("dev-server:start", (options) => {
    webpackConfig.resolve.alias["your-module"] = path.resolve(__dirname, "path/to/your-module-mock.js");

    return startDevServer({
      options,
      webpackConfig,
    })
  });
}
// path/to/your-module-mock.js

let yourMock;

export function setupMockModule(...) {
  yourMock = {
    ...
  };
}

export default yourMock;
// your-test.spec.js

import { setupMock } from ".../your-module-mock.js"

describe("...", () => {
  before(() => {
    setupMock(...);
  });

  it("...", () => {
    ...
  });
});
jchi2241
  • 2,032
  • 1
  • 25
  • 49