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

Unable to run tests because Nest can't resolve dependencies of a service #363

Closed
mogusbi opened this issue Jan 18, 2018 · 54 comments
Closed

Comments

@mogusbi
Copy link

mogusbi commented Jan 18, 2018

I'm submitting a...


[ ] Regression 
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

I have followed the unit test example but I am unable to get my very simple test to run (it's literally just testing if true === true) but it won't work because I'm met with this error

Nest can't resolve dependencies of the RoleService (?). Please verify whether [0] argument is available in the current context.

Minimal reproduction of the problem with instructions

You can find the repo at https://bitbucket.org/mogusbi/breeze-bb/

After cloning, run npm run api:test

// role.controller.spec.ts
import {Test} from '@nestjs/testing';
import {TestingModule} from '@nestjs/testing/testing-module';
import {RoleController} from './role.controller';
import {RoleService} from './role.service';

describe('Role controller', () => {
  let controller: RoleController;
  let service: RoleService;

  beforeEach(async () => {
    const mod: TestingModule = await Test
      .createTestingModule({
        components: [
          RoleService
        ],
        controllers: [
          RoleController
        ]
      })
      .compile();

    controller = mod.get<RoleController>(RoleController);
    service = mod.get<RoleService>(RoleService);
  });

  it('should be true', () => {
    expect(true).toBe(true);
  });
});

// role.controller.ts
import {Controller} from '@nestjs/common';
import {RoleService} from './role.service';

@Controller('role')
export class RoleController {
  constructor (
    private readonly roleService: RoleService
  ) {}

  ...
}

// role.service.ts
import {InjectModel} from '@nestjs/mongoose';
import {PaginateModel} from 'mongoose';
import {IRole} from './role.interface';
import {RoleSchema} from './role.schema';

@Component()
export class RoleService {
  constructor (
    @InjectModel(RoleSchema) private readonly model: PaginateModel<IRole>
  ) {}

  ...
}

Environment


- Node version: 8.2.1
- Platform:  Mac OS 10.13.2

├── @nestjs/[email protected]
├── @nestjs/[email protected]
├── @nestjs/[email protected]
├── @nestjs/[email protected]
├── @nestjs/[email protected]
├── @types/[email protected]
├── @types/[email protected]
├── @types/[email protected]
├── @types/[email protected]
├── @types/[email protected]
├── @types/[email protected]
├── @types/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]
@tonivj5
Copy link

tonivj5 commented Jan 18, 2018

I suppose it's failing because in your RoleService you are injecting an mongoose model, which is not including in the testing module. You should create a mock or inject the real one

@mogusbi
Copy link
Author

mogusbi commented Jan 18, 2018

@xxxTonixxx It's not clear how you would inject it, I've tried the following

  beforeEach(async () => {
    const mod: TestingModule = await Test
      .createTestingModule({
        components: [
          RoleService
        ],
        controllers: [
          RoleController
        ],
        imports: [
          MongooseModule.forFeature([
            {
              name: 'Role',
              schema: RoleSchema
            }
          ])
        ]
      })
      .compile();

    controller = mod.get<RoleController>(RoleController);
    service = mod.get<RoleService>(RoleService);
  });

but it only yields this error; Nest can't resolve dependencies of the useFactory (?). Please verify whether [0] argument is available in the current context.

@fwoelffel
Copy link
Contributor

fwoelffel commented Jan 19, 2018

That is because the MongooseModule.forFeature(...) needs to inject your Connection object. You should either add MongooseModule.forRoot(...) to your imports or mock your RoleSchema.

EDIT: See @shane-melton's response

I you want to mock the RoleSchema (which is the best option), try :

beforeEach(async () => {
  const mod: TestingModule = await Test
    .createTestingModule({
      components: [
        RoleService
      ],
      controllers: [
        RoleController
      ],
      imports: [
        {
          provide: 'RoleSchemaModel',
          useValue: {
            // Your mock
          }
        }
      ]
    })
    .compile();
  controller = mod.get<RoleController>(RoleController);
  service = mod.get<RoleService>(RoleService);
});

