繁体   English   中英

测试返回 Observable 的 Angular 服务方法

[英]Testing an Angular service method which returns an Observable

我正在尝试让以下 Angular 12.6 服务的所有线路都变为绿色:我有这个测试组

describe('AuthService', () => {
  let service: AuthService;
  let apiResponse: ApiResponse;
  let controller: HttpTestingController;
  let accountDetails: AccountDetails;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: AuthInterceptor,
          multi: true,
        },
        { provide: APP_CONFIG, useValue: AppConfig },
      ],
    });
    service = TestBed.inject(AuthService);
    controller = TestBed.inject(HttpTestingController);
    apiResponse = {
      accessToken: 'Gimme access'
    };
    accountDetails = {
      Password: 'bla',
      UserName: 'vlad',
    };
  });

/// a few other tests below
  it('should call the back-end', () => {
    spyOn(service, 'setSession');
    spyOn(service, 'login').and.callThrough();

    service.login(accountDetails.UserName, accountDetails.Password).subscribe(() => {
      expect(service.setSession).toHaveBeenCalled();
    });

    expect(service.login).toHaveBeenCalledOnceWith('vlad', 'bla');

    controller.match(AppConfig.apiEndPoint);
  });
});

这作为服务方法:

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  #storage = localStorage;
  options = new HttpHeaders({
    'Content-Type': 'application/json',
  });

  private static handleError(error: HttpErrorResponse) {
    if (error.status === 0) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong.
      console.error(`Backend returned code ${error.status}, body was: `, error.error);
    }
    // Return an observable with a user-facing error message.
    return throwError('Something happened; please try again later.');
  }

  constructor(@Inject(APP_CONFIG) private config: IapConfig, private http: HttpClient) {}

  login(customerKey: string, username: string, password: string): Observable<any> {
    const timestamp = new Date().getTime();

    return this.http
      .post<AccountDetails>(this.config.apiEndPoint, {
        Password: password,
        UserName: username,
      })
      .pipe(catchError(AuthService.handleError))
      .pipe(map((response: AccountDetails) => {
        this.setSession(response);
        return response;
      }))
  }

  setSession(data: ApiResponse | any): void {
    try {
      // tslint:disable-next-line:no-console
      console.info('User session is set 😁');
    } catch (error) {
      throw new Error(`Incorrect response structure. Unable to map: ${error}`);
    }
  }
}


一:不知何故,我感觉我并没有真正测试login方法中的setSession方法调用。 第二我如何测试 catch 块中的行?

我如何在没有 mocking 的情况下模拟测试通过 pipe 运算符登录返回的 Observable。 我无法模拟和检查的登录方法订阅下的 setSession 是用正确的 arguments 调用的。

在阅读了@AliF50推荐的测试文章 Angular - 测试服务后,我感谢他。 我在我的测试文件中提出了以下内容:

import { TestBed } from '@angular/core/testing';

import { AuthService } from './auth.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { APP_CONFIG, AppConfig } from '../app.config';
import { AccountDetails, ApiResponse } from '../interfaces';

describe('AuthService', () => {
  let service: AuthService;
  let apiResponse: ApiResponse;
  let controller: HttpTestingController;
  let accountDetails: AccountDetails;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        AuthService,
        { provide: APP_CONFIG, useValue: AppConfig },
      ],
    });
    service = TestBed.inject(AuthService);
    controller = TestBed.inject(HttpTestingController);
    apiResponse = {
      accessToken: 'Gimme access',
      languageCode: 'EN',
    };
    accountDetails = {
      Password: 'bla',
      UserName: 'vlad',
      TimeStamp: '123454',
    };
  });


  it('should call the back-end', () => {
    spyOn(service, 'setSession');
    spyOn(service, 'login').and.callThrough();
    let result: any;

    service.login(accountDetails.UserName, accountDetails.Password)
      .subscribe((response) => {
        result = response;
        expect(service.setSession).toHaveBeenCalled();
        expect(service.setSession).toHaveBeenCalledWith(apiResponse);
        expect(result).toEqual(apiResponse);
    });

    const request = controller.expectOne(AppConfig.apiEndPoint);
    request.flush(apiResponse);
  });
});

我错过了一些关键要素:

  • 我错过了注入服务
  • 调用request.flush并测试请求

我还稍微更改了登录组件及其在没有订阅的情况下对服务的调用。 相反,该服务在成功响应时调用setSession 我已经编辑了问题。 再次感谢@AliF50提供的宝贵非官方资源~!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM