【问题标题】:Angular Unit Test - Cannot read properties of undefined (reading 'subscribe')Angular Unit Test - 无法读取未定义的属性(读取“订阅”)
【发布时间】:2021-12-31 16:29:48
【问题描述】:

我正在使用 Angular / Jasmine 进行单元测试。
目前我正在测试一个带有输入字段+“添加”按钮的组件。
“添加”应该调用一个 add() 函数,其背后有一个服务。

我想测试是否调用了服务方法。

我对此有两个测试。
一个正在工作,(将值添加到输入字段并单击“添加”按钮)
一个不工作。 (直接在“添加”按钮后面调用 add() 方法)\

错误ERROR: TypeError: Cannot read properties of undefined (reading 'subscribe')表示服务未定义。但我不明白为什么服务在一个测试用例中定义而在另一个测试用例中没有?

这是一个带有 Jasmine-Test 的 Stackblitz:https://stackblitz.com/edit/angular-ivy-dnzv96?file=src/app/app.component.spec.ts

HTML

<div>
  <label>Hero name:
    <input #heroName />
  </label>
  
  <button (click)="add(heroName.value)">
    add
  </button>
</div>

add() 方法

 add(name: string): void {
    this.heroService.addHero({ name })
      .subscribe(hero => {
        this.heroes.push(hero);
      });
  }

两个测试

  describe('should call HeroService.addHero()',() => {

    **// WORKING TEST**
    it('by using input dialog and click "Add" button', () => {
      const input = el.query(By.css('input')).nativeElement;
      input.value = 'NewHero';
      const button = el.query(By.css('button'));
      button.triggerEventHandler('click', null)
      fixture.detectChanges;

      expect(mockHeroService.addHero).toHaveBeenCalledWith({ name: 'NewHero' } );
    })

    **// FAILING TEST - ERROR: TypeError: Cannot read properties of undefined (reading 'subscribe')**
    it('by calling method add()', (() => {
      component.add('newHero');

      expect(mockHeroService.addHero).toHaveBeenCalledWith({ name: 'NewHero'} );
    }))
  })

【问题讨论】:

    标签: angular typescript unit-testing jasmine


    【解决方案1】:

    您没有从mockHeroService spy 返回数据,因此出现错误。在规范文件中,您可以进行如下修改:

            const hero = {
              id: 1,
              name: 'NewHero',
              strength: 80,
            }
    
            // within describe
            it('by using input dialog and push add button', () => {
              const stubValue = 'NewHero';
              mockHeroService.addHero.and.returnValue(of(hero));
              // existing logic
              expect(mockHeroService.addHero).toHaveBeenCalledWith({ name: 'NewHero' } );
            })
            
            it('by calling method add()', (() => {
              const stubValue = 'newHero';
              mockHeroService.addHero.and.returnValue(of(hero));
              component.add('newHero');
              expect(mockHeroService.addHero).toHaveBeenCalledWith({ name: 'newHero'} );
            }))
    

    同样在ts文件中,你可以添加if语句来防止null/undefined as:

        this.heroService.addHero({ name })
           .subscribe(hero => {
             if (this.heroes) {
                this.heroes.push(hero);
             }
        });
    

    【讨论】:

      【解决方案2】:

      错误 ERROR: TypeError: Cannot read properties of undefined (reading 'subscribe') 表示服务未定义。但我不明白为什么服务在一个测试用例中定义而在另一个测试用例中没有?

      我认为不是这样:错误告诉服务已定义,调用了addMethod,但返回的值是未定义的而不是Observable

      this.heroService.addHero({ name }) // this is fine
            .subscribe(...); // this is not 
      

      所以,我认为问题在于您如何设置 mockHeroService,我猜您在模拟的 addHero 方法中没有返回任何内容。

      【讨论】:

        【解决方案3】:

        感谢 Chiesa 和 Siddhant。

        Chiesa 解释了错误。 Siddhants 提出了一个解决方案。 没什么可补充的。

        这是工作示例。

        https://stackblitz.com/edit/angular-ivy-p1ccau?file=src/app/app.component.spec.ts

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-23
          • 1970-01-01
          • 2018-02-13
          • 2019-10-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多