【问题标题】:Angular Unit Test Mock Replay SubjectAngular 单元测试模拟重播主题
【发布时间】:2019-08-12 05:38:02
【问题描述】:

我有一个带有重播主题的服务。

export class UserService {
  public userChanged: ReplaySubject<User> = new ReplaySubject<User>();

...
public getUser(userId?): void {
    ...
    this.http.get(url, httpOptions).pipe(
      catchError(this.handleError('getUser', null, 'Couldn\'t get user', options))
    ).subscribe( (user: User) => {

       this.userChanged.next(user);

    });
  }

我的组件订阅了userChanged。

this.userService.userChanged.subscribe((user) => {
  this.user = user;
});

现在,我想在组件测试中模拟我的UserService:

1 个选项Testing Observables in Angular)

import { of } from 'rxjs';
...
    const userServiceSpy = jasmine.createSpyObj('UserService', {'userChanged': of({_id: '1'}) });

或2个选项)

    const userServiceSpy = jasmine.createSpyObj('UserService', {'userChanged': () => of({_id: '1'}) });

或3个选项angular test tutorial)

const userServiceSpy = jasmine.createSpyObj('UserService', ['userChanged']});
const userChangedSpy = userServiceSpy.userChanged.and.returnValue( of({_id: '1'})  );

+

TestBed.configureTestingModule({
  ...
  providers: [
    ...
    {provide: UserService, useValue: userServiceSpy}
  ],
  schemas: [NO_ERRORS_SCHEMA]
})

把这个错误告诉我:

this.userService.userChanged.subscribe is not a function

of 不应该返回一个 Observable 来订阅吗?

问题:如何模拟这个?

【问题讨论】:

    标签: angular unit-testing


    【解决方案1】:

    这可能有点晚了。但我可以通过以下方法解决这个问题:

    1. mockUserService = TestBed.get(UserService)
    2. By calling the subscribe method using :
    mockUserService.userChanged.Subscribe(data => {
    // do something
    })
    

    注意:第 1 行和第 2 行的代码必须位于 TestBed.configureTestingModule 声明之后的任何位置。

    【讨论】:

      【解决方案2】:

      createSpyObj 用于在方法上创建间谍。您可以将它用于UserService 的getUser 方法。

      userChanged 只是类的一个属性。你不需要间谍。

      您可以做的只是创建一个返回主题的模拟对象:

      const userChanged = new Subject();
      
       providers: [
          ...
          {provide: UserService, useValue: { userChanged }}
        ],
      

      { userChanged } 等于 { userChanged: userChanged }

      然后,在您的 beforeEach 块中,您将发出一个新的用户实例:

      //...
      beforeEach(() => {
         const myUser = new User(...)
         userChanged.next(myUser)
      })
      
      

      我建议在 beforeEach 块中执行此操作以避免不同规范之间的副作用。

       providers: [
          ...
          {provide: UserService, useValue: { userChanged: of({id: 1}) }}
        ],
      

      做同样的事情的另一种方法是使用of 方法简单地创建可观察对象,就像你在示例中所做的那样。


      如果你真的想窥探subscribe方法,你可以在上面创建窥探:

      spyOn(userChanged, 'subscribe')


      如果您想将spyObject 与属性混合使用,可以使用扩展运算符:

      const spyObj = {
        ... jasmine.createSpyObj('MyObject', ['spyMethod']),
        myProperty: true,
      };
      
      spyObj.spyMethod();
      
      expect(spyObj.spyMethod).toHaveBeenCalled();
      expect(spyObj.myProperty).toBeTrue();
      

      【讨论】:

      • 我不得不将 .emit 更改为 .next 但它起作用了。谢谢。如果我想使用它并监视方法,我可以将它结合起来,例如:{provide: UserService, useValue: { userChanged, userServiceSpy}}
      • 对不起.emit。总是用 EventEmitter 弄乱主题 -_-.
      • 为了将属性与 spyObject 结合,您可以使用扩展运算符。检查更新的答案
      • 嗨@DDRamone。根据上述解决方案,您将返回 userChanged 并带有一个 observable。当我们在服务中编写多个主题时,您能帮忙吗?
      • @KarthikBhat 您可以根据需要向模拟服务添加任意数量的属性。只需像在普通对象中那样用逗号分隔它们。如果这不能回答您的问题,请告诉我更多详细信息,我会尽力提供帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 2020-11-19
      • 2018-11-24
      • 1970-01-01
      • 1970-01-01
      • 2017-12-20
      相关资源
      最近更新 更多