If you're wondering why provide: 'RoleSchemaModel' have a look at this ;)

EDIT: Missing link
https://github.com/nestjs/mongoose/blob/master/lib/mongoose.utils.ts#L3

@mogusbi
Copy link
Author

mogusbi commented Jan 19, 2018

@fwoelffel look at what? My apologies if this appears to be a bit of stupid question but what would a mocked schema look like

Once I can get this working I'll put in a pull request to include a test in the mongoose module example

@fwoelffel
Copy link
Contributor

fwoelffel commented Jan 20, 2018

look at what? My apologies if this appears to be a bit of stupid question

The only stupid thing here is me forgetting to c/c the link... Here it is https://github.com/nestjs/mongoose/blob/master/lib/mongoose.utils.ts#L3

According to your code, and my understanding (I've never used Mongoose), your mock should look like a PaginateModel<IRole>.

@kamilmysliwiec
Copy link
Member

kamilmysliwiec commented Jan 21, 2018

Hi,
Yeah, that's my mistake, there's no example in the documentation about how to mock repository/model. I'll update it soon. In the meantime, @shane-melton shows the best solution 🙂

@shane-melton
Copy link

@mogusbi Not sure if you got @fwoelffel's solution to work yet; but I had to put the mock in components instead of imports to get things working properly. Something like the following:

beforeEach(async () => {
    const mod: TestingModule = await Test
        .createTestingModule({
            components: [
                RoleService,
                {
                    provide: 'RoleSchemaModel',
                    useValue: {
                        // Your mock
                    }
                }
            ],
            controllers: [
                RoleController
            ],
        }
]
})
.compile();
    controller = mod.get<RoleController>(RoleController);
    service = mod.get<RoleService>(RoleService);
});

@VinceOPS
Copy link

VinceOPS commented Jan 24, 2018

For people encoutering the same error message with TypeORM, please note that the DI loader cannot "know" your Repository instances as they're injected with @InjectRepository:

export class DogsService {
  constructor(@InjectRepository(Dog) private readonly dogsRepository: Repository<Dog>) {
    // ...
  }
}

I had more or less the same issue this morning and solved it by injecting "DogsRepository" manually:

Test.createTestingModule({
  components: [
    {
      provide: 'DogsRepository',
      useClass: Repository,
    }
  ],
  // ...
}).compile();

However, I would recommend extending Repository with a custom repo Class (class DogsRepository extends Repository<Dog>) and use it in useClass (useClass: DogsRepository, instead of useClass: Repository), in order to be able to test the type of values returned by the repository instance (if you need to).

@kamilmysliwiec: does everything sound correct? Or am I mistaken somewhere? Shall I add it do the documentation?

@mogusbi
Copy link
Author

mogusbi commented Feb 1, 2018

I'm having no luck at all with this, I still can't work out how to mock the schema and even using the actual schema results in an error regardless of whether it's included in components or imports

    const mod: TestingModule = await Test
      .createTestingModule({
        components: [
          RoleService
        ],
        controllers: [
          RolesController
        ],
        imports: [
          {
            provide: 'RoleSchemaModel',
            useValue: RoleSchema
          }
        ]
      })
      .compile();

results in TypeError: Object prototype may only be an Object or null: undefined while

    const mod: TestingModule = await Test
      .createTestingModule({
        components: [
          RoleService,
          {
            provide: 'RoleSchemaModel',
            useValue: RoleSchema
          }
        ],
        controllers: [
          RolesController
        ]
      })
      .compile();

results in Error: Nest can't resolve dependencies of the RoleService (?). Please verify whether [0] argument is available in the current context.

@ebadia
Copy link

ebadia commented Feb 3, 2018

@VinceOPS I'm having this issue with TypoORM, but cannot see what exactly I shall do.

I have my UsersService with this code:

@Component()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepo: Repository<User>
  ) {}
//...

I written this in the spec file for the test:

//...
 beforeEach( async () => {
    const module = await Test.createTestingModule({
      components: [ UsersService, {
        provide: User,
        useClass: Repository
      } ],
      controllers: [ UsersController ],
    }).compile()
//...

But I have no clear what I need to write in provide and userClass to avoid the message

Error: Nest can't resolve dependencies of the UsersService (?). Please verify whether [0] argument is available in the current context.

@VinceOPS
Copy link

VinceOPS commented Feb 3, 2018

@ebadia Hi. Please have a look at this.
Give a token (name of the injected dependency) as provide (here, I use the default one: <entityName>Repository (e.g. "UserRepository"), and the class to use to instantiate this dependency in useClass (here, you want to inject an instance of "Repository" for your UsersService).

@samueleresca
Copy link

Hi all,
I have tried by following these steps:

  1. Extending the Repository class:
import {Repository} from "typeorm";
import {Label} from "../Models/Label";

export class LabelsRepository extends Repository<Label>{

}
  1. Declaring it on my dependency installer:
 const module = await Test.createTestingModule({

            controllers: [LabelsController],
            components: [{
                provide: 'LabelsRepository',
                useClass: LabelsRepository,
            }, LabelsService
            ]
        }).compile();
  1. Injecting it in my service constructor:
  private readonly labelRepository: LabelsRepository;

    constructor(@Inject('LabelsRepository')
                    labelRepository: LabelsRepository) {
        this.labelRepository = labelRepository;
    }

But I still have an error:

_Nest can't resolve dependencies of the LabelsService (?). Please verify whether [0] argument is available in the current context._

Am I wrong?

@VinceOPS
Copy link

VinceOPS commented Feb 13, 2018

Hi @samueleresca

The default injection token (for TypeORM repositories) is <entityName>Repository. In your case, LabelRepository, not LabelsRepository.

@samueleresca
Copy link

Hi @VinceOPS ,

It still not working, I have tried both, by extending Repository<Label> using LabelRepository class, and also by simply referring Repository.

Where am I doing wrong? Here you can find the repository:
https://github.com/samueleresca/Blog.NestGettingStarted

@asperling
Copy link

Hey!

I have almost the same situation as @samueleresca but with mongoose models. Neither registering a mock as component nor importing it resolves it.

Since almost every class to test somehow depends on a mongoose model I cannot write any tests. After reading through this issue I can't provide anything new. I made sure to update all dependencies to the latest version (4.6.4 in case of nestjs).

So does anyone has a working example?

@vanor89
Copy link

vanor89 commented Mar 3, 2018

Hey guys trying to write some tests for mongoose models as well, having no luck with this, im getting TypeError: Object prototype may only be an Object or null: undefined

@ws456999
Copy link

ws456999 commented Mar 13, 2018

i have a same problem

@superstarmcawesome
Copy link

same here..

using...
https://github.com/EasyMetrics/nestjs-api-seed
and then simply following
https://docs.nestjs.com/techniques/sql

Nest can't resolve dependencies of the DemoService (?). Please verify whether [0] argument is available in the current context.

@psaunders
Copy link

psaunders commented Mar 22, 2018

Is there any way at all to make this error message more helpful? Listing what has been included would be really good. I've been able to get to the bottom of it sometimes, but I've also just had to roll back my code and do something else. Also he was asking about database testing, can we get an example that doesn't use mocks?

@fmeynard
Copy link

fmeynard commented Apr 8, 2018

In the documentation "Mongoose.forRoot" is imported in the Application module, this module is not imported during the creation the TestModule leading to this error as Mongoose.forFeature won't be able to find the connection initialized by Mongoose.forRoot.

You can declare "Mongoose.forRoot()" in your Test.createTestModule() statement or use a similar as me :

( please note that i have multiple database in my real project thats why i have a DatabaseModule )

Example

DatabaseModule

@Module({
    imports: [MongooseModule.forRoot('mongodb://localhost')],
})
export class DatabaseModule {}

CatModule

@Module({
    imports: [MongooseModule.forFeature([{ name: 'Cat', schema: CatSchema }])], // note that database module is not imported here
    controllers: [CatController],
    components: [CatService, CatRepository],
})
export class CatModule {}

ApplicationModule ( for "normal" execution )

@Module({
  imports: [DatabaseModule, CatModule],
  controllers: [],
  components: [],
})
export class ApplicationModule {}

cat.e2e-spec.ts

    // ...
    beforeAll(async () => {
        const module = await Test.createTestingModule({
            imports: [DatabaseModule, CatModule],
        })
        .compile();
        // ....
    });

@ganchikov
Copy link

Can someone help me to understand the issue? i've created an example of simple app with 2 modules (dummy using nest/mongoose and cats using custom provider as per the doc: https://github.com/ganchikov/nest_mongo_ut_sample

Both modules have tests:

  • dummy has the test as per @fwoelffel suggestion above
  • cats has the e2e test copied from the doc :

the app itself is successfully executed, but when I run the test I get the following error for both modules:
`FAIL src\nest-mongoose\dummy.controller.spec.ts
● Test suite failed to run

TypeError: mongoose.Schema is not a constructor

  at Object.<anonymous> (nest-mongoose/dummy.schema.ts:2:43)
  at Object.<anonymous> (nest-mongoose/dummy.service.ts:24:14)
  at Object.<anonymous> (nest-mongoose/dummy.controller.spec.ts:11:14)
      at Generator.next (<anonymous>)
      at new Promise (<anonymous>)
  at handle (../node_modules/worker-farm/lib/child/index.js:44:8)
  at process.<anonymous> (../node_modules/worker-farm/lib/child/index.js:51:3)
  at emitTwo (../events.js:126:13)
  at process.emit (../events.js:214:7)
  at emit (../internal/child_process.js:772:12)
  at _combinedTickCallback (../internal/process/next_tick.js:141:11)
  at process._tickCallback (../internal/process/next_tick.js:180:9)

`
and

`FAIL src\nest-provider\cats.controller.spec.ts
● Test suite failed to run

TypeError: mongoose.Schema is not a constructor

  at Object.<anonymous> (nest-provider/cat.schema.ts:2:39)
  at Object.<anonymous> (nest-provider/cats.providers.ts:1:252)
  at Object.<anonymous> (nest-provider/cats.module.ts:11:14)
  at Object.<anonymous> (nest-provider/cats.controller.spec.ts:13:13)
      at Generator.next (<anonymous>)
      at new Promise (<anonymous>)
  at handle (../node_modules/worker-farm/lib/child/index.js:44:8)
  at process.<anonymous> (../node_modules/worker-farm/lib/child/index.js:51:3)
  at emitTwo (../events.js:126:13)
  at process.emit (../events.js:214:7)
  at emit (../internal/child_process.js:772:12)
  at _combinedTickCallback (../internal/process/next_tick.js:141:11)
  at process._tickCallback (../internal/process/next_tick.js:180:9)

`
I've played the whole day yesterday trying different options including suggestions from this thread: #438

The result is always the same :( What i am missing?

@AlbertoNitro
Copy link

@ganchikov also i have the same problem exactly... Any solution please ?

@periman2
Copy link

periman2 commented Apr 22, 2018

This is how I fixed it in my case:
I wanted to create a test case using a test database and not mock data.

So this is what I did (I'm using mocha but it should be the same with jest):

describe('Customer', function() {
    this.timeout(22000);
    const server = express();

    before(async () => {
        const module = await Test.createTestingModule({
            imports: [
                testDatabase([Customer, Channel]),
                TypeOrmModule.forFeature([Customer, Channel])],
            controllers: [CustomerController],
            components: [CustomerService]
        })
        .compile();

        const app = module.createNestApplication(server);
        await app.init();
    });

    it(`/GET customer/all`, () => {
        return request(server)
            .get('/customer/all')
            .expect(200);
    });
}); 

And in testDatabse file :

export const testDatabase = (entities) => {
    return TypeOrmModule.forRoot({
        type: "mssql",
        url: "databaseurlhere",
        entities: entities || [],
        options: {
            encrypt: true
        },
        synchronize: false
    })
}

Works fine so far and it's kind of amazing that it can access the database only using a portion of the entities that the db has!

So basically recreate the exact module in the test (Without importing the module itself) but before this add the connection to your desired database first. I used this simple functional wrapper to do that, you can do it your own way.

I like this approach even if it has a bit more work because it lets you see what entities are connected with the entity you're testing just by looking at the test's imports.

I hope this works with mongodb too.

@ChenShihao
Copy link

@ganchikov +1. Waiting for solution...

@kamilmysliwiec
Copy link
Member

kamilmysliwiec commented Apr 26, 2018

This is explained in the new version of the documentation: https://docs.nestjs.com/v5/

  1. Techniques -> Database (Testing section)
  2. Techniques -> Mongo (Testing section)

@madelfresno
Copy link

@ganchikov @ChenShihao @AlbertoNitro I have the same problem. Any idea??

@creadicted
Copy link

Does one has a idea to fix that?
It can not be that there is no working sample to unit test a component or sample with this setup. I found multiple sample projects on GH but they all have no tests.

@bitflut
Copy link

bitflut commented Aug 2, 2018

I think you've run into the same issue as me. In order to use jest for testing, nest uses ts-jest to compile test files. Somehow ts-jest doesn't seem to handle synthetic default imports properly. As soon as I turned it off all tests passed.

I've created a separate tsconfig for jest:

// tsconfig.jest.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "allowSyntheticDefaultImports": false
  }
}

And configured ts-jest to use this in package.json:

// package.json
...
  "jest": {
    ...
    "globals": {
      "ts-jest": {
        "tsConfigFile": "./tsconfig.jest.json"
      }
    },
    ....
...

Don't forget to also turn this off for e2e tests. By default, nest creates another configuration file for it under test/jest-e2e.json. I also added the globals object there:

...
"globals": {
      "ts-jest": {
        "tsConfigFile": "./tsconfig.jest.json"
      }
},
...

And voila, everything's running.
Hopefully that'll work for the rest of us too.

@danil-z
Copy link

danil-z commented Sep 26, 2018

Do u have to implement findOne on your const mockRepository ?
I'm getting

TypeError: this.userRepository.findOne is not a function

@billytianlan
Copy link

Any update on this issue?

@jkchao
Copy link

jkchao commented Oct 16, 2018

Example: https://github.com/jkchao/blog-service/blob/nest/src/module/auth/__test__/auth.spec.ts

@quezak
Copy link

quezak commented Nov 30, 2018

I'll leave a note, since this issue comes up first in most google searches about mocks / unit tests with Typeorm in Nest.

TL;DR see the example below, or the corrected link from @jkchao above: the https://github.com/jkchao/blog-service/blob/nest/src/module/auth/__test__/auth.service.spec.ts

The recommended testing strategy (mentioned in the docs), to add { provides: getRepositoryToken(...), useWhatever } works nicely for simple modules. The problem is when your tested module imports any other module, that depends (maybe indirectly) on any other db providers. Then, you get an error, because the other modules imports TypeOrmModule.forFeature(...), which in turn "depends" on TypeOrmModule.forRoot:

    Nest can't resolve dependencies of the YourEntity2Repository (?). Please verify whether [0] argument is available in the current context.

You can, of course, recursively list all the required imported providers in your call to createTestingModule, and add all the mock db providers the same way, but it quickly becomes unmaintainable. A much simpler solution is to just import the whole tested module, and then use .overrideProvider for each DB entity that the module uses (directly or not):

        const testingModule = await Test.createTestingModule({ imports: [YourTestedModule] })
            .overrideProvider(getRepositoryToken(YourEntity1))
            .useValue(...)
            .overrideProvider(getRepositoryToken(YourEntity2))
            .useValue(...)
            .compile();

Maybe it's worth mentioning this approach in the "Techniques > Database > Testing" docs? It took me a surprisingly long time looking for other workarounds (including mocking the db connection itself inside TypeOrmModule), until I found out about .overrideProvider.

@trainerbill
Copy link

Do u have to implement findOne on your const mockRepository ?
I'm getting

TypeError: this.userRepository.findOne is not a function

@danil-z did you find an answer to this?

@quezak
Copy link

quezak commented Nov 30, 2018

@trainerbill if your mockRepository object doesn't have all the methods it should have, it will fail if your services actually call the repository. You should either implement the method to return from some mock dataset, or mock the actual calls. This is what I'm currently using for the mock repos -- an object with the repo functions I use set to fail if used:

function mockRepositoryProvider<T extends /*my base entity type*/>(entity: Type<T>): ValueProvider {
    const mkFailFn = (name: string) => () => {
        throw new Error(`unexpected/unmocked call to Repository<${entity.name}>.${name}`);
    };
    const mockRepo: Partial<Repository<T>> = [
        'find',
        'findOne',
        'findOneOrFail',
        'save',
        'delete',
        'remove',
    ].reduce(
        (acc, fnName) => {
            acc[fnName] = mkFailFn(fnName);
            return acc;
        },
        {},
    );
    return {
        provide: getRepositoryToken(entity),
        useValue: mockRepo,
    };
}

Then, for each entity my test module needs, I do .overrideProvider(...) like I wrote above. Finally, in the unit tests, I mock the return values for the repositories, for example:

        const someRepo = testingModule.get<Repository<SomeEntity>>(getRepositoryToken(SomeEntity));
        jest.spyOn(someRepo, 'findOneOrFail').mockReturnValueOnce(someEntity)

@violettomsk
Copy link

Hey guys!

I've read the testing part of the database documentation but I could not find any hint or best practice for creating the mockRepository object.

I would like to write tests where I mock the database layer of the service (e.g.: the adminRepository of the AuthService) by providing good ol' POJOs as data source. Is this achievable with the current version of TypeORM / Nest?

There is a related conversation in typeorm/typeorm#1267, but I couldn't get closer to my desired functionality.

My AuthService looks like this:

// auth.service.ts

@Injectable()
export class AuthService {

  constructor(
    @InjectRepository(Admin)
    private readonly adminRepository: Repository<Admin>,
  ) { }

  findByEmail(email: string): Promise<Admin> {
    return this.adminRepository.findOne({ where: { email } });
  }

  findById(id: number): Promise<Admin> {
    return this.adminRepository.findOne(id);
  }

}

The desired output would look like something like this:

// auth.service.spec.ts

describe('AuthService', () => {
  let authService: AuthService;

  const mockRepository = {
    data: [
      { id: 1, email: '[email protected]', password: '' },
      { id: 2, email: '[email protected]', password: '' },
    ],
  };

  beforeEach(async () => {
    const module = await Test.createTestingModule({
        providers: [
          AuthService,
          {
            provide: getRepositoryToken(Admin),
            useValue: mockRepository,
          },
        ],
      }).compile();

    authService = module.get<AuthService>(AuthService);
  });

  describe('findByEmail', () => {
    it('should return an Admin with a valid email', async () => {
      const email = '[email protected]';
      const expected = mockRepository.data[1];
      const admin = await authService.findByEmail(email);
      expect(admin).toBe(expected);
    });
  });

});

I really look forward to hear some advices and tips about the pros/cons of this kind of testing strategy, my primary goal is to keep my unit tests as simple and as fast as I can. I really appreciate any help!

@pterblgh pt
This topic is old, but in your case, you need to retrieve your repo by adding:

let authRepository = new Repository<Admin>(getRepositoryToken(Admin));
...
authService = module.get<AuthService>(AuthService);
authRepository = module.get<Repository<Admin>>(getRepositoryToken(Admin));

@trainerbill
Copy link

I spent a ton of time on finding a solution here so I am going to post what I came up with. Its a combination of @quezak solution and providing a mock value.

import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { getRepositoryToken } from "@nestjs/typeorm";
import * as request from 'supertest';
import { UsersModule } from './../src/modules/users/users.module';
import { UserEntity } from "./../src/modules/users/entities/user.entity";

describe('UserController (e2e)', () => {
  let app: INestApplication;

  const mockUser = new UserEntity({
    email: '[email protected]'
  });

  beforeAll(async () => {
    
    const moduleFixture = await Test.createTestingModule({
      imports: [
        UsersModule
      ]
    })
      .overrideProvider(getRepositoryToken(UserEntity))
      .useValue({
        find: () => mockUser,
        findOne: () => mockUser,
        save: () => mockUser,
      })
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

@fadeojo
Copy link

fadeojo commented Jan 22, 2019

I think for me, I had two questions.

  1. How do you test with a test DB

Solution:
user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User)
        private readonly userRepository: Repository<User>,
    ) {}

    async findAll(): Promise<User[]> {
        return await this.userRepository.find();
    }
}

user.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';

describe('UserService', () => {
    let service: UserService;

    beforeAll(async () => {
        const module: TestingModule = await Test.createTestingModule({
            imports: [
                TypeOrmModule.forRoot(),
                TypeOrmModule.forFeature([User]),
            ],
            providers: [UserService],
        }).compile();
        service = module.get<UserService>(UserService);
    });
    it('should be defined', () => {
        expect(service).toBeDefined();
    });
});

  1. How do you test with a mocked repository

Solution:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User)
        private readonly userRepository: Repository<User>,
    ) {}

    async findAll(): Promise<User[]> {
        return await this.userRepository.find();
    }
}

user.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { UserRepository } from './user.repository';
import { UserService } from './user.service';

describe('UserService', () => {
    let service: UserService;

    beforeAll(async () => {
        const module: TestingModule = await Test.createTestingModule({
            providers: [
                UserService,
                {
                    provide: 'UserRepository',
                    useValue: UserRepository,
                },
            ],
        }).compile();
        service = module.get<UserService>(UserService);
    });
    it('should be defined', () => {
        expect(service).toBeDefined();
    });
});

user.repository.ts

import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';

@EntityRepository(User)
export class UserRepository extends Repository<User> {}

@mazyvan
Copy link

mazyvan commented Jan 23, 2019

I can't make tests work using Custom repository.

Having this in my service's constructor:

  constructor(
    @InjectRepository(UserRepository)
    private readonly userRepository: UserRepository,
  ) {}

Tests does not work. But without the UserRepository class. I can test the service as well as @pterblgh

Here is a extract of the service's test

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      // imports: [TypeOrmModule.forFeature([User, UserRepository])],
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useValue: mockRepository,
        },
      ],
    }).compile();
    service = module.get<UserService>(UserService);
  });

