【问题标题】:Why is this button click Angular Unit test not working?为什么此按钮单击 Angular 单元测试不起作用?
【发布时间】:2020-06-12 15:31:48
【问题描述】:

我有一个单元测试,我想测试一个函数是否在单击按钮时被调用。但我不断收到此错误:Expected spy archive to have been called.,我无法弄清楚为什么它不起作用。

我有什么遗漏或做错了吗?有关上下文,请参见下面的代码。

这是我的测试

it('should call archive function on button click', async(() => {
    spyOn(component, 'archive');
    fixture.detectChanges();
    component.project = rh.getProjects(1)[0];
    dh.clickButton('Archive');

    fixture.detectChanges();

    expect(component.archive).toHaveBeenCalled();
}));

这是 dh aka DOMHelper

clickButton(buttonText: string) {
    this.findAll('button').forEach(button => {
      const buttonElement: HTMLButtonElement = button.nativeElement;
      if (buttonElement.textContent === buttonText) {
        buttonElement.click();
      }
    });
}

findAll(tagName: string) {
    return this.fixture.debugElement
      .queryAll(By.css(tagName));
}

这是html

<td data-label="Title">{{ project.title }}</td>
<td data-label="Owner">{{ getOwnerName(project.owner) }}</td>
<td data-label="Actions">
    <button class="btn btn-primary" (click)="dashboard">Dashboard</button>
    <button *ngIf="authService.firebaseAuth.auth.currentUser.uid == project.owner" class="btn btn-primary" (click)="archive">Archive</button>
</td>

这是 component.ts 文件

import { Component, OnInit, Input } from '@angular/core';
import { Project } from 'src/app/models/project';
import { AuthService } from 'src/app/services/auth.service';
import { ProjectService } from 'src/app/services/project.service';
import { Router } from '@angular/router';

@Component({
    selector: 'tr[app-joined-project-item]',
    templateUrl: './joined-project-item.component.html',
    styleUrls: ['./joined-project-item.component.css'],
})
export class JoinedProjectItemComponent implements OnInit {

    @Input() public project: Project;

    constructor(public authService: AuthService, public projectService: ProjectService, private router: Router) {}

    ngOnInit(): void {
    }

    archive() {
        this.projectService.updateArchivedStatusProject(this.project);
    }

    dashboard() {
        this.router.navigate(['/projects/' + this.project.id + '/dashboard'])
    }

    getOwnerName(userId: string) {
        return Object.values(this.project.members).find((member: any) => member['userId'] === userId)['name'];
    }
}

更新

当我在检查服务函数是否被调用的地方进行测试时,我收到以下错误:Expected spy updateArchivedStatusProject to have been called with: *Project Object* but it was never called.

beforeEach(async(() => {
    TestBed.configureTestingModule({
            imports: [RouterTestingModule],
            declarations: [JoinedProjectItemComponent],
            providers: [
                { provide: ProjectService, useClass: MockProjectService },
                { provide: AuthService, useClass: MockAuthService },
                { provide: Router, useValue: routerSpy }
      ]
    }).compileComponents();
}));

it('should call archive function on button click', async(() => {
    spy = spyOn(TestBed.get(ProjectService), 'updateArchivedStatusProject');
    fixture.detectChanges();
    component.project = rh.getProjects(1)[0];
    dh.clickButton('Archive');

    fixture.detectChanges();

    expect(spy).toHaveBeenCalledWith(rh.projects[0]);
}));

【问题讨论】:

  • 建议通过监视组件的方法来测试组件。使用服务的测试替身,并断言其updateArchivedStatusProject 方法被正确的项目调用。通常,您应该通过公共接口(对于 Angular 组件:@Input/@Outputs、DOM 以及与注入的协作者的交互)测试行为,而不是实现。
  • 我添加了问题的更新。当我测试是否调用了模拟的服务函数时,我得到了它从未被调用过的错误
  • 如果您尝试单击的按钮不在 DOM 中,DOMHelper 中的 clickButton 方法不会引发错误。因为上面有一个*ngIf,所以我猜它不是。如果从findAll 返回的数组为空,请尝试记录找到的按钮和/或抛出错误。这将有助于调试失败的测试。
  • 该按钮在该方法中被找到,因为如果我在if (buttonElement.textContent === buttonText) 内登录,我会得到按钮&lt;button _ngcontent-a-c36="" class="btn btn-primary"&gt;Archive&lt;/button&gt;
  • 我认为这里的问题有所不同(见答案)。但是我认为在缺少元素的情况下在该方法中抛出错误将在将来对您有所帮助。

标签: angular typescript karma-jasmine


【解决方案1】:

我认为您在点击处理程序中缺少括号。应该是:

<button (...) (click)="archive()">Archive</button>

编辑:

您认为这是测试错误而不是实际的简单错误这一事实表明您不信任您的测试。当应用程序正常工作时它们失败很多时,可能会发生这种情况。这表明您编写测试的方式可能有问题。

我会给出的一个建议是让您在测试中使用的工具尽可能明确。例如,clickButton 可以重写为:

clickButton(buttonText: string) {
    const foundButtons = this.findAll('button')
      .map(button => button.nativeElement)
      .filter(button => button.innerText === buttonText);

    if (foundButtons.length !== 1) {
      fail(`Expected exactly one button with text "${buttonText}" but found ${foundButtons.length}`);
    }

    foundButtons[0].click();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多