簡體   English   中英

帶有.a​​sObservable()的Angular 2單元測試(TestBed)服務

[英]Angular 2 Unit Test (TestBed) service with .asObservable()

我有一個調用服務的組件,以查看是否已從另一個組件宣布訂閱。

零件:

this.activateProcessReadySubscription =  this.returnService.processReadySubscriptionAnnouced$.subscribe(
            itemsInCart => {
                this.itemsInCart = itemsInCart;
            });

當我嘗試對此進行測試時,出現錯誤:

TypeError:無法讀取未定義的屬性“ subscribe”

SPEC

it('should call constructor', fakeAsync(() => {
        mockReturnsService.setResponse(0, true);
        tick();
        fixture.detectChanges();
        expect(mockReturnsService.processReadySubscriptionAnnouced$Spy).toHaveBeenCalledTimes(1);
    }));

服務:

    private activateProcessReadySubscriptionSource = new Subject<number>();
    processReadySubscriptionAnnouced$ = this.activateProcessReadySubscriptionSource.asObservable();

    announceProcessReady(itemsInCart: number) {
        this.activateProcessReadySubscriptionSource.next(this.returnCartDataLength);
    }

我似乎無法弄清楚如何使訂閱正確測試。

(最后這是非常基本的東西,但是花了我幾天時間才弄清楚……希望能幫助那里的人節省一些時間:)……)

我遇到了同樣的問題,解決該問題的唯一方法是使用吸氣劑,以便能夠在測試時返回模擬值...

因此,在服務中,您必須將屬性更改為吸氣劑:

private activateProcessReadySubscriptionSource = new Subject<number>();

processReadySubscriptionAnnouced$ () { return this.activateProcessReadySubscriptionSource.asObservable();}

之后,您必須修改訪問該屬性的方式,以便(現在)在組件上執行它。

現在,您可以訪問.spec.ts文件上的可觀察訂閱功能...

現在,我將在代碼中告訴我類似的歷史記錄:

我有:

/* * * * MyData.service.ts * * * */
//   TYPING ASUMPTIONS...
//   - DI = [Your defined interface]
//   - BS = BehaviorSubject 
@Injectable()
export class MyDataService {
  private searchToggleSource: BS<DI> = new BS<DI>({ search: false });
  public currentToggleSearchStatus: Observable<DI> = this.searchToggleSource.asObservable();
}


/* * * * My.component.ts * * * */ 
@Component({
  selector: 'my-component',
  template: './my.component.html',
  styleUrls: ['./my.component.css'],
})
export class MyComponent implements OnInit, OnDestroy {
  private searchToggleSubscription: Subscription;
  public search: boolean;

  // DataService being the injected, imported service 
  constructor(dataService: DataService){ }

  ngOnInit(){
    this.searchToggleSubscription = this.dataService.currentToggleSearchStatus
    .subscribe(
      ({ search }) => {
        this.search = search;
      });
  }

  ngOnDestroy(){
    this.searchToggleSubscription.unsubscribe();
  }
}

/* * * * My.component.spec.ts * * * */ 
// ASUMPTIONS
// - USING 'jest'
describe('MyComponent', () => {
  let fixture: ComponentFixture<MyComponent>;
  let mockDataService;

  beforeEach(() => {
    mockDataService = createSpyObj('DataService', ['currentToggleSearchStatus', ]);

    TestBed.configureTestingModule({
      declarations: [
        MyComponent,
      ],
      providers: [
        { provide: DataService, useValue: mockDataService },
      ]
    });
    fixture = TestBed.createComponent(MyComponent);
  });

  it('should get state from DataService when ngOnInit', () => {
    mockDataService
    .currentToggleSearchStatus
    .mockReturnValue(of({search: true}));

    //... to call ngOnInit()

    // ****************** THE ERROR **************************
    // **** subscribe is not a function...
    // **** Since I have no access to a real Observable from
    // **** a fake DataService property...

    fixture.detectChanges();

    // *** SERIOUSLY I SPEND ALMOST 3 DAYS RESEARCHING AND PLAYING
    // *** WITH THE CODE AND WAS NOT ABLE TO FIND/CREATE A SOLUTION...
    // *** 'TILL LIGHT CAME IN...
    // *******************************************************
    expect(fixture.componentInstance.search).toBe(false)
  });
});

解決方案...使用吸氣劑...我將使用注釋“-”顯示“解決方案” ...

/* * * * MyData.service.ts * * * */
//   TYPING ASUMPTIONS...
//   - DI = [Your defined interface]
//   - BS = BehaviorSubject 
@Injectable()
export class MyDataService {
  private searchToggleSource: BS<DI> = new BS<DI>({ search: false });
  //------- CHANGED ---
  // public currentToggleSearchStatus: Observable<DI> = this.searchToggleSource.asObservable();

  //------- A GETTER ------ (MUST RETURN THE OBSERVABLE SUBJECT)
  public currentToggleSearchStatus(){
    return this.searchToggleSource.asObservable();
  }
}


/* * * * My.component.ts * * * */ 
@Component({
  selector: 'my-component',
  template: './my.component.html',
  styleUrls: ['./my.component.css'],
})
export class MyComponent implements OnInit, OnDestroy {
  private searchToggleSubscription: Subscription;
  public search: boolean;

  // DataService being the injected, imported service 
  constructor(dataService: DataService){ }

  ngOnInit(){
    //------------ CHANGED  -------
    //this.searchToggleSubscription = this.dataService.currentToggleSearchStatus
    //.subscribe(
    //  ({ search }) => {
    //    this.search = search;
    //  });

    //------------ EXECUTE THE SERVICE GETTER -------
    this.searchToggleSubscription = this.dataService.currentToggleSearchStatus()
    .subscribe(
      ({search}) => {
        this.search = search;
      }
    );
  }

  ngOnDestroy(){
    this.searchToggleSubscription.unsubscribe();
  }
}

/* * * * My.component.spec.ts * * * */ 
// ASUMPTIONS
// - USING 'jest'
describe('MyComponent', () => {
  let fixture: ComponentFixture<MyComponent>;
  let mockDataService;

  beforeEach(() => {
    mockDataService = createSpyObj('DataSharingSearchService', ['showHideSearchBar', 'currentToggleSearchStatus', ]);

    TestBed.configureTestingModule({
      declarations: [
        MyComponent,
      ],
      providers: [
        { provide: DataService, useValue: mockDataService },
      ]
    });
    fixture = TestBed.createComponent(MyComponent);
  });

  it('should get state from DataService when ngOnInit', () => {
    mockDataService
    .currentToggleSearchStatus
    .mockReturnValue(of({search: true}));

    //... to call ngOnInit()
    // ------------- NO ERROR :3!!!  -------------------
    fixture.detectChanges();
    expect(fixture.componentInstance.search).toBe(false)
  });
});

***注意:笑話API非常類似於茉莉花...

// jest:                    jasmine:
//  createSpyObj       <=>  jasmine.createSpyObj
// .mockReturnValue()  <=>  .and.returnValue()

不要忘記僅導入'of'函數,以在模擬服務中返回可觀察對象...

import { of } from "rxjs/observable/of";

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM