0

There is a named export in the config.js which sets based on environment variable provided or a default value.

export const Product = process.env.product || 'X';

I want to test this function inside product.js for different Products like X, Y, Z.

import {Product} from './config.js';

export const testProduct(){
   if(Product === 'X') {
     return 'Product X';
   }
   if(Product === 'Y'){
     return 'Product Y';
   }
   if(Product === 'Z'){
     return 'Product Z'; 
   }
   return 'Invalid product';
}

Tests goes here,

import { testProduct } from './product.js';

//pass
it("should test testUser for defaullt Product", ()=> {
   const result = testProduct();
   expect(result).toBe('Product X')
});

//pass since product X is default
it("should test testUser for Product X", ()=> {
   const result = testProduct();
   expect(result).toBe('Product X')
});

//failed
it("should test testUser for Product Y", ()=> {
   const result = testProduct();
   expect(result).toBe('Product Y')
});

//failed
it("should test testUser for Product Z", ()=> {
   const result = testProduct();
   expect(result).toBe('Product Z')
});

I'm not looking for changing the environment variable. I know that is not possible. I want to test the function by mocking the Product value used in the testProduct function to have full test coverage.

nim007
  • 2,958
  • 3
  • 16
  • 28

2 Answers2

1

Modules that use the constant need to be re-imported after the constant is mocked. This requires to reset module cache, mock environment variable that affects the constant and restore it to not affect other tests:

const originalProduct = process.env.product;

beforeEach(() => {
  jest.resetModules();
});

afterEach(() => {
  process.env.product = originalProduct;
});
...
it("should test testUser for Product Y", ()=> {
   process.env.product = 'Y';
   const { testProduct } = require('./product.js');
   const result = testProduct();
   expect(result).toBe('Product Y')
});
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    Did you try it? The answer explains why it should work. The point is to re-import the module that needs to be affected by new value of env var. – Estus Flask Nov 06 '20 at 22:37
  • Yes, the Product is getting updated when reimported with require, but it is causing some side effects in the SpyOn and mocks in the API calls hence failing many other tests. Is there any way I can spyOn and mock the Product value inside the testProject execution, probably without changing the env variable? – nim007 Nov 10 '20 at 08:34
  • There's no spyOn in the code you posted. Can you explain what you mean? – Estus Flask Nov 10 '20 at 11:29
  • I also do have some fetch API calls inside the testProduct function. So for that, i'm spying on it and returning some mocks in other test cases for the testProduct. After the reimport as you suggested, those mocks are returning values from different mocks. jest.spyOn(window, 'fetch').mockAndReturnOnce(Promise.resolve("something")); – nim007 Nov 12 '20 at 17:58
  • A reimport can affect the way modules interact with each other, but it doesn't affect global `fetch` or its mock. I don't see how these things are related if tests were written correctly. In your case there can be race conditions or unmet expectations that mix `Once` mocks up. If the problem persists, consider asking a new question with https://stackoverflow.com/help/mcve that can reproduce the problem. As for your original question, the answer shows a common way to do this in Jest. I'd like to help but current problem appears to be very different from what was shown in the OP. – Estus Flask Nov 12 '20 at 22:04
0

You can convert Product into a function:

// config.js
export const Product = () => process.env.product || 'X';

And in your test you can spy on the Product function and mock a return value per test.

// test-file.js
import * as product from '../path/to/config.js'


describe('your test suite', () => {
  afterEach(() => {
    jest.clearAllMocks();
  }
  it("should return 'X'", () => {
     jest.spyOn(product, 'Product').mockReturnValue('X');
     const result = testProduct();
     expect(result).toBe('X');
  });

   it("should return 'Y'", () => {
     jest.spyOn(product, 'Product').mockReturnValue('Y');
     const result = testProduct();
     expect(result).toBe('Y');
  });
}
dev
  • 863
  • 7
  • 14
  • Hi, I'm looking for a solution without changing the real implementation in a large codebase. – nim007 Nov 06 '20 at 18:39
  • This may be what you're looking for https://stackoverflow.com/a/29002989/7389957. – dev Nov 06 '20 at 19:30