65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Protractor 在 Angular 应用程序中进行端到端(E2E)测试

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018年7月8日

CPOL

6分钟阅读

viewsIcon

15674

如何为我们的 Angular 应用程序创建端到端测试

引言

在本文中,我们将学习如何为我们的 Angular 应用创建端到端测试 (e2e)。我们将使用一个现有的端到端测试框架,即 Protractor。Protractor 会在真实的浏览器中运行您的测试,就像您的用户执行操作一样。我将使用一个在我 GitHub 个人资料中可用的现有应用程序。源代码部分也有。请与我分享您的反馈。希望您喜欢这篇文章。让我们开始编码吧。

源代码

源代码可以在 这里 找到。请在开始之前克隆此存储库。

端到端测试的重要性

你们中的一些人可能已经开始为您的应用程序编写端到端测试,并且可能知道它的重要性。在这里,我将列出端到端测试的关键好处。

  • 端到端测试会测试完整的流程或操作。例如,一个完整的登录过程可以视为一个端到端测试。基本上,它会测试一个特定的功能。
  • 它不像单元测试那样提高代码质量。单元测试是一个不同的主题,端到端测试和单元测试是完全不同的。
  • 正如我之前所说,端到端测试在浏览器中运行测试,因此它会实时测试您的应用程序,并包含一些真实数据。
  • 您可以轻松地找出,是否由于您最近的更改或实现而导致任何功能损坏。

基本概述

如果您已经克隆了应用程序,您应该会在应用程序代码中看到一个名为 e2e 的文件夹,请打开该文件夹。您可以看到以下三个文件:

  • tsconfig.e2e.json
  • app.po.ts
  • app.e2e-spec.ts

这里的 tsconfig.e2e.json 是配置文件,如果您打开该文件,您会看到该文件是从 tsconfig.json 扩展而来的。

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/e2e",
    "baseUrl": "./",
    "module": "commonjs",
    "target": "es5",
    "types": [
      "jasmine",
      "jasminewd2",
      "node"
    ]
  }
}

app.po.ts页面 对象,这非常有帮助且很重要。在这里,我们将编写代码来查找页面或视图中的元素。因此,将来,如果您更改了元素的选择器,您的更改将仅在此文件中生效,这样您就不需要更改测试中的任何内容。这不是很方便吗?默认情况下,此文件将包含以下代码:

import { browser, by, element } from 'protractor';

export class AppPage {
  navigateTo() {
    return browser.get('/');
  }

  getParagraphText() {
    return element(by.css('app-root h1')).getText();
  }
}

正如您所见,在第一行,我们从 protractor 导入了 browserbyelement

  1. browser 用于与浏览器交互
  2. by 用于按 CSS 或任何其他函数查找元素
  3. element 用于转换选定的元素

在这一行,element(by.css('app-root h1')).getText(),正如您已经猜到的,我们只是使用选择器 'app-root h1' 来查找一个元素。

app.e2e-spec.ts 是测试的容器。我们将在其中编写所有测试。

import { AppPage } from './app.po';

describe('my-angular5-app App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('Welcome to ng5!');
  });
});

我们首先要做的是导入页面,在这种情况下是 AppPage。如果您已经使用 jasmine 编写了任何 单元测试用例,这将非常容易。如果您不确定如何编写单元测试,我建议您访问 此链接

导入页面后,我们在 beforeEach 函数中声明一个对象并用 AppPage 实例初始化它,以便在每个测试运行之前可以执行代码。

在我们第一个测试中,我们只是通过调用 page.getParagraphText() 从我们的 page 对象获取值,来确认标题是 'Welcome to app'。

创建登录组件

正如你们都知道的,一个组件默认会有两个文件:

  • login.component.html
  • login.component.ts

login.component.html

让我们为我们的组件编写一些 HTML 代码。

<div class="container" style="margin-top:100px;">
  <div class="row justify-content-center align-items-center">
    <div class="col-lg-4 col-sm-4 center-block ">
      <mat-card>
        <mat-card-header>
          <img mat-card-avatar src="../../../assets/images/App-login-manager-icon.png">
          <mat-card-title>Login here</mat-card-title>
          <mat-card-subtitle>Trust us for your data, and sign up</mat-card-subtitle>
        </mat-card-header>
        <mat-card-content>
          <div class="signup-fields">
            <form id="loginForm"[formGroup]="form" (ngSubmit)="login()">
              <div class="form-group">
                <input name="email" class="form-control" 
                 matInput type="email" placeholder="Email" formControlName="email" />
              </div>
              <div class="form-group">
                <input name="password" class="form-control" 
                 matInput type="password" placeholder="Password" 
                 formControlName="password" />
              </div>
              <div>
                <button id="btnSubmit" mat-raised-button type="submit" 
                 color="primary">Login</button>
              </div>
            </form>
          </div>
        </mat-card-content>
      </mat-card>
    </div>
  </div>
</div>

login.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  form;
  constructor(private fb: FormBuilder,
    private myRoute: Router,
    private auth: AuthService) {
    this.form = fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required]
    });
  }

  ngOnInit() {
  }

  login() {
    if (this.form.valid) {
      this.auth.sendToken(this.form.value.email)
      this.myRoute.navigate(["home"]);
    }
  }
}

