角度测试:开发人员简介

在本指南中,我们将研究如何在Angular 5项目中编写自动化测试。 Angular Testing是每个使用Angular CLIAngular快速启动项目设置的项目中可用的核心功能。

Angular测试的主题非常广泛,因为它是一个复杂且非常复杂的主题。 要完全覆盖它,将需要几章或一门全长课程。 因此,在本指南中,我将向您介绍入门的基础知识。

先决条件

在撰写本文时,Angular 5.2是当前的稳定版本-这就是我们将在此处使用的版本。 本指南假定您至少对Angular 4+基础有扎实的了解。 还假设您至少了解该概念或具有一些编写自动化测试的技能。

我们将基于Angular的官方初学者教程建立测试示例,以演示如何编写组件和服务的测试。 您可以在我们的GitHub存储库上找到带有测试的完整代码。 在本指南的最后,您应该具备在Angular 5中实施多个通过测试的技能。

角度测试:通过测试

角度测试技术

如您所知,Angular项目由模板,组件,服务和模块组成。 它们都在称为Angular的环境中运行。 尽管可以编写隔离的测试,但您实际上并不知道您的代码将如何与Angular环境中的其他元素进行交互。

幸运的是,我们有几种技术可以帮助我们以最少的精力编写此类单元测试。

1.角度测试实用程序

这是为Angular代码构建测试环境所需的一组类和函数。 您可以在Angular的api文档中找到它们。 最重要的是TestBed 。 它用于配置Angular模块的方式与@NgModule相同,只是它准备了要测试的模块。 它具有configureTestingModule函数,您可以在其中提供所有必需的依赖关系,以使组件在测试环境中起作用。 这是准备在测试环境中运行的dashboard component的示例。 该组件需要几个依赖项才能运行测试:

TestBed.configureTestingModule({
  imports: [ RouterTestingModule ],
  declarations: [ DashboardComponent ],
  schemas: [ NO_ERRORS_SCHEMA ],
  providers: [
    {
      provide: HeroService,
      useClass: MockHeroService
    }
  ],
})
.compileComponents();

我们将在下面更进一步地仔细研究这里发生的事情。

2.茉莉花

Jasmine是编写Angular测试的事实框架。 基本上,这是一个使用行为驱动的表示法的测试框架。 用Jasmine编写测试非常简单:

describe('createCustomer' () => {

  it('should create new customer',(customer) => {
    ...
    expect(response).toEqual(newCustomer)
  });

  it('should not create customer with missing fields', () => {
    ...
    expect(response.error.message).toEqual('missing parameters')
  });

  it('should not create customer with existing record', () => {
    ...
    expect(response.error.message).toEqual('record already exists')
  });
});

茉莉花测试的解剖结构至少由两个元素组成:一个describe函数(它是一组测试)和一个it函数(它是测试本身)。 通常,我们使用describe来表示我们关注的功能,例如createCustomer() 。 然后,在套件中,我们创建多个it测试。 每个测试都将目标函数置于不同的条件下,以确保其行为符合预期。 您可以参考Jasmine文档以获得更多信息。

3.因果报应

Karma是用于在浏览器环境中针对测试代码执行源代码的工具。 它支持在为其配置的每个浏览器中运行测试。 结果显示在命令行和浏览器上,供开发人员检查哪些测试通过或失败。 Karma还会监视文件,并且只要文件发生更改,就可以触发重新运行测试。 在Angular项目的根目录下,我们具有用于配置Karma的文件karma.conf 。 内容应如下所示:

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};

请查看Karma的配置文档以了解如何对其进行自定义。 如您所见,Chrome被列为用于运行测试的浏览器。 您需要定义一个名为CHROME_BIN的环境变量,该变量指向您的Chrome浏览器可执行文件的位置。 如果您使用的是Linux,只需将此行添加到.bashrc文件中:

export CHROME_BIN="/usr/bin/chromium-browser"

为了使Karma运行您的测试,必须确保测试文件以.spec.ts 。 您应该注意,Karma设计为主要运行单元测试。 要运行端到端测试,我们需要另一个工具Protractor,接下来我们将对其进行研究。

4.量角器

