simenandre / nestjs-envalid

Simple wrapper on top of envalid for NestJS
Apache License 2.0
25 stars 1 forks source link

Any sample on how to use this package in unit tests? #93

Open BabakScript opened 2 months ago

BabakScript commented 2 months ago

Hi @simenandre ,

Any idea how I can use this package in my unit tests? I prefer to check the functionality of my service function in different modes with different values for my environment variables.

simenandre commented 2 months ago

Are you looking to change configuration that, ie. mock the functionality of this package?

What I mean is, are there controllers and services etc. that you are using this package, where you want to define the values for?

In that case, you can set the configuration manually. I believe I have a few examples of this in the tests here, but I haven't checked (I answered you directly without checking the code base).

That said, even if it isn't really easy, it should be pretty easy to implement if needed. Tell me how you proceed!

Always happy to accept PRs that makes this package better for everyone, so there's that as well 👍

BabakScript commented 2 months ago

I have a nestjs service in a module and now want to test different paths of my code based on my environment variable. For normal environment variables I usually get a copy of current env variables, then fake the variables I want and then copy the backup values again to process.env something similar to this:

describe('environmental variables', () => {
  const OLD_ENV = process.env;

  beforeEach(() => {
    jest.resetModules() // Most important - it clears the cache
    process.env = { ...OLD_ENV }; // Make a copy
  });

  afterAll(() => {
    process.env = OLD_ENV; // Restore old environment
  });

  test('will receive process.env variables', () => {
    // Set the variables
    process.env.NODE_ENV = 'dev';
    process.env.PROXY_PREFIX = '/new-prefix/';
    process.env.API_URL = 'https://new-api.com/';
    process.env.APP_PORT = '7080';
    process.env.USE_PROXY = 'false';

    const testedModule = require('../../config/env').default

    // ... actual testing
  });
});

But I don't know how to do it with envalid and this package. As far as I understand as this package provides a read-only wrapper on top of values there is no set function available to set the values in the tests.

simenandre commented 2 months ago

envalid is immutable by design, but I see how you want in the nestjs situation want to have a easy function that you can set the environment.

How about this: We create a test constructor, which just sets the environment or we find a way to set what object to pass to envalid.

Do you wanna take a stab at it?

BabakScript commented 2 months ago

I personally prefer the second approach to pass an object to envalid for tests. I'll check it in my free time and keep you updated.

BabakScript commented 2 months ago

OK This is my second try using @golevelup/ts-jest and nestjs-envalid:

import { createMock } from "@golevelup/ts-jest";
import { ENVALID, EnvalidModule } from "nestjs-envalid";
import { Config } from "../../config";

describe("MyService", () => {
  let service: MyService;
  let envMock: Config;

beforeEach(async () => {

    envMock = createMock<Config>({
    // Cache is disabled for all tests by default except for the test cases where we want to test the cache
      CACHE_ENABLED: false,
    });

    onst module: TestingModule = await Test.createTestingModule({
      imports: [],
      providers: [
        MyService,
        // Always use mock for the environment variables
        { provide: ENVALID, useValue: envMock },
      ],
    }).compile();

    service = module.get<MyService>(MyService);
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

    describe("Test the cache", () => {
      it("Cache is Enabled", async () => {
            jest
          .spyOn(envMock, "CACHE_ENABLED", "get")
          .mockReturnValueOnce(true);

           ....

      });
      it("Cache is Disabled", async () => {
        log CACHE_ENABLED here

           ....

      });

But I'm getting this error:

    Property `CACHE_ENABLED` does not have access type get

Here in this line:

jest
          .spyOn(envMock, "CACHE_ENABLED", "get")
          .mockReturnValueOnce(true);

But it seems the mock value is working because I have correct value from here:

envMock = createMock<Config>({
    // Cache is disabled for all tests by default except for the test cases where we want to test the cache
      CACHE_ENABLED: false,
    });
simenandre commented 2 months ago

It probably is better to change this library, do you want write access or do you want to fork it?

BabakScript commented 1 month ago

Let me check to see how we can achieve this. I'll probably send a PR for it. Thanks for your response.