And the test's output:

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

@quezak
Copy link

quezak commented Jan 23, 2019

@mazyvan adding a custom provider directly in the module works only sometimes. To be sure it works, use the overrideProvider construct mentioned above (for example in my previous comment)

@raschan
Copy link

raschan commented Jan 30, 2019

@mazyvan I ran into the same problem, here is what i found to work for me:
My custom repository:

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  public async findOneByEmail(email: string): Promise<User> {
    return this.findOne({ where: { email } });
  }
}

My Service constructor

export class LoginService {
  constructor(
    @InjectRepository(UserRepository)
    private readonly userRepository: UserRepository,
  ) {}
// ...
}

and the spec file:

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        LoginService,
        { provide: getRepositoryToken(UserRepository), useValue: mockRepository },
      ],
    }).compile();
    service = module.get<LoginService>(LoginService);
  });

Notice that I get the repositoryToken for UserRepository instead of User

@rankery
Copy link

rankery commented Mar 7, 2019

Trying to use it for 3 days now.

I'm getting error during creating testing Module:

    beforeAll(async () => {
        module = await Test.createTestingModule({
            imports: [
                UserModule
            ],
            controllers: [AuthController],
            providers: [
            ]
        })
            .overrideProvider(getRepositoryToken(User))
            .useValue({})
            .compile()
        this.authController = module.get<AuthController>(AuthController)
    });