量角器是Angular的端到端测试框架。 它在真实的浏览器中运行测试,并像真实的人一样与之交互。 与单元测试不同,在单元测试中,我们测试各个功能,而在这里,我们测试整个逻辑。 量角器能够填写表格,单击按钮,并确认预期的数据和样式显示在HTML文档中。 就像噶,量角器有你的角项目的根,自己的配置文件protractor.conf

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './e2e/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};

您可以在此处找到有关其配置的文档。 与Jasmine / Karma测试不同,量角器测试位于src文件夹之外的e2e文件夹中。 我们稍后将研究编写端到端测试。 现在,让我们开始编写单元测试。

写作单元测试

如前所述,Angular附带了为项目编写自动化测试所需的一切。 要开始测试,只需运行以下命令:

ng test

业力将加速运行所有可用的测试。 假设您刚刚完成了“英雄之旅”教程,那么您应该有一个类似的报告:

角度测试:测试失败

当您使用Angular CLI工具生成组件,服务和类时,将创建这些测试。 在创建时,这些测试中的代码是正确的。 但是,当您向组件和服务中添加代码时,测试就失败了。 在下一节中,我们将看到如何解决损坏的测试。

测试组件

对组件进行单元测试可以通过两种方式进行。 您可以单独对其进行测试,也可以在Angular环境中对其进行测试,以查看其如何与其模板和依赖项进行交互。 后者听起来很难实现,但是使用Angular Testing Utilities使创建测试更加容易。 这是使用Angular CLI工具创建组件时为您生成的测试代码的示例:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { HeroesComponent } from './heroes.component';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ HeroesComponent ]
    })
    .compileComponents();
  }));

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

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

在第一个beforeEach()函数中,我们使用TestBed.configureTestingModule函数创建用于测试组件的模块环境。 它类似于NgModules ,除了在这种情况下,我们正在创建一个用于测试的模块。
在第二个beforeEach()函数中,我们创建了component-under-test的实例。 完成此操作后,我们将无法再次配置TestBed ,因为将引发错误。

最后,我们有了should be created的规范,我们should be created其中确认component已初始化。 如果此测试通过,则表示组件应在Angular环境中正常运行。 但是,如果失败,则可能是组件具有我们未包含在测试配置中的特定依赖性。 让我们看看如何处理不同的问题。

测试使用另一个组件的组件

在Angular中构建用户界面时,我们经常通过选择器引用模板文件中的其他组件。 看一下以下示例dashboard.component.html

<h3>Top Heroes</h3>
...
</div>

<app-hero-search></app-hero-search>

在此示例中,我们引用另一个具有选择器app-hero-search 。 如果尝试按原样运行初始测试,它将失败。 这是因为我们尚未在测试环境中声明引用的组件。 在单元测试中,我们将所有精力都放在要测试的组件上。 我们在单元测试中不关心其他组件。 我们必须假设它们正在按预期工作。 在我们的测试中包括引用的组件可能会污染结果。 为了解决此问题,我们可以模拟引用的组件,也可以使用NO_ERRORS_SCHEMA指令将其忽略。 这是一个例子:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';

import { DashboardComponent } from './dashboard.component';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ DashboardComponent ],
      schemas: [ NO_ERRORS_SCHEMA
    })
    .compileComponents();
  }));

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

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

现在,此测试应该不会出现组件依赖性问题。 但是,此测试尚未通过,因为还有另一种情况我们必须处理……

测试使用模块的组件

这次让我们检查一下hero-detail.component.html

<div *ngIf="hero">
  <h2>{{ hero.name | uppercase }} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name"/>
    </label>
  </div>
  <button (click)="goBack()">go back</button>
  <button (click)="save()">save</button>
</div>

在这里,我们使用的是ngModel指令,该指令来自FormsModule库。 为了编写支持此模块的测试,我们只需要导入FormsModule并将其包含在TestBed配置中:

import { FormsModule } from '@angular/forms';
...
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ HeroDetailComponent ],
      imports: [ FormsModule],
    })
    .compileComponents();
  }));
  ...

那应该可以解决FormsModule的问题。 但是,我们需要在测试环境中指定更多依赖关系。

