Search code examples
angularunit-testingdependency-injectionkarma-jasmineangular16

Writing unit tests for simple Angular functions which using inject() method in Angular 16


After Angular CanActivate interface became deprecated, I've changed my guards for simple const functions based on official documentation. For example here is my inverseAuthGuard method, which seems working correctly:

export const inverseAuthGuard = (): boolean => {
  const authService = inject(AuthService);
  const router = inject(Router);
  if (authService.isAuthenticated()) {
    router.navigate(['/visual-check']);
    return false;
  }
  return true;
};

My problem is that, I want to write some unit tests for it and I don't know how can I inject a mock authService and a mockRouter into this function. I've watched this video, which explains how can I inject mock services into a class, but for my guard function I couldn't make it working.

I have tried some ways, but I couldn' find any solution. If I do this way:


describe('InverseAuthGuard', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, RouterTestingModule],
      providers: [
        { provide: AuthService, useValue: AuthService },
        { provide: Router, useValue: Router },
      ],
    });
  });

  fit('should return true on not authenticated user', () => {
    const result = inverseAuthGuard();
    expect(result).toBe(true);
  });
});

I've got the following error:

NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`

If I do that way, what I saw in the video:

describe('InverseAuthGuard', () => {
  const setupWithDI = (authService: unknown, router: unknown) =>
    TestBed.configureTestingModule({
      providers: [
        { provide: AuthService, useValue: authService },
        { provide: Router, useValue: router },
      ],
    }).inject(inverseAuthGuard);

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, RouterTestingModule],
    });
  });

  fit('should return true on not authenticated user', () => {
    const mockAuthService: unknown = { isAuthenticated: () => true };
    const mockRouter: Router = jasmine.createSpyObj(['navigate']);
    setupWithDI(mockAuthService, mockRouter);
    const result = inverseAuthGuard();
    expect(result).toBe(true);
  });
});

I've got the following error:

NullInjectorError: No provider for inverseAuthGuard!

Of course, I've tried providing inverseAuthGuard somehow, but without any success. I think there should be an easy solution for it, but I didn't find in any documentation. I will be thanksful for any answer.


Solution

  • you can run functions in the right injection context with the old setup that you had

    const result = TestBed.runInInjectionContext(() => inverseAuthGuard());