代码之家  ›  专栏  ›  技术社区  ›  RBz

角积分测试-jasmine Spy不会从observable返回数据-预期0为3

  •  0
  • RBz  · 技术社区  · 3 年前

    我是做集成测试的新手。整件事太令人困惑了。

    对于我的第一次测试,我的间谍似乎没有像我打算返回的那样返回数据。给出和错误:预期0为3。如果有人能帮助我理解我做错了什么,那就太好了。

    这是我的服务、页面、规范文件以及模板:

    我的服务

    
        import { Data } from './../data/data.model';
        import { Injectable } from '@angular/core';
        import { BehaviorSubject, of } from 'rxjs';
        import { tap } from 'rxjs/operators';
    
        @Injectable({
          providedIn: 'root',
        })
        export class MyService {
          private _data = new BehaviorSubject<Data[]>([]);
    
          get data() {
            return this._data;
          }
    
          constructor() {}
    
          getAllData() {
            return of([
              {
                id: '1',
                title: 'Rice',
              },
              {
                id: '2',
                title: 'Wheat',
              },
              {
                id: '33',
                title: 'Water',
              },
            ]).pipe(
              tap((data) => {
                this._data.next(data);
              })
            );
          }
        }
    
    

    数据页面组件

    
        import { Component, OnInit } from '@angular/core';
        import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
        import { MyService } from '../services/my.service';
        import { Data } from './data.model';
    
        @Component({
          selector: 'app-data',
          templateUrl: './data.page.html',
          styleUrls: ['./data.page.scss'],
        })
        export class DataPage implements OnInit {
          allData: Data[];
          dataServiceSub: Subscription;
          isLoading: boolean;
    
          constructor(private myService: MyService) {}
    
          ngOnInit() {
            this.dataServiceSub = this.myService.data.subscribe(
              (data) => {
                console.log(data);
                this.allData = data;
              }
            );
          }
    
          ngOnDestroy() {
            if (this.dataServiceSub) {
              console.log('ngOnDestroy');
              this.dataServiceSub.unsubscribe();
            }
          }
    
          ionViewWillEnter() {
            this.isLoading = true;
            this.myService.getAllData().subscribe(() => {
              console.log('ionViewWillEnter');
              this.isLoading = false;
            });
          }
        }
    
    

    数据页面.spec

        import { MyService } from '../services/my.service';
        import { async, ComponentFixture, TestBed } from '@angular/core/testing';
        import { IonicModule } from '@ionic/angular';
    
        import { DataPage } from './data.page';
        import { of } from 'rxjs';
    
        describe('DataPage', () => {
          let component: DataPage;
          let fixture: ComponentFixture<DataPage>;
          let serviceSpy: jasmine.SpyObj<MyService>;
    
          beforeEach(async(() => {
            TestBed.configureTestingModule({
              declarations: [DataPage],
              providers: [
                {
                  provide: MyService,
                  useClass: MyService
                },
              ],
              imports: [IonicModule.forRoot()],
            }).compileComponents();
    
            fixture = TestBed.createComponent(DataPage);
            component = fixture.componentInstance;
            fixture.detectChanges();
          }));
    
          fit('Should show list of data if data is available', () => {
            serviceSpy = TestBed.get(MyService);
            spyOn(serviceSpy, 'getAllData').and.returnValue(of([
              {
                id: '1',
                title: 'Rice',
              },
              {
                id: '2',
                title: 'Wheat',
              },
              {
                id: '33',
                title: 'Water',
              },
            ]));
            fixture.detectChanges();
            const element = fixture.nativeElement.querySelectorAll(
              '[test-tag="dataList"] ion-item'
            );
            console.log(
              fixture.nativeElement.querySelectorAll('[test-tag="dataList"]')
            );
            expect(element.length).toBe(3);
          });
        });
    

    HTML

    
        <ion-content>
          <div test-tag="empty" class="ion-text-center">
            <ion-text color="danger">
              <h1>No data</h1>
            </ion-text>
          </div>
          <div test-tag="dataList">
            <ion-list>
              <ion-item *ngFor="let data of allData">
                <ion-label test-tag="title">{{data.title}}</ion-label>
              </ion-item>
            </ion-list>
          </div>
        </ion-content>
    
    
    
    0 回复  |  直到 3 年前
        1
  •  3
  •   Shashank Vivek    3 年前

    好的,那么问题来了:

    你需要打电话 ionViewWillEnter() 设置以下值 this.allData '

    原因:因为你在创建时有空值 BehaviorSubject .并利用 data ( this._data.next(data) ),你需要打电话 getAllData() .

     import { MyService } from '../services/my.service';
        import { async, ComponentFixture, TestBed } from '@angular/core/testing';
        import { IonicModule } from '@ionic/angular';
    
        import { DataPage } from './data.page';
        import { of } from 'rxjs';
    
        describe('DataPage', () => {
          let component: DataPage;
          let fixture: ComponentFixture<DataPage>;
          let serviceSpy: jasmine.SpyObj<MyService>;
    
          beforeEach(async(() => {
            TestBed.configureTestingModule({
              declarations: [DataPage],
              providers: [ MyService ],
              imports: [IonicModule.forRoot()],
            }).compileComponents();
    
            fixture = TestBed.createComponent(DataPage);
            component = fixture.componentInstance;
            fixture.detectChanges();
          }));
    
          fit('Should show list of data if data is available', () => {
            component.ionViewWillEnter(); // or create an event which will trigger ionViewWillEnter()
            fixture.detectChanges();
            const element = fixture.nativeElement.querySelectorAll(
              '[test-tag="dataList"] ion-item'
            );
            console.log(
              fixture.nativeElement.querySelectorAll('[test-tag="dataList"]')
            );
            expect(element.length).toBe(3);
          });
        });
    

    请注意,我做了一些更改:

    1. 远离的 UseClass (因为你没有按预期使用它)
    2. 远离的 spy (因为您在原始服务中已经有一个硬编码的值)

    为了更好地理解角度测试,您可以参考 my article 该文还展示了 useClass 供您参考。


    附言:尝试使用 asObservable() ,并遵循使用惯例 $ 当创建一个 Observable ( this.data$.asObservable() ). 这不是一种强迫,而是JS社区中公认的做法。

    get data() {
      return this._data.asObservable();
    }
    
        2
  •  1
  •   satanTime    3 年前

    为了避免可观察性带来的痛苦,我建议使用类似这样的模拟库 ng-mocks ,并查看其文章“如何在Angular测试中模拟可观察流” https://ng-mocks.sudo.eu/extra/mock-observables .

    在您的情况下,测试可能看起来像这样:

    describe('DataPage', () => {
      // mocks everything except DataPage
      beforeEach(() => {
        return MockBuilder(DataPage)
          .mock(IonicModule.forRoot())
          .mock(MyService);
      });
    
      // We need to stub it because of subscription in ionViewWillEnter.
      // in a mock service, ionViewWillEnter does not return anything.
      // But we need to tell it to return an empty observable stream
      // to avoid errors like cannot call .subscribe on undefined.
      // This line can be removed along with the debugging from the
      // component.
      beforeEach(() => MockInstance(MyService, 'getAllData', () => EMPTY));
    
      it('Should show list of data if data is available', () => {
        // spies the getter of the property the component uses.
        MockInstance(MyService, 'data', jasmine.createSpy(), 'get')
          .and.returnValue(of([
            {
              id: '1',
              title: 'Rice',
            },
            {
              id: '2',
              title: 'Wheat',
            },
            {
              id: '33',
              title: 'Water',
            },
          ]));
    
        // render (already with detected changes)
        const fixture = MockRender(DataPage);
    
        // assertions
        const element = fixture.nativeElement.querySelectorAll(
          '[test-tag="dataList"] ion-item'
        );
        console.log(
          fixture.nativeElement.querySelectorAll('[test-tag="dataList"]')
        );
        expect(element.length).toBe(3);
      });
    });