admin管理员组

文章数量:1405359

I've tried to search for answer to this problem for some time now and I failed. So I decided to give it a try here. If there is a such question already and I missed it, I'm sorry for duplicate.

Assume I have this javascript module 'myValidator.js', where are two functions and one function calls the other one.

export const validate = (value) => {
  if (!value.something) {
    return false
  }

  // other tests like that
  return true
}

export const processValue = (value) => {
  if (!validate(value)) {
    return null
  }

  // do some stuff with value and return something
}

Test for this like this. I want to test the validate function, whether is behaves correctly. And then I have the processValue function that invokes the first one and returns some value when validation is ok or null.

import * as myValidator from './myValidator'

describe('myValidator', () => {
  describe('validate', () => {
    it('should return false when something not defined', () => {
      ...
    }
  }

  describe('processValue', () => {
    it('should return something when value is valid', () => {
      const validateMock = jest.spyOn(myValidator, 'validate')
      validateMock.mockImplementation(() => true)
      expect(validate('something')).toEqual('somethingProcessed')
    }

    it('should return null when validation fails', () => {
      const validateMock = jest.spyOn(myValidator, 'validate')
      validateMock.mockImplementation(() => false)
      expect(validate('somethingElse')).toEqual(null)
    }
  }
}

Now, the problem is that this doesn't actually work as processValue() actually calls the function inside the module, because of the closure I suppose. So the function is not mocked as only the reference in exports is changed to jest mock, I guess.

I have found a solution for this and inside the module to use

if (!exports.validate(value)) 

That works for the tests. However we use Webpack (v4) to build the app, so it transforms those exports into its own structure and then when the application is started, the exports is not defined and the code fails.

What's best solution to test this?

Sure I can do it with providing some valid and invalid value, for this simple case that would work, however I believe it should be tested separately.

Or is it better to not mock functions and call it through to avoid the problem I have or is there some way how to achieve this with JavaScript modules?

I've tried to search for answer to this problem for some time now and I failed. So I decided to give it a try here. If there is a such question already and I missed it, I'm sorry for duplicate.

Assume I have this javascript module 'myValidator.js', where are two functions and one function calls the other one.

export const validate = (value) => {
  if (!value.something) {
    return false
  }

  // other tests like that
  return true
}

export const processValue = (value) => {
  if (!validate(value)) {
    return null
  }

  // do some stuff with value and return something
}

Test for this like this. I want to test the validate function, whether is behaves correctly. And then I have the processValue function that invokes the first one and returns some value when validation is ok or null.

import * as myValidator from './myValidator'

describe('myValidator', () => {
  describe('validate', () => {
    it('should return false when something not defined', () => {
      ...
    }
  }

  describe('processValue', () => {
    it('should return something when value is valid', () => {
      const validateMock = jest.spyOn(myValidator, 'validate')
      validateMock.mockImplementation(() => true)
      expect(validate('something')).toEqual('somethingProcessed')
    }

    it('should return null when validation fails', () => {
      const validateMock = jest.spyOn(myValidator, 'validate')
      validateMock.mockImplementation(() => false)
      expect(validate('somethingElse')).toEqual(null)
    }
  }
}

Now, the problem is that this doesn't actually work as processValue() actually calls the function inside the module, because of the closure I suppose. So the function is not mocked as only the reference in exports is changed to jest mock, I guess.

I have found a solution for this and inside the module to use

if (!exports.validate(value)) 

That works for the tests. However we use Webpack (v4) to build the app, so it transforms those exports into its own structure and then when the application is started, the exports is not defined and the code fails.

What's best solution to test this?

Sure I can do it with providing some valid and invalid value, for this simple case that would work, however I believe it should be tested separately.

Or is it better to not mock functions and call it through to avoid the problem I have or is there some way how to achieve this with JavaScript modules?

Share Improve this question asked Oct 2, 2018 at 14:38 DouglishDouglish 2934 silver badges15 bronze badges 3
  • Have you reached a solution? – David Commented May 7, 2019 at 10:35
  • I have not, I just test the function as a whole and I try to not mock the other function, however that works fine for simple functions, but when there is some plex logic, it's not ideal tho. – Douglish Commented May 9, 2019 at 14:05
  • have you tried something like myValidator .[functionName] = jest.fn(()=> return) ? – David Commented Jun 2, 2019 at 13:04
Add a ment  | 

1 Answer 1

Reset to default 8

I finally found the answer to this question. It's actually in the the Jest examples project on GitHub.

// Copyright 2004-present Facebook. All Rights Reserved.

/**
 * This file illustrates how to do a partial mock where a subset
 * of a module's exports have been mocked and the rest
 * keep their actual implementation.
 */
import defaultExport, {apple, strawberry} from '../fruit';

jest.mock('../fruit', () => {
  const originalModule = jest.requireActual('../fruit');
  const mockedModule = jest.genMockFromModule('../fruit');

  //Mock the default export and named export 'apple'.
  return {
    ...mockedModule,
    ...originalModule,
    apple: 'mocked apple',
    default: jest.fn(() => 'mocked fruit'),
  };
});

it('does a partial mock', () => {
  const defaultExportResult = defaultExport();
  expect(defaultExportResult).toBe('mocked fruit');
  expect(defaultExport).toHaveBeenCalled();

  expect(apple).toBe('mocked apple');
  expect(strawberry()).toBe('strawberry');
});

本文标签: unit testingJavaScript testsmocking function from same module with jestStack Overflow