Home > Net >  How can i create fake functions to test the components?
How can i create fake functions to test the components?

Time:02-10

I have to test my component, but that component have a functions for that it can work, this function need a key enviroment,

my function have a onMounted function

 onMounted(async () => {

    const available = await getAvailableBills()

and the implementation is:

export const getAvailableBills = async () => {
  try {
    const bill = `${process.env.VAR_ENV}`

i am getting this error:

Error: Provided address {VAR_ENV} is invalid

but i think i dont need to execute the real functions i would like to create fake functions in my test

import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/tables'

const store = createStore({
  state() {
    return {
      user: {},
    }
  },
  mutations: {},
})

let wrapper
const app = createApp(App)
app.use(store)
beforeEach(() => {
  wrapper = shallowMount(App, {
    propsData: {
      whitelist: [1,2,3],
    },
    global: {
      plugins: [store],
    },
  })
})

describe('Tables', () => {
  it('test 1', () => {
    expect(wrapper.props().list).toEqual([1,2,3])
  })
})

CodePudding user response:

You have to mock it. It seems like you're using jest. You can use jest.spyOn method to mock a particular object, or mock the whole file using jest.Mock.

For example, if you have

src /
- app.js
- app.spec.js
- services /
--- bills.js

In your src/app.spec.js, if you're exported function getAvailableBills is in src/services/bills.js, just do this :

import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/tables'

import { getAvailableBills } from './services/bills'
jest.mock('./services/bills', {
  getAvailableBills: jest.fn().mockResolvedValue({ bills: ['...'] }) // you can mock the response directly here for the whole file
})

getAvailableBills.mockResolvedValue({ bills: ['...'] }) // or you can mock the response when you need to mock it

const store = createStore({
  state() {
    return {
      user: {},
    }
  },
  mutations: {},
})

let wrapper
const app = createApp(App)
app.use(store)
beforeEach(() => {
  wrapper = shallowMount(App, {
    propsData: {
      whitelist: [1,2,3],
    },
    global: {
      plugins: [store],
    },
  })
})

describe('Tables', () => {
  it('test 1', () => {
    expect(wrapper.props().list).toEqual([1,2,3])
  })
})

Notice that I used mockResolvedValue because it's returning a Promise (async method), but if it returns a direct value and not a promise, use mockReturnValue. You can also mock only once when needed with mockResolvedValueOnce and mockReturnValueOnce, or mock a rejected promise with mockRejectedValue.

Btw, you better encapsulate the tests inside describe, it avoids some errors, and it makes more readable & writable tests. You should also mock inside beforeAll / beforeEach methods to mock once for multiple tests when needed.

If needed, you can also add a setup files where you instantiate fake env values inside jest.config. That's not the point but it may help you one day.

CodePudding user response:

If you're trying to replace a function/method of your component, the short answer is: you don't!.

Never replace parts of your component in tests. The tests are there to check if your component fails, now and in the future, after you make changes to it. If you introduce a breaking change in the future, but the test replaces the part/method where you introduce the change, how will your test know the app is broken?


There are a few important principles you need to follow when testing:

  1. Do not modify your component so it passes tests. Only modify it to fix real problems with it, found during testing.
  2. Never replace parts of your component during testing. What you can (and should) do is mock anything external to it, replacing those parts with functions/modules performing what you expect those parts to do in a live environment 1. The gist of a mock is:
    jest.mock('some-module', () => jest.fn().mockImplementation(() => yourMock )).
    Full docs here.
    Mocking needs to happen before your test calls import Something from 'some-module'.
  3. Only test inputs/outputs of your component. Don't test how your component does it (a.k.a internal implementation) (this allows you to refactor it at some later point without breaking the test, as long as your component still outputs as expected for each provided input).
    Also don't test if external stuff works. Your test is not concerned with that. External stuff has its own tests and is, typically, already tested. That's why you rely on it.

So, in your case, you want to modify process.env during the test. The only problem is process.env is global and if you make changes to it, those changes will persist after your test ends, unless you change everything back to how it was when you started the test.

Most importantly, you have to call jest.resetModules() before each test, to clear jest cache (by default, jest caches, so it runs tests faster).

Here's an example of how to test with controlled process.env. This should eliminate the need to replace the components' method:

describe('SomeComponent', () => {
  const REAL_ENV = process.env;

  beforeEach(() => {
    jest.resetModules();
    process.env = { ...process.env }; // copy, for your test
  });

  afterAll(() => {
    process.env = REAL_ENV; // restore initial values
  });

  it('should do stuff depending on process.env', () => {
    process.env.FOO = 'bar';

    wrapper = shallowMount(/*...*/);
    expect(/* wrapper to do stuff...*/)
  })
});

Testing with controlled process.env values needs shallow mounting inside the test, rather than the typical shallow mounting in beforeAll, I normally create two separate testing suites: one for normal tests, shallow mounting in beforeAll, and one for special tests, which set up environment vars before shallow mounting:

describe('SomeComponent - normal tests', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallowMount(/*...*/)
  });
  it('should do stuff', () => {
    // component here has already been shallowMounted
  })
});

describe('SomeCompoent - process.env tests', () => {
  const REAL_ENV = process.env;
  let wrapper;

  beforeEach(/* ... */); // replace process.env
  afterEach(/* ... */); // restore process.env

  it('Does stuff with FOO = "bar"', () => {
    process.env.FOO = 'bar';
    wrapper = shallowMount(/* ... */);
    expect(/* wrapper to do stuff when process.env.FOO is 'bar' */)
  })

  it('Does stuff with FOO = "baz"', () => {
    process.env.FOO = 'baz';
    wrapper = shallowMount(/* ... */);
    expect(/* wrapper to do stuff when process.env.FOO is 'baz' */)
  })
})

1 As an example, you don't want to use axios in your tests. You don't want to test if the local machine is connected to the internet or if the servers your app is calling are actually up and running. You want a replacement for axios which does what axios is doing without making actual calls, allowing you to control the responses and errors returned, thus testing your component makes the correct calls and behaves as expected when receiving responses/errors from axios.

  • Related