测试使用路由模块的组件

让我们研究一下hero-detail.component.ts构造函数:

constructor(
  private route: ActivatedRoute,
  private location: Location,
  private heroService: HeroService   
) {}

该组件具有处理路由的ActivatedRouteLocation依赖性。 在我们的测试代码hero-detail.component.spec.ts ,我们可以实现类的模拟版本。 但是,我发现最好的解决方案是这样导入RouterTestingModule

import { RouterTestingModule } from ’@angular/router/testing’;
...
beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ HeroDetailComponent ],
    imports: [ FormsModule, RouterTestingModule ],
  })
  .compileComponents();
}));

RoutingTestingModule可以轻松解决我们测试代码中的ActivateRouteLocation依赖关系。 RoutingTestingModule还可以处理涉及路由的其他情况。 在dashboard.component.html查看以下代码:

<h3>Top Heroes</h3>
<div class="grid grid-pad">
  <a *ngFor="let hero of heroes" class="col-1-4" routerLink="/detail/{{hero.id}}">
    <div class="module hero">
      <h4>{{hero.name}}</h4>
    </div>
  </a>
</div>

注意,我们有一个名为routerLink的指令。 这是AppRoutingModule库提供的指令。 如果运行仪表板测试,则由于该依赖关系而将失败。 要解决此问题,只需在dashboard.component.spec.ts实现RoutingTestingModule ,就像我们对hero-detail.component.spec.ts所做hero-detail.component.spec.ts

现在让我们看一下如何测试依赖于服务的组件。

测试使用服务的组件

每个组件至少需要一个服务来处理逻辑。 有两种测试使用服务的组件的方法。 让我们看一下message.service.ts ,它正在由message.component.ts

import { Injectable } from ’@angular/core’;

@Injectable()
export class MessageService {
  messages: string[] = [];

  add(message: string) {
    this.messages.push(message);
  }

  clear() {
    this.messages = [];
  }
}

MessageService具有非常简单的实现。 它不使用任何外部依赖项。 虽然建议从单元测试中排除外部逻辑,但是我们将在这里做一个例外。 我认为不需要使测试复杂化。 因此,我认为最好在测试中包含该服务。 这是message.component.spec.ts的测试代码:

import { MessageService } from '@services/message.service';
...

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ MessagesComponent ],
    providers: [ MessageService ]
  })
  .compileComponents();
}))

现在让我们看看另一个服务hero-service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
...
@Injectable()
export class HeroService {

  private heroesUrl = 'api/heroes';

  constructor(
    private http: HttpClient,
    private messageService: MessageService) { }

    /** GET heroes from the server */
    getHeroes (): Observable<Hero[]> {
      return this.http.get<Hero[]>(this.heroesUrl)
      .pipe(
         tap(heroes => this.log(`fetched ${heroes.length} heroes`)),
         catchError(this.handleError('getHeroes', []))
       );
    }

    getHero(id: number): Observable<Hero> {
      const url = `${this.heroesUrl}/${id}`;
      return this.http.get<Hero>(url).pipe(
        tap(_ => this.log(`fetched hero id=${id}`)),
        catchError(this.handleError<Hero>(`getHero id=${id}`))
      );
    }
    ...
}

HeroService类包含很多逻辑-总共约104行。 它具有多种依赖性,包括一个到另一个服务。 而且,其所有功能都是异步的。 这样复杂的代码很可能会污染我们的单元测试。 因此,我们应排除其逻辑。 我们通过创建模拟版本的hero.service.ts做到这hero.service.ts 。 只需创建一个新文件,并将其hero.service.mock.ts 。 模拟其功能,以剥离其核心逻辑:

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { Hero } from '@models/hero.model';

export class MockHeroService {
  getHeroes(): Observable<Hero[]> {
    return of([]);
  }

  getHero() {
    return of({});
  }
}

您可以看到模拟版本更简单。 现在,它污染我们的单元测试的可能性为零。 要将其包含在我们的组件规格文件中,我们可以这样实现:

