单元测试 CanActivateFn

问题描述 投票:0回答:3

在 Angular 中,我如何为实现这样的

CanActivateFn
的东西编写单元测试用例?

export const AuthorizedUserClaimGuard: CanActivateFn = (route, state) => {
  const auth = inject(AuthenticationService);
  const config = inject(ConfigurationService);

  if (!auth.authenticated) {
    auth.setState({
      route: state.url,
    });
    auth.logout();
    // tslint:disable-next-line: no-console
    console.info('User is not authenticated, redirecting to the login page.');
  }

  const userClaims = config.authorizedClaims;

  for (const userClaim of userClaims) {
    if (auth.hasPermission(userClaim)) {
      return true;
    }
  }
  return false;
};

我尝试了以下代码,但它不起作用,因为

AuthorizedUserClaimGuard
不是一个类。有没有办法让它像这样工作?

describe('AuthorizedUserClaimGuard', () => {
  let guard: AuthorizedUserClaimGuard;
  let authentication: AuthenticationService;
  let configuration: ConfigurationService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        {
          provide: AuthenticationService,
          useClass: MockAuthenticationService,
        },
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    authentication = TestBed.get(AuthenticationService);
    configuration = TestBed.get(ConfigurationService);
    configuration.setConfiguration(MOCK_CONFIGURATION);
    guard = new AuthorizedUserClaimGuard(authentication, configuration);
  });

  it('should return true for a logged in user', () => {
    const route = createMockRoute();
    const state = createMockRouteState();
    expect(guard.canActivate(route, state)).toBeTruthy();
  });
}
angular unit-testing testing karma-jasmine
3个回答
1
投票

您必须使用

TestBed.runInInjectionContext
来执行该函数,这样
inject
才能工作。

const result = await TestBed.runInInjectionContext(() => 
    AuthorizedUserClaimGuard(route, state));

完整代码:

import {
  TestBed,
} from '@angular/core/testing';
import {AuthorizedUserClaimGuard,
  AuthenticationService,
  ConfigurationService} from './guard';
import { HttpClientTestingModule } from '@angular/common/http/testing';
  
describe('AuthorizedUserClaimGuard', () => {
  let guard: any;
  let authentication: AuthenticationService;
  let configuration: ConfigurationService;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
      ],
    }).compileComponents();
  });

  beforeEach(() => {
    authentication = TestBed.inject(AuthenticationService);
    configuration = TestBed.inject(ConfigurationService);
    configuration.setConfiguration({});
  });

  it('should call logout when not authenticated and return false', async () => {
    spyOn(authentication, 'logout');
    const route: any = {};
    const state: any = {};
    const result = await TestBed.runInInjectionContext(() => AuthorizedUserClaimGuard(route, state));
    expect(authentication.logout).toHaveBeenCalled();
    expect(result).toBe(false);
  });

  it('should call true when permissions present', async () => {
    authentication.authenticated = true;
    configuration.authorizedClaims = [{}];
    const route: any = {};
    const state: any = {};
    const result = await TestBed.runInInjectionContext(() => AuthorizedUserClaimGuard(route, state));
    expect(result).toBe(true);
  });
});

Stackblitz 演示


0
投票

是的,它不是一个,它是一个函数。你不需要声明它。但是,由于您包含注入,因此您需要在注入上下文中运行您的函数。

describe('AuthorizedUserClaimGuard', () => {
  let authentication: AuthenticationService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        {
          provide: AuthenticationService,
          useClass: MockAuthenticationService,
        },
      ],
    });
  }));

  it('should return true for a logged in user', () => {
    const guardResult = TestBed.runInInjectionContext(() = AuthorizedUserClaimGuard(createMockRoute(), createMockRouteState()));
    expect(guardResult).toBeTruthy();
  });
}

guardResult 类型可能因您的实现而异。您有同步结果,可以直接比较。之前的回答中的纳伦·穆拉里 (Naren Murali) 预计结果将是一个承诺。如果这是一个可观察的结果,我建议将结果与弹珠进行比较。


0
投票

使用

TestBed.runInInjectionContext()
是关键(如其他答案中指出的),但这里有一个解决方案,它看起来更像您当前的测试设置,并且可以在多个测试用例中重复使用:

describe('AuthorizedUserClaimGuard', () => {
  let guard: CanActivateFn;
  let authentication: AuthenticationService;
  let configuration: ConfigurationService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        {
          provide: AuthenticationService,
          useClass: MockAuthenticationService,
        },
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    authentication = TestBed.get(AuthenticationService);
    configuration = TestBed.get(ConfigurationService);
    configuration.setConfiguration(MOCK_CONFIGURATION);
    guard = (...params) => TestBed.runInInjectionContext(() =>
      AuthorizedUserClaimGuard(...params));
  });

  it('should return true for a logged in user', () => {
    const route = createMockRoute();
    const state = createMockRouteState();
    expect(guard(route, state)).toBeTruthy();
  });
}
© www.soinside.com 2019 - 2024. All rights reserved.