简体   繁体   English

如何模拟 Angular 服务发射器并返回订阅

[英]How to Mock Angular service Emitter and return Subscription

I have the following code in my Component, that I would like to test我的组件中有以下代码,我想测试

  ngOnInit(): void {
    this.authService.loggedInSignal.subscribe((data) => {
      console.log(data)
      if (data) {
        this.planFeatures = this.authService.getProfileData().planFeatures;
        console.log(this.planFeatures)
      }
    });
  }

For that purpose, I mock the authService, but not sure how to test further.为此,我模拟了 authService,但不确定如何进一步测试。 I would like to verify that subscription is fired and to test inside code我想验证订阅是否被触发并测试内部代码

So far I did到目前为止我做到了

describe('UpgradeSorageButtonComponent', () => {
  let component: UpgradeSorageButtonComponent;
  let fixture: ComponentFixture<UpgradeSorageButtonComponent>;
  let authService: AuthService;
  let mockAuthService = {
    loggedInSignal: {
      subscribe: {}
    },
    
    getProfileData() {return {};}
  };

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [UpgradeSorageButtonComponent],
      imports: [RouterTestingModule],
      providers: [{provide: AuthService, useValue: mockAuthService}]
    }).compileComponents();

    authService = TestBed.inject(AuthService);
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(UpgradeSorageButtonComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  fit('should attach to subscriber', fakeAsync(() => {
    spyOn(authService.loggedInSignal, 'subscribe').and.returnValue(true)
  }));
});

but getting an error that type 'boolean' is not assignable to parameter of type 'Subscription'但收到一个错误,即“布尔”类型不能分配给“订阅”类型的参数

Can someone help with how to properly mock the service and do tests for that piece of code?有人可以帮助如何正确模拟服务并对那段代码进行测试吗? Thanks谢谢

The first fixture.detectChanges() you call is when ngOnInit is called.您调用的第一个 fixture.detectChanges fixture.detectChanges()是调用ngOnInit时。

Since, ngOnInit has this.authService.loggedInSignal.subscribe , it is important that it is mocked and returning an observable value before this fixture.detectChanges() .因为ngOnInitthis.authService.loggedInSignal.subscribe ,所以在这个fixture.detectChanges()之前模拟它并返回一个可观察的值是很重要的。

Follow the comments with.!关注评论。! for more details.更多细节。

// !! I would use a spy object, it makes constructions of spies easier
let mockAuthService: jasmine.SpyObj<AuthService>;

beforeEach(async () => {
    mockAuthService = jasmine.createSpyObj<AuthService>('AuthService', ['loggedInSignal', 'getProfileData']);
    await TestBed.configureTestingModule({
      declarations: [UpgradeSorageButtonComponent],
      imports: [RouterTestingModule],
      providers: [{provide: AuthService, useValue: mockAuthService}]
    }).compileComponents();
     // !! don't need the below line
    // authService = TestBed.inject(AuthService);
  });

beforeEach(() => {
    fixture = TestBed.createComponent(UpgradeSorageButtonComponent);
    component = fixture.componentInstance;
    authService.loggedInSignal.and.returnValue(of(true));
    // !!!~ ngOnInit is called here
    fixture.detectChanges();
  });

I like using Spy Objects because it makes mocking much easier.我喜欢使用 Spy Objects,因为它使 mocking 更容易。

Check this link out on how to use Spy Objects and check this link on how to unit test in Angular as a whole.查看此链接以了解如何使用 Spy Objects,并查看此链接以了解如何在 Angular 中整体进行单元测试。 The 2nd link is really good.第二个链接真的很好。

First, remove the fixture.detectChanges();首先,移除fixture.detectChanges(); from the beforeEach call, then call the ngOnInit in your test and -if you spied the code from the authService -, you should tick() the call to "enter" the subscription callback.beforeEach调用中,然后在您的测试中调用ngOnInit并且-如果您从authService中窥探代码-,您应该tick()调用以“输入”订阅回调。

fit('should attach to subscriber', fakeAsync(() => {
    ...//Setup test (if necessary)
    spyOn(authService.loggedInSignal, 'subscribe').and.returnValue(true);
    componente.ngOnInit();
    tick();
    expect(thingInsideSubscription);
}));

Heres my mock router service, which has a Behaviorsubject that many components subscribe to in their OnInit hooks..... I am providing this service and have a convenience method 'emit', which is what calls next on my Behaviorsubject during testing....这是我的模拟路由器服务,它有一个许多组件在其 OnInit 钩子中订阅的 Behaviorsubject .....我正在提供此服务并有一个方便的方法“emit”,这是在测试期间调用我的 Behaviorsubject 的 next 方法。 ..

class MockRouterUrlStateSvc {
  private _currentRouterUrlState$ = new BehaviorSubject<any>(null);

  get currentRouterUrlState$() {
    return this._currentRouterUrlState$.asObservable();
  }

  public emit(value: any) {
    this._currentRouterUrlState$.next(value);
  }
}

Your test can look something like the following:您的测试可能如下所示:

class MockAuthService {
// BehaviorSubject with initial value of true
    _loggedInSignal$ = new BehaviorSubject<boolean>(true);

    get loggedInSignal() {
        return this._loggedInSignal$.asObservable();
    }

    emit(value: boolean) {
        this._loggedInSignal$.next(value);
    }

    getProfileData() {
        return {};
    }
}

describe('UpgradeSorageButtonComponent', () => {
  let component: UpgradeSorageButtonComponent;
  let fixture: ComponentFixture<UpgradeSorageButtonComponent>;
  let authService;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [UpgradeSorageButtonComponent],
      imports: [RouterTestingModule],
      providers: [{provide: AuthService, useClass: MockAuthService}]
    }).compileComponents();

    authService = TestBed.inject(AuthService);
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(UpgradeSorageButtonComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  fit('should attach to subscriber and get true value initially', () => {
    authService.loggedInsignal.subscribe((isLoggedIn) => {
        expect(isLoggedIn).toBeTrue();
    })
  });

  fit('should have emitted false value inside onInit', () => {
    authService.emit(false);
    component.ngOnInit();
    // test stuff happening inside your OnInit based on a false login
  });

  describe('Other stuff', () => {
    it('should not log you in with a false observable value', () => {
      authService.emit(false);
      tick();
      authService.isLoggedInSignal.subscribe((isLoggedIn) => {
        expect(isLoggedIn).toBeFalse();
        // other stuff to test if the user isnt logged in
    })
    })

    it('same as above with jasmine marbles', () => {
      const emissions$ = hot('-a', { a: authService.emit(false) });
      authService.isLoggedInSignal.subscribe((isLoggedIn) => {
        expect(isLoggedIn).toBeFalse();
      });
    });
  });
});


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

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