import { HeroService } from '@services/hero.service';
import { MockHeroService } from '@services/hero.service.mock';
...

  TestBed.configureTestingModule({
      declarations: [ HeroDetailComponent ],
      imports: [ FormsModule, RouterTestingModule ],
      providers: [
        {
          provide: HeroService,
          useClass: MockHeroService
        },
      ],
    })
    .compileComponents();
  }));
...

我们使用providers选项将MockHeroService注入我们的服务。 使用该服务为所有组件的测试代码实现此功能。

测试服务

现在,我们已经处理了测试组件时发生的一些常见情况,下面让我们看一下如何测试服务。 服务执行应用程序的核心逻辑,因此,全面测试其功能非常重要。 如前所述,Angular测试是一个很深的主题,因此我们只是在这里进行介绍。

打开hero.service.ts并检查功能。 让我重点介绍几个:

...
  /** GET heroes from the server */
  getHeroes (): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl)
    .pipe(
       tap(heroes => this.log(`fetched ${heroes.length} heroes`)),
       catchError(this.handleError('getHeroes', []))
     );
  }

  /** UPDATE: update selected hero on the server */
  updateHero (hero: Hero): Observable<any> {
    return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
      tap(_ => this.log(`updated hero id=${hero.id}`)),
      catchError(this.handleError<any>('updateHero'))
    );
  }
...

每个函数由几行代码组成,但仍在继续。 为了全面测试每种情况,我们需要考虑多种情​​况。 当我们执行getHeroes() ,服务器可能

  • 发回英雄名单
  • 发回一个空清单
  • 抛出错误
  • 无法回应。

您也许可以考虑将更多可能的方案添加到列表中。 现在我们已经考虑了可能的场景,是时候编写测试了。 这是一个如何为HeroService编写specHeroService

import { TestBed, inject } from '@angular/core/testing';
import { HttpClientModule, HttpClient, HttpResponse } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

import { HeroService } from './hero.service';
import { MessageService } from './message.service';
import { Hero } from '@models/hero.model';

const mockData = [
  { id: 1, name: 'Hulk' },
  { id: 2, name: 'Thor'},
  { id: 3, name: 'Iron Man'}
] as Hero[];

describe('HeroService', () => {

  let service;
  let httpTestingController: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      providers: [HeroService, MessageService]
    });
    httpTestingController = TestBed.get(HttpTestingController);
  });

  beforeEach(inject([HeroService], s => {
    service = s;
  }));

  beforeEach(() => {
    this.mockHeroes = [...mockData];
    this.mockHero = this.mockHeroes[0];
    this.mockId = this.mockHero.id;
  });

  const apiUrl = (id: number) => {
    return `${service.heroesUrl}/${this.mockId}`;
  };

  afterEach(() => {
    // After every test, assert that there are no more pending requests.
    httpTestingController.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('getHeroes', () => {

    it('should return mock heroes', () => {
      service.getHeroes().subscribe(
        heroes => expect(heroes.length).toEqual(this.mockHeroes.length),
        fail
      );
      // Receive GET request
      const req = httpTestingController.expectOne(service.heroesUrl);
      expect(req.request.method).toEqual('GET');
      // Respond with the mock heroes
      req.flush(this.mockHeroes);
    });
  });

  describe('updateHero', () => {

    it('should update hero', () => {
      service.updateHero(this.mockHero).subscribe(
        response => expect(response).toEqual(this.mockHero),
        fail
      );
      // Receive PUT request
      const req = httpTestingController.expectOne(service.heroesUrl);
      expect(req.request.method).toEqual('PUT');
      // Respond with the updated hero
      req.flush(this.mockHero);
    });
  });

  describe('deleteHero', () => {

    it('should delete hero using id', () => {
      const mockUrl = apiUrl(this.mockId);
      service.deleteHero(this.mockId).subscribe(
        response => expect(response).toEqual(this.mockId),
        fail
      );
      // Receive DELETE request
      const req = httpTestingController.expectOne(mockUrl);
      expect(req.request.method).toEqual('DELETE');
      // Respond with the updated hero
      req.flush(this.mockId);
    });

    it('should delete hero using hero object', () => {
      const mockUrl = apiUrl(this.mockHero.id);
      service.deleteHero(this.mockHero).subscribe(
        response => expect(response).toEqual(this.mockHero.id),
        fail
      );
      // Receive DELETE request
      const req = httpTestingController.expectOne(mockUrl);
      expect(req.request.method).toEqual('DELETE');
      // Respond with the updated hero
      req.flush(this.mockHero.id);
    });
  });
});

