#Trying to understand how to use createTestingModule

1 messages · Page 1 of 1 (latest)

humble lintel
#

I have a very simple hello world example service - i want to test it, passing in our own stubbed config service (to show how the thing works).

import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class HelloWorldService {
  private readonly logger = new Logger(HelloWorldService.name);

  private logPrefix = '';

  constructor(private readonly configService: ConfigService) {
    this.logger.log('HelloWorldService constructor', { configService });
    console.log('HelloWorldService constructor', { configService });

    this.logPrefix = this.configService.get('HelloWorldService.logPrefix') ?? ' x ';
    this.logger.log('logPrefix', { logPrefix: this.logPrefix });

    console.log('HelloWorldService constructor', { logPrefix: this.logPrefix });
  }

  /**
   * Returns a greeting message
   * @param name - The name to greet
   * @returns A greeting string
   */
  hello(name = 'World'): string {
    const response = `${this.logPrefix}Hello, ${name}!`;
    this.logger.log(response);
    return response;
  }

  /**
   * Returns a farewell message
   * @param name - The name to bid farewell to
   * @returns A farewell string
   */
  goodbye(name = 'World'): string {
    const response = `${this.logPrefix}Goodbye, ${name}!`;
    this.logger.log(response);
    return response;
  }
}

Yet, nothing i've tried has worked. Seems like nothing is passed to the service, as i always see:

HelloWorldService constructor { configService: undefined }

  • sample spec in first comment.
#
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import * as sinon from 'sinon';
import { it, describe, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert';
import { HelloWorldService } from './helloworld.service';

describe('HelloWorldService', () => {
  let service: HelloWorldService;
  let configServiceStub: sinon.SinonStubbedInstance<ConfigService<Record<string, unknown>, false>>;

  beforeEach(async () => {
    // Create a stubbed instance of ConfigService
    configServiceStub = sinon.createStubInstance(ConfigService<Record<string, unknown>, false>);
    configServiceStub.get.returns(' test ');

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        HelloWorldService,
        {
          provide: ConfigService,
          useValue: configServiceStub,
        },
      ],
    }).compile();

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

  afterEach(() => {
    sinon.restore();
  });

  it('should be defined', () => {
    assert(service !== undefined);
  });
});

What am i doing wrong?

thorny forum
#

This looks correct to me, where seems to be the problem?

#

Ah, I see now. The way you're re-assigning the sinon instance can't work

#

Because the DI container is created once with the value of ConfigService provider with the original, undefined, configServiceStub

#

When you later re-assign it, it has no effect on the original value

#

You should assign the stub instance directly on variable initialization and only then set method mocks on the mock instance, but not re-assign it.

humble lintel
#

actually, its not that (well it could be) the bigger issue, was i was trying to run the tests using node:test intead of Jest... turns out if you do that the DI just fails!!

#

i've just tested and using jest, it works fine - but using sinon, it fails when the constructor tries to access the injected config service, which is undefined.

#

so odd.

#

but thank you for the reply papooch !

thorny forum
#

Hm, I haven't yet tried the new node's test runner, but it worked for me with both jest and mocha. I'm not sure how Node's test runner works with typescript, but as long as it generates the correct decorator metadata, it should work. My gut tells me it's a configuration issue, unless it's just not supported at all.

humble lintel
#

im trying to build a test for it now - will share ones its ready

#

could also be something in my monorepo / config is messed up - so this is useful debugging

thorny forum
#

Node's built-in TS transpilation doesn't support emitting metadata, hence, DI and anything metadata-related won't work as expected.
There's your answer. If you want to run test via node's test runner, you'll need to compile them to javascript yoursel first

humble lintel
#

Right - so the documentation needs updating?

#

i lost several days to this - nowhere does it say it won't work with node:test - so we spent many hours feeling gaslit by the framework / documentation / tutorials.