#Real method being called instead of mocked one in Jest test

37 messages · Page 1 of 1 (latest)

blazing laurel
#

Attempting here to test the validateUser method of the AuthService service.

However, within the validateUser method, it contains a call to a function within UsersService, named user, which queries the database for a specific user. For the unit test to work without a database connection, I'm overriding this method within the providers array when creating the testing module.

The issue is that the mocked UsersService is not being called - the real method in the real service is the one that is actually being called.

My best guess is that this is something with dependencies - maybe I'm importing incorrectly and that means that the real one is being called? I'm at a loss here. Code is below.

let authService: AuthService;

  beforeEach(async () => {
    const userData = {
      id: 1,
      username: "John",
      email: "[email protected]",
      password: await bcrypt.hash("password", 10),
      verified: true
    };

    const module = await Test.createTestingModule({
      providers: [
        AuthService,
        {
          provide: UsersService,
          useValue: {
            user: jest.fn().mockResolvedValue(userData)
          }
        }
      ],
      imports: [
        AppModule,
        MailModule,
        HttpModule
      ]
    }).compile();

    authService = await module.get(AuthService);
  });

  describe("when validating a user", () => {
    it("should verify a hashed password successfully", async () => {
      const result = await authService.validateUser("", "password");
      expect(result).toBe(true);
    });
  });
vocal panther
#

You shouldn't add imports in your unit test, just fyi

blazing laurel
#

I get issues with dependency resolution if I don't import here.

vocal panther
#

What all does AuthService need to function?

blazing laurel
vocal panther
#

So something like

providers: [
  AuthService,
  {
    provide:UsersService,
    useValue: userMock,
  },
  {
    provide: JwtService,
    useValue: jwtMock,
  },
  {
    provide: HttpService,
    useVlaue: httpMock,
  },
  {
    provide: ConfigService,
    useVlaue: configMock,
  },
  {
    provide: getRedisToken(), // assuming this exists,
    useValue: redisMock,
  }
]
blazing laurel
#

Eventually yes, but I wanted to isolate it to this one test with usersService.

#

The validateUser function is only dependent on UsersService.

#

Is there anything special I need to do for the mocked UsersService to be the one that is used for this module?

vocal panther
#

I'm concerned about the AppMoudle import, because that's probably importing AuthModule which might mean that when you module.get(AuthService) you are getting the "live" version instead of the mocked one

blazing laurel
#

I had a suspicion about that too. I temporarily added that in.

#

I have a provider named MailConfig within MailModule that sets the configuration for SMTP.

#

Without the AppModule import,

#

Error: Nest can't resolve dependencies of the MailConfig (?). Please make sure that the argument ConfigService at index [0] is available in the MailModule context.

vocal panther
#

Why does MailModule need to be imported?

blazing laurel
#

I'm not sure, it was just something that fixed a dependency issue. Was throwing stuff to see what would stick.

#

If I limit my imports to just HttpModule, it then throws issues with my JwtModule, which is globally declared in AppModule.

#

For example, this compiles dependencies successfully: ```ts
imports: [
AppModule,
HttpModule
]

#

With just the HttpModule import, this is then the issue: Error: Nest can't resolve dependencies of the AuthService (UsersService, ?, HttpService, ConfigService, RedisModule:default). Please make sure that the argument JwtService at index [1] is available in the RootTestModule context.

#

My JwtModule is globally declared in my AppModule's imports, so it's strange as to why it couldn't find JwtService:

#
{
    ...JwtModule.registerAsync({
        useFactory: (configService: ConfigService) => ({
            secret: configService.get("JWT_TOKEN"),
            signOptions: {
                expiresIn: "14d"
            }
        }),
        inject: [ConfigService]
    }),
    global: true
}
vocal panther
#

Why not get rid of the AppModule and import the necessary modules to the improts. I mean, I really suggest mocking them out in the first place, but if you're gonna import things why not import smaller things rather than the whole application?

blazing laurel
vocal panther
#

Again, I would avoid importing the app module in your test, so yes, you'd need to import that global module to the test.

#

And I really can't stress enough how much I would avoid imports in unit tests in the first place

blazing laurel
#

Not sure if you would know how, but how would I go about using a decorator for one of the dependencies?

#

Redis needs to have a @InjectRedis() attached to it when in the constructor, and it appears that simply using the pure Redis object without anything else in the providers array does not work.

vocal panther
#

What's the package you use for redis?

blazing laurel
#

Apologies for the ghost ping liao

#

IORedis, with the decorator from @ liaoliaots/nestjs-redis

#

Found a workaround - you can pass a redisService and then call getClient() on that.

#

Didn't realize that was an applicable method, thought the decorator was the only way.

vocal panther
#

Try REDIS_CLIENTS imported from that package

blazing laurel
#

Ended up with my providers array looking like this, no imports needed.