Tryed a lot of variants but still the same error in injected file. With postman it all works good. So is there any fix for this?

Error:

TypeError: Cannot read property 'prototype' of undefined
export class UserService {
constructor(@InjectRepository(User)
...
at Object.getRepositoryToken

There is my project

takayukioda added a commit to takayukioda/nestjs-typeorm that referenced this issue Mar 13, 2019
Thanks to the following issue and comment!
nestjs/nest#363 (comment)

Further ToDo:
- Make more generic mock class to ease injections
takayukioda added a commit to takayukioda/nestjs-typeorm that referenced this issue Mar 13, 2019
Thanks to the following issue and comment!
nestjs/nest#363 (comment)

Further ToDo:
- Make more generic mock class to ease injections
@takayukioda
Copy link

thanks for having this issue!
the solution in #363 (comment) saved my life :)

One note that I needed to put mock into providers instead of imports.

@kiwikern
Copy link
Contributor

This issue was referenced in a question on Stackoverflow. Here is my solution: https://stackoverflow.com/a/55366343/4694994

@xjrcode
Copy link

xjrcode commented Mar 31, 2019

@kiwikern I got this error with your code:

Type 'Mock<{ findOne: Mock<any, any[]>; }, []>' is not assignable to type '() => MockType<Repository<any>>'.
      Type '{ findOne: Mock<any, any[]>; }' is missing the following properties from type 'MockType<Repository<any>>': manager, metadata, createQueryBuilder, target, and 19 more

@kiwikern
Copy link
Contributor

kiwikern commented Mar 31, 2019

@Xavi-dev As it says, your mock definition is not complete. That's why in the example there is // @ts-ignore telling the typescript compiler to ignore the missing properties error in your mock. Alternatively, you can either remove the type definition or define all 23 missing properties.

@Nosfistis
Copy link
Contributor

@kiwikern I got this error with your code:

Type 'Mock<{ findOne: Mock<any, any[]>; }, []>' is not assignable to type '() => MockType<Repository<any>>'.
      Type '{ findOne: Mock<any, any[]>; }' is missing the following properties from type 'MockType<Repository<any>>': manager, metadata, createQueryBuilder, target, and 19 more

You can avoid typescript errors if you mention that your mock is partial:

export type MockType<T> = {
  [P in keyof T]?: jest.Mock<{}>;
};

@kunal-relan
Copy link

@VinceOPS I'm having this issue with TypoORM, but cannot see what exactly I shall do.

I have my UsersService with this code:

@Component()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepo: Repository<User>
  ) {}
//...

I written this in the spec file for the test:

//...
 beforeEach( async () => {
    const module = await Test.createTestingModule({
      components: [ UsersService, {
        provide: User,
        useClass: Repository
      } ],
      controllers: [ UsersController ],
    }).compile()
//...

But I have no clear what I need to write in provide and userClass to avoid the message

Error: Nest can't resolve dependencies of the UsersService (?). Please verify whether [0] argument is available in the current context.

Did you figure this out? if yes can you give an example in the same context.
Thanks in advance

@raschan
Copy link

raschan commented May 21, 2019

@kunal-relan
In this case something like the following should work:

UserService

@Component()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepo: Repository<User>
  ) {}
//...

The spec file:

import { getRepositoryToken } from '@nestjs/typeorm';
//...
 beforeEach( async () => {
    const module = await Test.createTestingModule({
      components: [ UsersService, {
        provide: getRepositoryToken(User),
        useClass: Repository
      } ],
      controllers: [ UsersController ],
    }).compile()
//...

I'm not sure about the useClass: Repository part, I would mock that out,
but the provide part has to look like above

@wu0211
Copy link

wu0211 commented Jun 19, 2019

How to Solve the Problem

@lock
Copy link

lock bot commented Sep 23, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 23, 2019
@kamilmysliwiec
Copy link
Member

For those that are repeatedly struggling with the "Cannot resolve dependency" error, check out the NestJS Devtools (read more here).

Example:

image

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests