99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Angular 測試服務(wù)

2022-07-07 10:18 更新

測試服務(wù)

為了檢查你的服務(wù)是否正常工作,你可以專門為它們編寫測試。

如果你要試驗本指南中所講的應(yīng)用,請在瀏覽器中運行它下載并在本地運行它。

服務(wù)往往是最容易進行單元測試的文件。下面是一些針對 ?ValueService ?的同步和異步單元測試,甚至不需要 Angular 測試工具的幫助。

// Straight Jasmine testing without Angular's testing support
describe('ValueService', () => {
  let service: ValueService;
  beforeEach(() => { service = new ValueService(); });

  it('#getValue should return real value', () => {
    expect(service.getValue()).toBe('real value');
  });

  it('#getObservableValue should return value from observable',
    (done: DoneFn) => {
    service.getObservableValue().subscribe(value => {
      expect(value).toBe('observable value');
      done();
    });
  });

  it('#getPromiseValue should return value from a promise',
    (done: DoneFn) => {
    service.getPromiseValue().then(value => {
      expect(value).toBe('promise value');
      done();
    });
  });
});

有依賴的服務(wù)

服務(wù)通常依賴于 Angular 在構(gòu)造函數(shù)中注入的其它服務(wù)。在很多情況下,調(diào)用服務(wù)的構(gòu)造函數(shù)時,很容易手動創(chuàng)建和注入這些依賴。

?MasterService ?就是一個簡單的例子:

@Injectable()
export class MasterService {
  constructor(private valueService: ValueService) { }
  getValue() { return this.valueService.getValue(); }
}

?MasterService ?只把它唯一的方法 ?getValue ?委托給了所注入的 ?ValueService?。

這里有幾種測試方法。

describe('MasterService without Angular testing support', () => {
  let masterService: MasterService;

  it('#getValue should return real value from the real service', () => {
    masterService = new MasterService(new ValueService());
    expect(masterService.getValue()).toBe('real value');
  });

  it('#getValue should return faked value from a fakeService', () => {
    masterService = new MasterService(new FakeValueService());
    expect(masterService.getValue()).toBe('faked service value');
  });

  it('#getValue should return faked value from a fake object', () => {
    const fake =  { getValue: () => 'fake value' };
    masterService = new MasterService(fake as ValueService);
    expect(masterService.getValue()).toBe('fake value');
  });

  it('#getValue should return stubbed value from a spy', () => {
    // create `getValue` spy on an object representing the ValueService
    const valueServiceSpy =
      jasmine.createSpyObj('ValueService', ['getValue']);

    // set the value to return when the `getValue` spy is called.
    const stubValue = 'stub value';
    valueServiceSpy.getValue.and.returnValue(stubValue);

    masterService = new MasterService(valueServiceSpy);

    expect(masterService.getValue())
      .withContext('service returned stub value')
      .toBe(stubValue);
    expect(valueServiceSpy.getValue.calls.count())
      .withContext('spy method was called once')
      .toBe(1);
    expect(valueServiceSpy.getValue.calls.mostRecent().returnValue)
      .toBe(stubValue);
  });
});

第一個測試使用 ?new ?創(chuàng)建了一個 ?ValueService?,并把它傳給了 ?MasterService ?的構(gòu)造函數(shù)。

然而,注入真實服務(wù)很難工作良好,因為大多數(shù)被依賴的服務(wù)都很難創(chuàng)建和控制。

相反,可以模擬依賴、使用仿制品,或者在相關(guān)的服務(wù)方法上創(chuàng)建一個測試間諜

我更喜歡用測試間諜,因為它們通常是模擬服務(wù)的最佳途徑。

這些標(biāo)準(zhǔn)的測試技巧非常適合對服務(wù)進行單獨測試。

但是,你幾乎總是使用 Angular 依賴注入機制來將服務(wù)注入到應(yīng)用類中,你應(yīng)該有一些測試來體現(xiàn)這種使用模式。Angular 測試實用工具可以讓你輕松調(diào)查這些注入服務(wù)的行為。

使用 TestBed 測試服務(wù)

