Nest can't resolve dependencies in the RootTestModule context when I use Bazel Test
Asked Answered
B

3

28

I need to run my tests in the bezel. how can I solve this mysterious problem?

I have a nestjs project contains multiple apps and libs. When I run the test yarn jest --config ./jest.config.json libs/lib1, it works perfectly. However when I run with bezel bazel test //libs/lib1/... it gives me an error "Nest can't resolve dependencies ... Please make sure that the argument dependency at index ... is available in the RootTestModule context.".

REPO

https://github.com/smhmayboudi/bazel_jest_nestjs

I find out that the order of mapping at jest.config.json is important. this one works well ( shows test + coverage ), but dependency problem

  "moduleNameMapper": {
    "@melo/lib1": "<rootDir>/libs/lib1/src",
    "@melo/lib1/(.*)": "<rootDir>/libs/lib1/src/$1",
  },

this one works ( show just pass message with out actual test result and coverage !? )

  "moduleNameMapper": {
    "@melo/lib1/(.*)": "<rootDir>/libs/lib1/src/$1",
    "@melo/lib1": "<rootDir>/libs/lib1/src",
  },

Jest Config

{
  "coverageReporters": ["lcov", "text-summary"],
  "moduleNameMapper": {
    "@melo/libs1": "<rootDir>/libs/libs1/src",
    "@melo/libs1/(.*)": "<rootDir>/libs/libs1/src/$1",
  },
  "modulePathIgnorePatterns": ["/bazel-out/", "/node_modules/"],
  "preset": "ts-jest",
  "testEnvironment": "node"
}

Bazel Config

ts_library(
    name = "lib1_test_ts_library",
    srcs = glob(["*spec.ts"]),
    runtime = "nodejs",
    deps = [
        ":lib1_ts_library",
        "@npm//@nestjs/common",
        "@npm//@nestjs/testing",
        "@npm//@types/jest",
        "@npm//rxjs",
        "@npm//ts-jest",
    ],
)

jest_test(
    name = "lib1_jest_test",
    srcs = glob(["*spec.ts"]),
    jest_config = "//:jest.config.json",
    deps = [
        ":lib1_test_ts_library",
    ],
    coverage = True,
)

Error Log

INFO: Invocation ID: 84f45d55-c6e4-4c2a-b05d-367d0f84baf7
INFO: Analyzed target //libs/lib1/src:lib1_jest_test (633 packages loaded, 19569 targets configured).
INFO: Found 1 test target...
WARNING: failed to create one or more convenience symlinks for prefix 'dist/':
  cannot create symbolic link bazel-out -> /Users/WHOAMI/Developer/MY_PROJECT/bazel-out/execroot/melo/bazel-out:  /Users/WHOAMI/Developer/MY_PROJECT/bazel-out (File exists)
FAIL: //libs/lib1/src:lib1_jest_test (see /Users/WHOAMI/Developer/MY_PROJECT/bazel-out/execroot/melo/bazel-out/darwin-fastbuild/testlogs/libs/lib1/src/lib1_jest_test/test.log)
INFO: From Testing //libs/lib1/src:lib1_jest_test:
==================== Test output for //libs/lib1/src:lib1_jest_test:
PASS libs/lib1/src/lib1.util.spec.ts (23.866 s)
PASS libs/lib1/src/lib1.interceptor.spec.ts (23.977 s)
FAIL libs/lib1/src/lib1.service.spec.ts (24.717 s)
  ● ApmService › should be defined

    Nest can't resolve dependencies of the ApmService (?). Please make sure that the argument dependency at index [0] is available in the RootTestModule context.

    Potential solutions:
    - If dependency is a provider, is it part of the current RootTestModule?
    - If dependency is exported from a separate @Module, is that module imported within RootTestModule?
      @Module({
        imports: [ /* the Module containing dependency */ ]
      })

      at Injector.resolveSingleParam (../../../../../../../../node_modules/@nestjs/core/injector/injector.js:134:19)
      at resolveParam (../../../../../../../../node_modules/@nestjs/core/injector/injector.js:102:49)
          at Array.map (<anonymous>)
      at Injector.resolveConstructorParams (../../../../../../../../node_modules/@nestjs/core/injector/injector.js:117:58)
      at Injector.loadInstance (../../../../../../../../node_modules/@nestjs/core/injector/injector.js:81:20)
      at Injector.loadProvider (../../../../../../../../node_modules/@nestjs/core/injector/injector.js:38:20)
      at ../../../../../../../../node_modules/@nestjs/core/injector/instance-loader.js:43:62
          at Array.map (<anonymous>)
      at InstanceLoader.createInstancesOfProviders (../../../../../../../../node_modules/@nestjs/core/injector/instance-loader.js:43:36)
      at ../../../../../../../../node_modules/@nestjs/core/injector/instance-loader.js:28:24

  ● ApmService › should be defined

    expect(received).toBeDefined()

    Received: undefined

      56 | 
      57 |   it("should be defined", () => {
    > 58 |     expect(service).toBeDefined();
         |                     ^
      59 |   });
      60 | 
      61 |   it("start should be called", () => {

      at Object.<anonymous> (libs/lib1/src/lib1.service.spec.ts:58:21)

...

Test Suites: 2 failed, 2 passed, 4 total
Tests:       27 failed, 3 todo, 6 passed, 36 total
Snapshots:   0 total
Time:        26.102 s
Ran all test suites within paths "libs/lib1/src/lib1.decorator.spec.ts", "libs/lib1/src/lib1.interceptor.spec.ts", "libs/lib1/src/lib1.service.spec.ts", "libs/lib1/src/lib1.util.spec.ts".
================================================================================
Target //libs/lib1/src:lib1_jest_test up-to-date:
  dist/bin/libs/lib1/src/lib1_jest_test.sh
  dist/bin/libs/lib1/src/lib1_jest_test_loader.js
  dist/bin/libs/lib1/src/lib1_jest_test_require_patch.js
INFO: Elapsed time: 83.878s, Critical Path: 59.53s
INFO: 4 processes: 4 local.
INFO: Build completed, 1 test FAILED, 12 total actions
//libs/lib1/src:lib1_jest_test                                             FAILED in 28.2s
  /Users/WHOAMI/Developer/MY_PROJECT/bazel-out/execroot/melo/bazel-out/darwin-fastbuild/testlogs/libs/lib1/src/lib1_jest_test/test.log

INFO: Build completed, 1 test FAILED, 12 total actions
Beaman answered 16/7, 2020 at 19:22 Comment(0)
P
17

Add providers to your RootTestModule. Nest doesn't automatically include the services in your test, depending on if you used the cli vs creating the files/folders directly.

const module: TestingModule = await Test.createTestingModule({
      providers: [/** Services goes here **/],
      controllers: [CustomersController],
}).compile();

Not working vs Working below

import { Test, TestingModule } from '@nestjs/testing';
import { CustomersController } from './customers.controller';

describe('CustomersController', () => {
  let controller: CustomersController;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [CustomersController],
    }).compile();

    controller = module.get<CustomersController>(CustomersController);
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  it('shoud return customer', async () => {
    const tcase = await controller.d({});
    expect(tcase).toHaveProperty('firstName');
  })
});

Working (I had the exact error message with different filenames ofcourse)

import { Test, TestingModule } from '@nestjs/testing';
import { CustomersController } from './customers.controller';
import { CustomersService } from './customers.service';

describe('CustomersController', () => {
  let controller: CustomersController;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [CustomersService],
      controllers: [CustomersController],
    }).compile();

    controller = module.get<CustomersController>(CustomersController);
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  it('shoud return customer', async () => {
    const tcase = await controller.d({});
    expect(tcase).toHaveProperty('firstName');
  })
});
Policlinic answered 27/10, 2020 at 1:44 Comment(2)
got the same error, but not working even with this.Fawcette
Same here, still got the same problem even with the service in providers.Conglomeration
I
7

The solution that worked for me was following the unit test examples of the library.

import { Test, TestingModule } from '@nestjs/testing';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

const createUserDto: CreateUserDto = {
  firstName: 'firstName #1',
  lastName: 'lastName #1',
};

describe('UsersController', () => {
  let usersController: UsersController;
  let usersService: UsersService;

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [UsersController],
      providers: [ //  <---------- THIS IS THE MOST IMPORTANT SECTION TO SOLVE THIS ISSUE.
        {
          provide: UsersService,
          useValue: {
            create: jest
              .fn()
              .mockImplementation((user: CreateUserDto) =>
                Promise.resolve({ id: '1', ...user }),
              ),
            findAll: jest.fn().mockResolvedValue([
              {
                firstName: 'firstName #1',
                lastName: 'lastName #1',
              },
              {
                firstName: 'firstName #2',
                lastName: 'lastName #2',
              },
            ]),
            findOne: jest.fn().mockImplementation((id: string) =>
              Promise.resolve({
                firstName: 'firstName #1',
                lastName: 'lastName #1',
                id,
              }),
            ),
            remove: jest.fn(),
          },
        },
      ],
    }).compile();

    usersController = app.get<UsersController>(UsersController);
    usersService = app.get<UsersService>(UsersService);
  });

  it('should be defined', () => {
    expect(usersController).toBeDefined();
  });

  describe('create()', () => {
    it('should create a user', () => {
      expect(usersController.create(createUserDto)).resolves.toEqual({
        id: '1',
        ...createUserDto,
      });
      expect(usersService.create).toHaveBeenCalled();
      expect(usersService.create).toHaveBeenCalledWith(createUserDto);
    });
  });

  describe('findAll()', () => {
    it('should find all users ', () => {
      usersController.findAll();
      expect(usersService.findAll).toHaveBeenCalled();
    });
  });

  describe('findOne()', () => {
    it('should find a user', () => {
      usersController.findOne('1');
      expect(usersService.findOne).toHaveBeenCalled();
      expect(usersController.findOne('1')).resolves.toEqual({
        firstName: 'firstName #1',
        lastName: 'lastName #1',
        id: '1',
      });
    });
  });

  describe('remove()', () => {
    it('should remove the user', () => {
      usersController.remove('2');
      expect(usersService.remove).toHaveBeenCalled();
    });
  });
});

The key change was to update the 'providers' property to use an object instead of just using: providers: [CustomersService],.

Implicatory answered 7/12, 2022 at 18:17 Comment(1)
Do you know why this works?Earplug
P
2

A potential workaround is to declare "empty"

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
  providers: [{
    provide: CustomerService,
    useValue: {},
  }],
  controllers: [CustomersController],
}).compile();
Pokelogan answered 28/9, 2022 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.