Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@nestjs/testing doesn't work with vitest #9228

Closed
3 of 15 tasks
wight554 opened this issue Feb 18, 2022 · 8 comments
Closed
3 of 15 tasks

@nestjs/testing doesn't work with vitest #9228

wight554 opened this issue Feb 18, 2022 · 8 comments
Labels
needs triage This issue has not been looked into

Comments

@wight554
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

When trying to run vitest with @nestjs/testing, it throws errors due to UserService being undefined inside of UserController. I tried doing deep digging into nestjs, comparing jest and vitest but can't really get enough info for debugging. Decided to create this issue to help tracking/fixing vitest-dev/vitest#708

Minimum reproduction code

https://github.com/wight554/vitest-nestjs-testing

Steps to reproduce

npx vitest run src/users/users.controller.vitest.spec.ts

Expected behavior

vitest should work with @nestjs/testing

Package

  • I don't know. Or some 3rd-party package
  • @nestjs/common
  • @nestjs/core
  • @nestjs/microservices
  • @nestjs/platform-express
  • @nestjs/platform-fastify
  • @nestjs/platform-socket.io
  • @nestjs/platform-ws
  • @nestjs/testing
  • @nestjs/websockets
  • Other (see below)

Other package

vitest

NestJS version

8.2.3

Packages versions

 _   _             _      ___  _____  _____  _     _____
| \ | |           | |    |_  |/  ___|/  __ \| |   |_   _|
|  \| |  ___  ___ | |_     | |\ `--. | /  \/| |     | |
| . ` | / _ \/ __|| __|    | | `--. \| |    | |     | |
| |\  ||  __/\__ \| |_ /\__/ //\__/ /| \__/\| |_____| |_
\_| \_/ \___||___/ \__|\____/ \____/  \____/\_____/\___/


[System Information]
OS Version     : macOS Monterey
NodeJS Version : v14.19.0
NPM Version    : 6.14.16 

[Nest CLI]
Nest CLI Version : 8.1.5 

[Nest Platform Information]
platform-express version : 8.2.3
schematics version       : 8.0.5
sequelize version        : 8.0.0
testing version          : 8.2.3
common version           : 8.2.3
core version             : 8.2.3
cli version              : 8.1.5

Node.js version

v14.19.0

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

No response

@wight554 wight554 added the needs triage This issue has not been looked into label Feb 18, 2022
@jmcdo29
Copy link
Member

jmcdo29 commented Feb 18, 2022

vitest uses vite and vite uses esbuild which doesn't support decorators. There's not much Nest can do about this.

Vite pre-bundles dependencies using esbuild. Esbuild is written in Go and pre-bundles dependencies 10-100x faster than JavaScript-based bundlers.

@jmcdo29 jmcdo29 closed this as completed Feb 18, 2022
@wight554
Copy link
Author

@jmcdo29 that's not it, I could even test custom decorators with vitest and use decorators in tests
https://github.com/wight554/blog-template/blob/master/test/server/decorators/UserDecorator.test.ts
https://github.com/wight554/blog-template/blob/interceptors-test/test/server/interceptors/MongooseClassSerializerInterceptor.test.ts#L10
issue is somewhere deeper and I'm not too familiar with nestjs internal apis to dig into it hence why asking for help

@wight554
Copy link
Author

Also that's the only problematic case with nest, if you check comments in vitest issue, it works fine when using test module for mocking providers of service, but not for controller

@jmcdo29
Copy link
Member

jmcdo29 commented Feb 18, 2022

It is actually the problem, believe it or not. Esbuild has partial decorator support, but it does not support emitDecoratorMetadata, which is how Nest automagically knows how to inject providers based on classes. With the @Exclude(), class-transformer is reading metadata specifically set for the property, not anything about the property type. For the @InjectModel(), once again, metadata is manually set for the parameter so Nest knows the injection token to use. If we were to create a new service Users2Service, like so:

import { Injectable } from "@nestjs/common";

@Injectable()
export class Users2Service {
  foo(): string {
    return "foo";
  }
}

And inject it into the UsersService and add a user2Foo to call the foo method

@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User)
    private readonly userModel: typeof User,
    private readonly user2: Users2Service
  ) {}

  user2foo(): string {
    return this.user2.foo();
  }
...
}

and then create a users.service.vitest.spec.ts like so:

import { Test, TestingModule } from "@nestjs/testing";
import { User } from "./models/user.model";
import { UsersService } from "./users.service";
import { getModelToken } from "@nestjs/sequelize";
import { Users2Service } from "./user2.service";

import { beforeEach, describe, it, expect } from "vitest";
import * as vi from "vitest";

const usersArray = [
  {
    firstName: "firstName #1",
    lastName: "lastName #1",
  },
  {
    firstName: "firstName #3",
    lastName: "lastName #2",
  },
];

const oneUser = {
  firstName: "firstName #1",
  lastName: "lastName #1",
};

describe("UserService", () => {
  let service: UsersService;
  let model: typeof User;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UsersService,
        {
          provide: getModelToken(User),
          useValue: {
            findAll: vi.fn(() => usersArray),
            findOne: vi.fn(),
            create: vi.fn(() => oneUser),
            remove: vi.fn(),
            destroy: vi.fn(() => oneUser),
          },
        },
        {
          provide: Users2Service,
          useValue: {
            foo: () => "test foo",
          },
        },
      ],
    }).compile();

    service = module.get<UsersService>(UsersService);
    model = module.get<typeof User>(getModelToken(User));
  });

  it("should be defined", () => {
    expect(service).toBeDefined();
  });

  describe("user2foo", () => {
    it("should return test foo", () => {
      vi.expect(service.user2foo()).toBe("test foo");
    });
  });
});

And run it using npx vitest run src/users/users.service.vitest.spec.ts we'll get the error that

 FAIL  src/users/users.service.vitest.spec.ts > UserService > user2foo > should return test foo
TypeError: Cannot read properties of undefined (reading 'foo')
 ❯ UsersService.user2foo src/users/users.service.ts:16:22
     14|
     15|   user2foo(): string {
     16|     return this.user2.foo();
       |                      ^
     17|   }
     18|
 ❯ src/users/users.service.vitest.spec.ts:104:24

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

Test Files  1 failed (1)
     Tests  1 failed | 1 passed (2)
      Time  1.64s (in thread 17ms, 9434.40%)

Meaning that Nest was not able to inject the Users2service because it doesn't know what type to use.


You can use @Inject() on every one of your dependencies to set the token yourself, so that Nest isn't trying to read the metadata emitted from the decorator, or you can use a tool that doesn't use esbuild, because it will not support this feature.

@wight554
Copy link
Author

Thanks much, now it's crystal clear for me. Will see how can this be fixed from vite side

@gabriiels
Copy link

@wight554
Does vitest work in nestjs?

@hiphapis
Copy link

@gabriiels you can see this doc. https://docs.nestjs.com/recipes/swc#vitest
but.. i have same problem also

@kamilmysliwiec
Copy link
Member

You can use Vitest with Nest as long as you have the SWC plugin enabled https://docs.nestjs.com/recipes/swc#vitest

@nestjs nestjs locked and limited conversation to collaborators Jun 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
needs triage This issue has not been looked into
Projects
None yet
Development

No branches or pull requests

5 participants