你的應(yīng)用依靠 Angular 的依賴注入(DI)來創(chuàng)建服務(wù)。當(dāng)服務(wù)有依賴時,DI 會查找或創(chuàng)建這些被依賴的服務(wù)。如果該被依賴的服務(wù)還有自己的依賴,DI 也會查找或創(chuàng)建它們。

作為服務(wù)的消費者,你不應(yīng)該關(guān)心這些。你不應(yīng)該關(guān)心構(gòu)造函數(shù)參數(shù)的順序或它們是如何創(chuàng)建的。

作為服務(wù)的測試人員,你至少要考慮第一層的服務(wù)依賴,但當(dāng)你用 ?TestBed ?測試實用工具來提供和創(chuàng)建服務(wù)時,你可以讓 Angular DI 來創(chuàng)建服務(wù)并處理構(gòu)造函數(shù)的參數(shù)順序。

Angular TestBed

?TestBed ?是 Angular 測試實用工具中最重要的。?TestBed ?創(chuàng)建了一個動態(tài)構(gòu)造的 Angular 測試模塊,用來模擬一個 Angular 的 ?@NgModule?。

?TestBed.configureTestingModule()? 方法接受一個元數(shù)據(jù)對象,它可以擁有?@NgModule?的大部分屬性。

要測試某個服務(wù),你可以在元數(shù)據(jù)屬性 ?providers ?中設(shè)置一個要測試或模擬的服務(wù)數(shù)組。

let service: ValueService;

beforeEach(() => {
  TestBed.configureTestingModule({ providers: [ValueService] });
});

將服務(wù)類作為參數(shù)調(diào)用 ?TestBed.inject()?,將它注入到測試中。

注意:
?TestBed.get()? 已在 Angular 9 中棄用。為了幫助減少重大變更,Angular 引入了一個名為 ?TestBed.inject()? 的新函數(shù),你可以改用它。
it('should use ValueService', () => {
  service = TestBed.inject(ValueService);
  expect(service.getValue()).toBe('real value');
});

或者,如果你喜歡把這個服務(wù)作為設(shè)置代碼的一部分進行注入,也可以在 ?beforeEach()? 中做。

beforeEach(() => {
  TestBed.configureTestingModule({ providers: [ValueService] });
  service = TestBed.inject(ValueService);
});

測試帶依賴的服務(wù)時,需要在 ?providers ?數(shù)組中提供 mock。

在下面的例子中,mock 是一個間諜對象。

let masterService: MasterService;
let valueServiceSpy: jasmine.SpyObj<ValueService>;

beforeEach(() => {
  const spy = jasmine.createSpyObj('ValueService', ['getValue']);

  TestBed.configureTestingModule({
    // Provide both the service-to-test and its (spy) dependency
    providers: [
      MasterService,
      { provide: ValueService, useValue: spy }
    ]
  });
  // Inject both the service-to-test and its (spy) dependency
  masterService = TestBed.inject(MasterService);
  valueServiceSpy = TestBed.inject(ValueService) as jasmine.SpyObj<ValueService>;
});

該測試會像以前一樣使用該間諜。

it('#getValue should return stubbed value from a spy', () => {
  const stubValue = 'stub value';
  valueServiceSpy.getValue.and.returnValue(stubValue);

  expect(masterService.getValue())
    .withContext('service returned stub value')
    .toBe(stubValue);
  expect(valueServiceSpy.getValue.calls.count())
    .withContext('spy method was called once')
    .toBe(1);
  expect(valueServiceSpy.getValue.calls.mostRecent().returnValue)
    .toBe(stubValue);
});

沒有 beforeEach() 的測試

本指南中的大多數(shù)測試套件都會調(diào)用 ?beforeEach()? 來為每一個 ?it()? 測試設(shè)置前置條件,并依賴 ?TestBed ?來創(chuàng)建類和注入服務(wù)。

還有另一種測試,它們從不調(diào)用 ?beforeEach()?,而是更喜歡顯式地創(chuàng)建類,而不是使用 ?TestBed?。

你可以用這種風(fēng)格重寫 ?MasterService ?中的一個測試。

首先,在 setup 函數(shù)中放入可供復(fù)用的預(yù)備代碼,而不用 ?beforeEach()?。

function setup() {
  const valueServiceSpy =
    jasmine.createSpyObj('ValueService', ['getValue']);
  const stubValue = 'stub value';
  const masterService = new MasterService(valueServiceSpy);

  valueServiceSpy.getValue.and.returnValue(stubValue);
  return { masterService, stubValue, valueServiceSpy };
}

?setup()? 函數(shù)返回一個包含測試可能引用的變量(如 ?masterService?)的對象字面量。你并沒有在 ?describe()? 的函數(shù)體中定義半全局變量(比如 ?let masterService: MasterService?)。

然后,每個測試都會在第一行調(diào)用 ?setup()?,然后繼續(xù)執(zhí)行那些操縱被測主體和斷言期望值的步驟。

it('#getValue should return stubbed value from a spy', () => {
  const { masterService, stubValue, valueServiceSpy } = setup();
  expect(masterService.getValue())
    .withContext('service returned stub value')
    .toBe(stubValue);
  expect(valueServiceSpy.getValue.calls.count())
    .withContext('spy method was called once')
    .toBe(1);
  expect(valueServiceSpy.getValue.calls.mostRecent().returnValue)
    .toBe(stubValue);
});

請注意測試如何使用解構(gòu)賦值來提取它需要的設(shè)置變量。

const { masterService, stubValue, valueServiceSpy } = setup();

許多開發(fā)人員都覺得這種方法比傳統(tǒng)的 ?beforeEach()? 風(fēng)格更清晰明了。

雖然這個測試指南遵循傳統(tǒng)的樣式,并且默認(rèn)的CLI 原理圖會生成帶有 ?beforeEach()? 和 ?TestBed ?的測試文件,但你可以在自己的項目中采用這種替代方式。

測試 HTTP 服務(wù)

對遠(yuǎn)程服務(wù)器進行 HTTP 調(diào)用的數(shù)據(jù)服務(wù)通常會注入并委托給 Angular 的 ?HttpClient?服務(wù)進行 XHR 調(diào)用。

你可以測試一個注入了 ?HttpClient ?間諜的數(shù)據(jù)服務(wù),就像測試所有帶依賴的服務(wù)一樣。

let httpClientSpy: jasmine.SpyObj<HttpClient>;
let heroService: HeroService;

beforeEach(() => {
  // TODO: spy on other methods too
  httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
  heroService = new HeroService(httpClientSpy);
});

it('should return expected heroes (HttpClient called once)', (done: DoneFn) => {
  const expectedHeroes: Hero[] =
    [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];

  httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));

  heroService.getHeroes().subscribe({
    next: heroes => {
      expect(heroes)
        .withContext('expected heroes')
        .toEqual(expectedHeroes);
      done();
    },
    error: done.fail
  });
  expect(httpClientSpy.get.calls.count())
    .withContext('one call')
    .toBe(1);
});

it('should return an error when the server returns a 404', (done: DoneFn) => {
  const errorResponse = new HttpErrorResponse({
    error: 'test 404 error',
    status: 404, statusText: 'Not Found'
  });

  httpClientSpy.get.and.returnValue(asyncError(errorResponse));

  heroService.getHeroes().subscribe({
    next: heroes => done.fail('expected an error, not heroes'),
    error: error  => {
      expect(error.message).toContain('test 404 error');
      done();
    }
  });
});
?HeroService ?方法會返回 ?Observables?。你必須訂閱一個可觀察對象(a)讓它執(zhí)行,(b)斷言該方法成功或失敗。
?subscribe()? 方法會接受成功(?next?)和失敗(?error?)回調(diào)。確保你會同時提供這兩個回調(diào)函數(shù),以便捕獲錯誤。如果不這樣做就會產(chǎn)生一個異步的、沒有被捕獲的可觀察對象的錯誤,測試運行器可能會把它歸因于一個完全不相關(guān)的測試。

HttpClientTestingModule

數(shù)據(jù)服務(wù)和 ?HttpClient ?之間的擴展交互可能比較復(fù)雜,并且難以通過間諜進行模擬。

?HttpClientTestingModule ?可以讓這些測試場景更易于管理。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號