这只是我们应该如何为与HttpClientModule交互的服务编写测试的示例。 检查每个测试,并注意我们正在使用HttpTestingController类来拦截请求。 在此测试中,我们将控制输入和输出以创建不同的场景。 这些测试的主要目的是确保我们的服务方法能够妥善处理每种情况。 请注意,我们尚未完全实现hero.service.spec.ts所需的所有测试,因为这超出了本指南的范围。

在本指南结束之前,还有更多主题需要我们研究。

端到端角度测试

单元测试可确保组件和服务在受控的测试环境中正确运行。 但是,不能保证组件和服务将在Angular环境中相互交互。 这就是为什么我们需要执行端到端测试。 端到端测试是一种模拟人工测试的测试。 换句话说,这些测试旨在通过​​浏览器界面以与我们相同的方式与我们的应用程序进行交互。

在我们的英雄之旅应用程序中,我们可以测试一些用例,例如确保-

  • 仪表盘组件上显示五个英雄
  • 所有英雄均显示在英雄组件上
  • 导航链接没有损坏
  • 可以创建一个新英雄
  • 英雄可以更新
  • 英雄可以删除。

随着更多功能的实现,您可以继续添加到此列表中。 理想的端到端测试包含两个部分。

第一部分是一个帮助程序文件,提供了特定于组件的帮助程序功能。 这是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();
  }
}

定义了辅助功能后,您可以在编写e2e测试时轻松访问它们。 这是e2e/app.e2e.spec.ts的示例:

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

