[Angular 7+]:如何在另一个服务规范中对服务进行单元测试?

内拉杰

我有这两个服务文件,其中一个包含在另一个文件中。

app.service.ts

    import { Injectable } from '@angular/core';
    import { HttpClient, HttpHeaders } from '@angular/common/http';

    import { throwError, Observable } from 'rxjs';

    import { catchError, map } from 'rxjs/operators';

    import { Router } from '@angular/router';

    import { environment } from '../../environments/environment';

    import { AuthService } from '../_services/auth.service';

    @Injectable({
        providedIn: 'root'
    })
    export class AppService {

        protected _apiURL = environment.apiURL;
        // Define URLs here
        _overviewURL: string;
        _deleteUserUrl: string;

        constructor(private http: HttpClient,
                    private _router: Router,
                    private _authService: AuthService) {
            this.setApiUrl();
        }

        /* Begin: Misc services */
        /**
         * @description Sets the header for each request
         * @param authorize Flag to whether include authorization token in headers or not
         * @returns - Header consisting of Authorization token & Content-type
         */
        setHeaders(authorize: boolean = false) {
            const headers: any = {};
            headers['Content-Type'] = 'application/json';

            if (authorize && this._authService.isAuthenticated()) {
                const authenticated = this._authService.getAuthenticatedUser();
                headers.Authorization = `Bearer ${authenticated.idToken}`;
            }
            return {
                headers: new HttpHeaders(headers)
            };
        }

        /**
         * @description Sets all the service URLs with the respective endpoints
         */
        setApiUrl() {
            this._overviewURL = this._apiURL + 'overview';
            this._deleteUserUrl = this._apiURL + 'user/delete';
        }

        /**
         * @description Gets the user overview page details based on BGtOccName & BGtID
         * @param params - consists of BGtOccName & BGtId (BG Occupation Name & BG Occupation ID).
         * Refer BG Docs: https://dev.burning-glass.com/docs/versions/3.3/getting-started
         */
        getOverviewPageInfo(params: any) {
            return this.http.post(this._overviewURL, params, this.setHeaders())
                .pipe(
                    map(this.handleResponse),
                    catchError(this.handleError)
                );
        }

        /**
         * @description Delete an authenticated user
         * @param user User object from localStorage
         */
        deleteUser(user: any) {
            return this.http.post(this._deleteUserUrl, user, this.setHeaders(true))
                .pipe(
                    map(this.handleResponse),
                    catchError(this.handleError)
                );
        }


        /**
         * @description processes observable response
         * @param res - takes in the response object
         * @returns - data object
         */
        private handleResponse = (res: any) => {
            return res.data || {};
        }

        /**
         * @description processes observable error
         * @param error - takes in the error object
         * @returns - error object
         */
        private handleError = (error: Response | any) => {
            console.error(error.error.message || error.message);
            const errorMsg = error.error.message || error.message;
            if (errorMsg === 'Invalid token') { this._router.navigate(['/login']); localStorage.removeItem('loggedUser'); }
            return throwError(error.error.error);
        }
    }

身份验证服务

    import { Injectable } from '@angular/core';
    import { Router } from '@angular/router';
    import { environment } from '../../environments/environment';
    import * as auth0 from 'auth0-js';

    import { ToastrService } from 'ngx-toastr';

    @Injectable({
        providedIn: 'root'
    })
    export class AuthService {

      private _idToken: string;
      private _accessToken: string;
      private _expiresAt: number;
      private auth0User: any;

      auth0 = new auth0.WebAuth({
        clientID: environment.AUTH0_CLIENTID,
        domain: environment.AUTH0_DOMAIN,
        responseType: 'token id_token',
        redirectUri: environment.AUTH0_REDIRECT_URI
      });

      constructor(  public router: Router,
                    private _toastr: ToastrService) {
        this.auth0User = JSON.parse(localStorage.getItem('auth0User'));
        this._idToken = (this.auth0User && this.auth0User.idToken) ? this.auth0User.idToken : '';
        this._accessToken = (this.auth0User && this.auth0User.accessToken) ? this.auth0User.accessToken : '';
        this._expiresAt = (this.auth0User && this.auth0User.expiresAt) ? this.auth0User.expiresAt : 0;
      }

      get accessToken(): string {
        return this._accessToken;
      }

      get idToken(): string {
        return this._idToken;
      }

      public login(): void {
        this.auth0.authorize();
      }

      public handleAuthentication(): void {
        this.auth0.parseHash((err, authResult) => {
          if (authResult && authResult.accessToken && authResult.idToken) {
            this.localLogin(authResult);
            this.router.navigate(['/home']);
            this._toastr.success(`You have been logged in!`, `Success`);
          } else if (err) {
            this.router.navigate(['/home']);
            this._toastr.error(`Invalid login`, `Failed`);
          }
        });
      }

      private localLogin(authResult): void {
        // Set the time that the access token will expire at
        const expiresAt = (authResult.expiresIn * 1000) + Date.now();
        this._accessToken = authResult.accessToken;
        this._idToken = authResult.idToken;
        this._expiresAt = expiresAt;
        let auth0User: any = localStorage.getItem('auth0User');
        if (auth0User) {
            auth0User = JSON.parse(auth0User);
            auth0User.idToken = authResult.idToken;
            auth0User.expiresAt = expiresAt;
            auth0User.accessToken = authResult.accessToken;
            localStorage.setItem('auth0User', JSON.stringify(auth0User));
        } else {
            localStorage.setItem('auth0User', JSON.stringify({
                idToken: authResult.idToken,
                expiresAt: expiresAt,
                accessToken: authResult.accessToken,
                idTokenPayload: authResult.idTokenPayload
            }));
        }
      }

      public renewTokens(): void {
        this.auth0.checkSession({}, (err, authResult) => {
           if (authResult && authResult.accessToken && authResult.idToken) {
             this.localLogin(authResult);
           } else if (err) {
             this._toastr.error(`Could not get a new token (${err.error}: ${err.error_description}).`, `Failed`);
             this.logout();
           }
        });
      }

      public logout(): void {
        // Remove tokens and expiry time
        this._accessToken = '';
        this._idToken = '';
        this._expiresAt = 0;
        localStorage.removeItem('auth0User');

        this.auth0.logout({
          returnTo: window.location.origin
        });
        this._toastr.success(`You have been logged out!`, `Success`);
      }

      public isAuthenticated(): boolean {
        // Check whether the current time is past the
        // access token's expiry time
        return this._accessToken && Date.now() < this._expiresAt;
      }

      public getAuthenticatedUser() {
          if (localStorage.getItem('auth0User')) {
              return JSON.parse(localStorage.getItem('auth0User'));
          } else {
              return null;
          }
      }

    }

每当我运行app.service.spec.ts测试文件时,它都会引发错误,该错误与任何代码无关。

app.service.spec.ts

    import { TestBed } from '@angular/core/testing';
    import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
    import { RouterTestingModule } from '@angular/router/testing';

    import { AppService } from './app.service';

    describe('AppService', () => {
        let service: AppService;
        let httpMock: HttpTestingController;

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

        beforeEach(() => {
            service = TestBed.get(AppService);
            httpMock = TestBed.get(HttpTestingController);
        });

        it('should be created', () => {
            expect(service).toBeTruthy();
        });

        afterEach(() => {
            httpMock.verify();
        });

        it('should retrieve overview details', () => {
            const postParams = {
                'designation': 'Fire Chief / Marshal',
                'id': '378'
            };

            const overviewDetails = {
                'data': {
                    'highSalary': 115511.77,
                    'estimatedSalary': 98935,
                    'nationalSalary': 98498.34,
                    'skills': [
                        {
                            'name': 'JavaScript Object Notation (JSON)',
                            'description': 'In computing, JavaScript Object Notation or JSON ( JAY-sn), is an open-standard ' +
                                            'file format that uses human-readable text to transmit data objects consisting of' +
                                            'attributevalue pairs and array data types (or any other serializable value).',
                            'count': 45084
                        },
                        {
                            'name': 'Software Architecture',
                            'description': 'Software architecture refers to the high level structures of a software system,' +
                            'the discipline of creating such structures, and the documentation of these structures.',
                            'count': 42676
                        }
                    ],
                    'careers': [
                        {
                            'name': 'Chief Executive Officer',
                            'meanSalaryDiff': 11347.74
                        },
                        {
                            'name': 'Database Architect',
                            'meanSalaryDiff': 7699.84
                        }
                    ]
                }
            };

            service.getOverviewPageInfo(postParams).subscribe(overview => {
                expect(overview).toEqual(overviewDetails.data);
            });

            const req = httpMock.expectOne(service._overviewURL);

            expect(req.request.method).toBe('POST');

            req.flush(overviewDetails);
        });
    });

但是,如果删除线

if (authorize && this._authService.isAuthenticated()) {
            const authenticated = this._authService.getAuthenticatedUser();
            headers.Authorization = `Bearer ${authenticated.idToken}`;
        }

从app.service.ts文件中,然后所有测试工作正常(如您所见,它正在调用authService函数)。

我试图将authService包含在app.service.spec.ts的提供程序中,如下所示,但是没有运气。:(

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

我的问题:如何在另一个可注射文件中包含/测试一个可注射(服务)?

DJ之家

我认为您需要为此提供一个模拟,除非您想进行集成测试。例如,您尝试仅通过其包括AuthServiceproviders: [...]列表中来进行修复的方法将无法工作,除非您还添加了必要的文件来构造自己的文件AuthService(即,您需要将其包括ToastrService在提供商列表中)。

我的建议是AuthService通过告诉测试TestBed使用模拟而不是实际模拟来模拟您的测试AuthService这是我所拥有的,它通过了测试:

// in app.service.spec.ts

//imports

describe('AppService', () => {
  let service: AppService;
  let httpMock: HttpTestingController;

  // Add your mock and all the methods/values you need.
  // You can Spy on this object if you need to change the return values 
  //  for different tests
  let mockAuthSerice: any = {
    isAuthenticated: () => true,
    getAuthenticatedUser: () => {
      return { user: 'bob', idToken: 'token' }
    },
  };

  beforeEach(() => TestBed.configureTestingModule({
    imports: [
      HttpClientTestingModule,
      RouterTestingModule
    ],
    // Provide AuthServide but tell Angular to use the mock instead
    providers: [
      AppService,
      { provide: AuthService, useValue: mockAuthSerice }
    ]
  }));

  // ...rest of test file

});

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在Laravel 7中将注册服务单例传递给另一个注册服务的构造函数?

Angular Karma单元测试:如何在业力配置中而不是在所有测试规范中注入模拟服务

另一个服务中的Angular2 Inject服务创建2个实例

类型具有私有属性模拟服务单元测试Angular 7的单独声明

单元测试Angular服务,该服务依赖于另一个使用InjectionToken的服务

如何使用Angular 7从另一个组件访问组件值

Angular中的单元测试服务

如何在angular 7中动态传递另一个组件中的组件

Angular 7 Jasmine单元测试:如何获取组件NativeElement?

如何在Angular 7(或2+)中编写文件上传方法的单元测试

重定向到Angular 7中的另一个组件后如何发送消息?

如何使用Angular 6/7将服务注入到另一个服务中?(providedIn:'root')服务始终是未定义的

如何在Angular 7中管理API函数调用服务

如何将表单组数据传递到另一个组件中的“提交”按钮(Angular 7)

如何在Angular 7中使用另一个for循环打印ID,名称或类型,而不是bird.ID或bird.Name?

如何在Angular中将数组从一个服务注入到另一个服务的数组中

如何对一个依赖于其注册回调的Angular服务进行单元测试?

如何在同一服务中从另一个角度调用一个Angular函数?

如何在 Angular 5 中将服务数据从一个组件传递到另一个组件

Angular 单元测试服务

单元测试 Angular 服务

在 angular 7 单元测试中测试转换

Angular 7 从组件导航到另一个

Angular 7 (RxJs6) - 单元测试 - 如何检查服务是否返回 throwError?

将组件导入 angular 7 中的另一个组件时出错

如何在 Angular 中为 HTTPClient get() 方法服务编写单元测试用例?

如何对angular7中具有@Input变量的组件进行单元测试?

如何在 Angular 中对 NGXS 进行单元测试?

如何在 angular 7 中使用 jasmine 和 karma 编写单元测试用例