如果您运行应用程序,您会看到,只有当我们为字段提供相关值时,我们才会接受表单,否则表单将无效。在下一步中,我们将为这个功能编写端到端测试。听起来不错吧?

为登录组件编写端到端测试

首先,让我们创建以下两个文件:

  1. login.po.ts
  2. login.e2e-spec.ts

现在让我们在 login.po.ts 中定义我们的页面和一些函数。

设置 login.po.ts

打开文件并像前面一样编写一些代码。

import { browser, by, element } from 'protractor';

export class LoginPage {
    navigateTo(){
        return browser.get('/login');
    }
}

现在我们将编写代码来查找电子邮件和密码文本框。

getEmailTextbox() {
 return element(by.name('email'));
}

getPasswordTextbox() {
 return element(by.name('password'));
}

设置 login.e2e-spec.ts

在开始编写测试之前,是时候设置我们的 spec 文件了。

import { LoginPage } from './login.po';

describe('Login tests', () => {
    let page: LoginPage;

    beforeEach(() => {
        page = new LoginPage();
        page.navigateTo();        
    });
});

我们已经导入了我们的 LoginPage 并对其进行了初始化。现在是时候编写测试了。

it('Login form should be valid', () => {
 page.getEmailTextbox().sendKeys('info@sibeeshpassion.com');
 page.getPasswordTextbox().sendKeys('1234');

 let form = page.getForm().getAttribute('class');

 expect(form).toContain('ng-valid');
});

在这里,我们所做的是,使用 sendKeys 函数设置文本框的值,然后查找我们表单的 class 属性,以便我们可以检查它是否有效。如果表单有效,表单将具有 class ng-valid,否则,它将具有 ng-invalid 类。

运行端到端测试

运行端到端测试就像从日志中掉下来一样容易。由于我们使用 Angular CLI,我们所要做的就是运行命令 ng e2e。这将在我们的 package.json 文件中设置。

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  }

现在让我们运行 ng e2e

PS F:\My Projects\ng5> ng e2e

如果一切顺利,您的应用程序应该会在浏览器中打开并测试该功能。您还将收到一条消息:“Chrome 正在被自动化测试软件控制”。

ng e2e 浏览器输出

在您的终端控制台中,您应该会看到一条消息,说明所有测试都已通过。

自动化测试输出

编写更多测试

让我们编写更多测试来检查其他功能。

检查表单是否无效

要检查表单是否无效,我们需要向表单传递一些无效数据。最终的测试应如下所示:

it('Login form should be invalid', () => {
 page.getEmailTextbox().sendKeys('');
 page.getPasswordTextbox().sendKeys('');

 let form = page.getForm().getAttribute('class');

 expect(form).toContain('ng-invalid');
});

检查值是否已保存到本地存储

您可能已经查看了我们在应用程序中单击 **Login** 按钮时执行的功能。目前,我们只是将电子邮件值保存到本地存储。下面是单击 **Login** 按钮时调用的函数。

login() {
    if (this.form.valid) {
      this.auth.sendToken(this.form.value.email)
      this.myRoute.navigate(["home"]);
    }
  }

这是我们 AuthService 中的 sendToken 方法。

sendToken(token: string) {
    localStorage.setItem("LoggedInUser", token)
 }

现在,我们将为此功能编写自动化测试。首先,让我们在 login.po.ts 中添加一个返回 **Submit** 按钮的函数。

getSubmitButton() {
 return element(by.css('#btnSubmit'));
}

现在,像这样编写测试:

it('Should set email value to local storage', () => {
 page.getEmailTextbox().sendKeys('info@sibeeshpassion.com');
 page.getPasswordTextbox().sendKeys('1234');

 page.getSubmitButton().click();

 let valLocalStorage = browser.executeScript
                       ("return window.localStorage.getItem('LoggedInUser');");
 expect(valLocalStorage).toEqual('info@sibeeshpassion.com');
});

正如您所看到的,我们实际上向表单设置了一些有效数据,并触发了我们按钮的单击事件,电子邮件文本框的值被保存到 localStorage。在我们的测试中,我们通过执行脚本 browser.executeScript("return window.localStorage.getItem('LoggedInUser');") 来检查 localStorage 的值。如果一切正常,我们应该会看到如下输出。

Protractor 中的 localStorage

结论

非常感谢您的阅读。我很快就会带着一篇关于同一主题的帖子回来。我是否遗漏了您认为需要的东西?您是否觉得这篇文章有用?希望您喜欢这篇文章。请分享您宝贵的建议和反馈。

现在轮到你了。你有什么想法?

没有评论的博客算不上博客,但请尽量保持主题相关。如果您有一个与本文无关的问题,最好将其发布到 C# Corner、Code Project、Stack Overflow、ASP.NET 论坛,而不是在此处评论。请在下面的评论部分发布您的问题,我会尽力提供帮助。

历史

  • 2018年7月18日:初始版本
© . All rights reserved.