describe('angular-tour-of-heroes App', () => {
  let page: AppPage;

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

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

要运行此测试,只需执行以下命令:

ng e2e

如果这是您第一次执行此命令,则可能需要Internet连接。 测试完成后,您很可能会收到一条类似以下内容的失败消息:

angular-tour-of-heroes App
   ✗ should display welcome message
     - Expected 'Tour of Heroes' to equal 'Welcome to app!'.

让我们按照以下方法修复错误。 我还添加了一个测试,以确保我们在app-routing.module.ts指定的重定向有效:

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

describe('angular-tour-of-heroes App', () => {
  let page: AppPage;

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

  it('should redirect to dashboard', async () => {
    page.navigateTo();
    const url = await browser.getCurrentUrl();
    expect(url).toContain('/dashboard');
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('Tour of Heroes');
  });
});

再次运行测试。 现在,我们应该通过测试:

angular-tour-of-heroes App
  ✓ should redirect to dashboard
  ✓ should display welcome message

观看e2e测试运行真是一种感觉。 它使您确信应用程序将在生产中平稳运行。 既然您已经了解了e2e ,现在该该继续进行另一个出色的测试功能了。

代码覆盖率

作为开发人员,我们最大的问题之一是“我们测试了足够的代码吗?” 幸运的是,我们拥有可以生成“代码覆盖率”的工具,可以确定测试了多少代码。 要生成报告,只需运行以下命令:

ng test --watch=false --code-coverage

Coverage文件夹将在Angular项目的根目录下创建。 浏览该文件夹,您将找到index.html 。 使用网络浏览器打开它。 您应该会看到以下内容:

角度测试:代码覆盖率报告

我不会在这里详细介绍,但是您可以看到有些类已经过完全测试,而另一些还没有完全测试过。 由于时间和资源的可用性,通常并非总是可能实现100%的测试覆盖率。 但是,您可以与您的团队决定最小的数量。 要指定最小值,请使用karma.conf来配置代码覆盖率设置,如下所示:

coverageIstanbulReporter: {
  reports: [ 'html', 'lcovonly' ],
  fixWebpackSourcePaths: true,
  thresholds: {
    statements: 80,
    lines: 80,
    branches: 80,
    functions: 80
  }
}

上面的阈值指定了至少80%的单元测试范围。

其他实用程序

现在,我们已经介绍了Angular测试的基础知识。 但是,我们可以通过进一步执行一些步骤来提高代码质量。

1.棉绒

Angular附带了用于执行代码整理的工具。 只需执行以下代码即可对您的项目进行检查:

ng lint

此命令将发出有关代码的警告-例如,在您忘记使用分号或使用太多空格的地方。 该命令还将帮助您识别未使用的代码和语句中的某些错误。 通常使用此命令将确保团队中的每个人都使用一致的样式编写代码。 您可以在tslint.json文件中进一步自定义lint选项。

2.智能代码编辑器

对于代码编辑器和IDE,我个人最喜欢的是AtomSublime Text 。 但是,我最近发现了Visual Studio Code ,它具有更吸引人的功能。 它是一个免费的代码编辑器,可以在Windows,macOS和Linux中运行。 它从Atom那里借鉴了很多东西,除了它还具有我要强调的其他功能:

  • 智能感知
  • 突出显示错误
  • 现代角度扩展

当前,在VSCode中内置了Atom和Sublime Text都没有这些功能。 您只需要安装所需的语言扩展名。 键入代码时,Intellisense功能会为您列出选项。 就像自动完成一样,但是具有语法正确选项的特定列表。 使用此功能,很难犯语法错误。 您还可以查看函数的文档,以查看返回类型和所需的输入。

角度测试:Visual Studio代码

Visual Studio Code还具有适当的错误突出显示功能。 它不仅检查语法错误,而且确保分配具有正确的类型。 例如,如果您尝试将数组分配给Observable函数的结果,则会为您突出显示错误。 VSCode还具有与Angular 5兼容的Angular扩展。

拥有一个IDE可以在您键入时检查代码是否存在错误,这对于提高生产力非常有用。 它可以帮助您减少花费在纠正错误上的时间。 可能还有其他代码编辑器可以完成相同的任务,但是目前,我建议为Angular项目使用Visual Studio Code。

3.持续整合

持续集成(CI)是自动化测试和构建的过程。 作为开发人员,我们经常孤立地工作几个星期或更长时间。 当我们将更改合并到master分支时,会产生许多错误和冲突。 这可能需要很多时间才能解决。

CI鼓励开发人员编写测试并经常以较小的位数提交任务。 CI服务器将自动构建并运行测试,以帮助开发人员及早发现错误,从而减少冲突和问题。 Angular开发人员可以使用许多CI解决方案。 查看SitePoint的有关在Travis测试Jasmine和Karma的教程。

包起来

我们可以访问有关自动化测试的大量信息,以及用于测试驱动的开发的框架,这些信息可以帮助我们编写测试。 但是,有两个原因导致我们不总是编写测试:

  1. 不要为新应用编写测试。 该项目的范围将迅速变化,这取决于客户的需求或市场的反应。
  2. 编写测试除了实现功能外还需要更多时间。 当功能范围更改时,还需要时间来维护。 如果您的预算很低,可以跳过编写测试。 对您拥有的资源要切实可行。

因此,剩下的问题是什么时候该编写测试。 这里有一些指针:

  1. 您已经完成了原型阶段,并确定了应用程序的核心功能。
  2. 您的项目有足够的资金。

现在,假设您已决定强制执行TDD,则有很多好处可以收获:

  1. 编写可以测试的代码意味着您正在编写质量更高的代码。
  2. 作为开发人员,您将更有信心将最新版本发布到生产环境中。
  3. 编写测试是记录代码的一种方式。 这意味着未来的开发人员将可以更轻松地升级遗留代码。
  4. 您无需雇用人员进行质量控制,因为CI服务器将为您完成这项工作。

如果您决定完全跳过产品就绪应用程序的测试,请准备在将来面对生气和失望的客户。 错误的数量将随着代码库大小的增加而呈指数增长。

希望这对Angular测试是一个有用的介绍。 如果您想了解更多信息,建议您首先使用Angular 5官方文档。 除非另有说明,否则大多数信息都针对较旧的Angular版本。

让我们知道您在Angular测试中有任何有趣的提示!

From: https://www.sitepoint.com/angular-testing-introduction/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值