nuxt / test-utils

🧪 Test utilities for Nuxt
http://nuxt.com/docs/getting-started/testing
MIT License
287 stars 75 forks source link

mocking plugins used within nuxt #544

Open gbyesiltas opened 1 year ago

gbyesiltas commented 1 year ago

Hey, firstly thank you for making this plugin, it looks very useful :)

Running this module seems to run the whole nuxt environment, including the plugins, and we have a plugin that makes a api call. We normally just mock the module where we import the api function but it doesn't seem to work in this case. For example:

// somePlugin.ts
import { fetchProducts } from "~/service/products";

export default defineNuxtPlugin(async () => {
    const products = fetchProducts();
    // do something with the products
});
// SomeComponent.nuxt.spec.js
import { describe, it, expect, vi } from "vitest";
import { shallowMount } from "@vue/test-utils";
import SomeComponent from "../SomeComponent.vue";

// trying to mock the products service so that the real api doesn't get called during the plugins
vi.mock("~/service/products");

describe("SomeComponent", async () => {
  it("Is a Vue instance", () => {
    const wrapper = shallowMount(SomeComponent);
    expect(wrapper.vm).toBeTruthy();
  });
});

I assume these mocks only work for the component that we are testing and not for the plugins in the nuxt environment for example. Do you have a suggestion about how to solve this?

joel-wenzel commented 1 year ago

I had a similar problem, my rest client is setup in a nuxt plugin and a subsequent plugin uses that client to make an app init api call.

In my rest client plugin i have something like the following:

const client = process.env?.TEST ? createMockClient() : createClient()
provideClient(client)

How you actually mock the requests is up to you

gbyesiltas commented 1 year ago

I had a similar problem, my rest client is setup in a nuxt plugin and a subsequent plugin uses that client to make an app init api call.

In my rest client plugin i have something like the following:

const client = process.env?.TEST ? createMockClient() : createClient()
provideClient(client)

How you actually mock the requests is up to you

Thanks! Indeed we could do this but I personally like not having any test related logic in the actual code so I would definitely prefer somehow moving this to the vitest config or the test files themselves :/

danielroe commented 1 year ago

it's worth noting that you can mock api routes with registerEndpoint: https://github.com/danielroe/nuxt-vitest#registerendpoint.

There may be other reasons you would want to mock a plugin, and I think it should be possible, so leaving this issue up until we document it.

Nicolas-Yazzoom commented 11 months ago

I'm using a plotting library in my project that is installed as a client side only plugin using the name convention (yourplugin.client.ts) since it uses browser API's. When running tests I get errors indicating the plugin is being executed in an environment that doesn't support it. Could it be that I need additional steps to get client side plugins to behave properly? Being able to mock this all together would be great since none of the things I want to test rely on this plugin. This is my code:

// vitest.config.ts
import { defineVitestConfig } from 'nuxt-vitest/config'

export default defineVitestConfig({
  test: {
    environment: 'nuxt'
  }
})
// plugins/09.plotly.client.ts
import Plotly from 'plotly.js-dist-min'

export default defineNuxtPlugin(() => {
  return {
    provide: {
      Plotly
    }
  }
})
Austio commented 8 months ago

@danielroe Would it be possible to have something that would allow us to replace a plugin in tests?

Api would be akin to

// In the test
import { overridePlugin } from 'nuxt-vitest'

it('allows me to do things that have great power', () => {
  overridePlugin('something', () => {
    ...function that would override the plugin and receive the same arguments
  })
})
adam-gipril commented 7 months ago

I'd like to echo the desire to be able to mock and/or exclude functionality within plugins. For a project I work on, either or both of the following resolutions would be ideal for our use case:

1. Some way to easily specify which plugins to include/exclude in the nuxt-vitest Nuxt runtime

The ability to exclude plugins that don't need to run in our testing environment would save us from needing to mock that plugin logic despite questionable value to the quality of our tests.

Note:

We tried to specify a subset of our plugins using environmentOptions.nuxt.overrides.plugins in our vitest.config.ts, but Nuxt appears to auto-import all of the plugins despite our explicitly-passed subset.

2. A way to run test setup files (specifically, files with the same context as Vitest setupFiles) before spinning up the Nuxt runtime environment

Upon running vitest from the command line (through npm test), we see the following output. (Sample repo with reproducible output here).

❯ npm test

> nuxt-app@0.0.0 test
> vitest

global setup

 DEV  v0.33.0 /Users/adam.gipril/Developer/nuxt-vitest-playground

stdout | unknown test
<Suspense> is an experimental feature and its API will likely change.

stdout | unknown test
setup file

 ✓ app.test.ts (1)
   ✓ is 2

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  14:31:58
   Duration  553ms (transform 216ms, setup 3ms, collect 6ms, tests 1ms, environment 382ms, prepare 53ms)

 PASS  Waiting for file changes...
       press h to show help, press q to quit

So to put this ask in other words, is there a way to run some sort of setup with the same Vitest context and mocking abilities as the setupFiles, but such that they execute after globalSetup and before the Nuxt runtime starts?

danielroe commented 7 months ago

@adam-gipril Yes, we should build this in in future. (Adding setup files easily.) For now, you can likely implement with this hack:

import { defineVitestConfig } from 'nuxt-vitest/config';

export default defineVitestConfig({
  test: {
    environmentOptions: {
      nuxt: {
        overrides: {
          plugins: [
            // path to custom file
          ]
        }
      }
    }
  }
});