npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

shark-test

v1.0.4

Published

test cli-tool for shark angular projects

Downloads

5

Readme

shark-test

为 shark 项目提供搭建测试环境的工具。

目录

一、安装

$ npm install --save-dev shark-test

二、安装测试相关的依赖

$ npm install --save classlist
$ npm install --save @types/jasmine

三、添加执行测试指令

在package.json文件中,添加测试指令


{
    "scripts": {
        "test": "./node_modules/shark-test/dist/src/index.js test"
    },
}

四、初始化配置文件

执行以下初始化命令,自动创建四个测试相关的配置文件


st init

或者


shark-test init

五、测试工具配置文件说明

在项目根目录下创建配置文件shark-test-conf.json,所有配置项都设了默认值

{
    //项目的源码路径--所有配置基于该路径
    "basePath": "src/main/webapp",
    // 测试入口文件(.spec.ts)
    "main": "test.ts",
    // 测试环境的polyfills
    "polyfills": "polyfills",
    // karma的配置文件
    "configFile": "karma.conf.js",
    // 项目组件目录
    "componentPath": "app",
    // 项目资源
    "assets": ["assets", "favicon.ico"],
    // 项目模板
    "indexTemplate": "index.ejs"
}

测试入口文件

在配置文件中设置的basePath路径下添加用于测试入口文件test.ts


// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import "zone.js/dist/long-stack-trace-zone";
import "zone.js/dist/proxy";
import "zone.js/dist/sync-test";
import "zone.js/dist/jasmine-patch";
import "zone.js/dist/async-test";
import "zone.js/dist/fake-async-test";

import { getTestBed } from '@angular/core/testing';

import {
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

测试环境的polyfills.ts

在配置文件中设置的basePath路径下添加用于测试环境的polyfills.ts文件

/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/map';
import 'core-js/es6/weak-map';
import 'core-js/es6/set';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
import 'classlist.js';  // Run `npm install --save classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
import 'core-js/es6/reflect';


/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';


/**
 * Required to support Web Animations `@angular/platform-browser/animations`.
 * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
 **/
// import 'web-animations-js';  // Run `npm install --save web-animations-js`.



/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import 'zone.js/dist/zone';  // Included with Angular CLI.
/***************************************************************************************************
 * APPLICATION IMPORTS
 */

karma配置文件

在项目根目录下创建karma配置文件karma.conf.js


// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

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

六、使用

运行测试代码


npm run test

或者


st test

或者


shark-test test

七、FAQ

八、angular测试手册

各类测试说明:

一、Component(带输入输出的组件、带路由的组件、带依赖的组件、嵌套组件)

  1. 组件类测试

像测试服务类一样测试组件类,单独测试组件类本身而不必涉及DOM。

例子1:下面是一个列表组件ProduceScheduleComponent(带路由的组件、带依赖的组件、嵌套组件)

import { Component, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { ProduceScheduleService } from './produce-schedule.service';
@Component({
    styleUrls: ['./produce-schedule.component.scss'],
    templateUrl: './produce-schedule.component.html'
})

export class ProduceScheduleComponent {
    purchaseOrderList: Array<any>;//采购单列表
    pagination: any;

    constructor(
        private router: Router,
        private route: ActivatedRoute,
        private produceScheduleService: ProduceScheduleService
    ) {
    }

    ngOnInit() {
        let params={
          page:1,
          size:20
        }
        this.getPurchaseList(params)
    }

    getPurchaseList(params) {
        this.produceScheduleService.getProceedingList(params).then(data => {
            this.purchaseOrderList = data.result
            this.pagination = data.pagination
        }).catch(err => { })
    }
}

组件对应的测试代码

import { TestBed, async } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { SharkValid, SharkValidForm, Common } from "@shark/shark-angularX";
import { RouterTestingModule } from '@angular/router/testing';
import { ProduceScheduleComponent } from './produce-schedule.component';
import { ProduceScheduleService } from './produce-schedule.service';

describe('#ProduceScheduleComponent', () => {
    let produceScheduleService;
    let fixture;
    let component;
    let getProceedingListSpy;
    let selectDom;
    let data = {
        code: 200,
        data: {
            pagination: {
                page: 1,
                size: 10,
                total: 354,
                totalPage: 36,
                offset: 0
            },
            result: [{}]
        }
    };
    beforeEach(() => {
        // 创建 ProduceScheduleService实例,并伪造了`getProceedingList()`方法
        const produceScheduleService = jasmine.createSpyObj('ProduceScheduleService', ['getProceedingList']);
        // 设置伪造的`getProceedingList()`方法的返回值
        getProceedingListSpy = produceScheduleService.getProceedingList.and.returnValue(new Promise<any>((resolve, reject) => { resolve(data) }));
        // 引入组件测试环境的依赖
        TestBed.configureTestingModule({
          // RouterTestingModule:建立测试需要的路由
            imports: [RouterTestingModule],
            declarations: [ProduceScheduleComponent, SharkValidForm, SharkValid],
            providers: [{ provide: ProduceScheduleService, useValue: produceScheduleService }, Common],
            // 对嵌套组件,可能包含更多组件
            //NO_ERRORS_SCHEM会要求 Angular 编译器忽略不认识的那些元素和属性
            schemas: [NO_ERRORS_SCHEMA]
        });
        fixture = TestBed.createComponent(ProduceScheduleComponent);
        component = fixture.componentInstance;
    });
    // 测试组件是否创建成成功
    it('should create the ProduceScheduleComponent', async(() => {
        expect(component).toBeTruthy();
    }));
    // 测试getProceedingList方法有没有被调用
    it('should call getProceedingList after component initialized', () => {
        fixture.detectChanges(); 
        // sync spy result shows getProceedingList immediately after init
        expect(getProceedingListSpy.calls.any()).toBe(true, 'getProceedingList called');
    });
});

例子2:下面是一个列表组件ProduceScheduleComponent(带输入的组件)

调用组件代码

<schedule-item [purchaseItem]="item"></schedule-item>

ScheduleItemComponent组件的测试代码

import { TestBed, async } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { SharkToastrService, SharkModalService } from "@shark/shark-angularX";
import { RouterTestingModule } from '@angular/router/testing';
import { ScheduleItemComponent } from './schedule-item.component';
import { PurchaseStatePipe } from '../purchase-state.pipe'
import { SharedModule } from '../../../shared/shared.module'

describe('#ScheduleItemComponent', () => {
    let fixture;
    let component;
    let purchaseOrderEl;
    let purchaseItem = {
        purchaseOrder: "1375870000011112-20171024-32",
        allowModify: true,
        isVersion1_0: 0,
        purchaseOrderType: 2,
        purchaseOrderSaleModel: 1,
    }
    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [RouterTestingModule, SharedModule],
            declarations: [ScheduleItemComponent, PurchaseStatePipe],
            providers: [SharkToastrService, SharkModalService],
            // test will not load sub components
            schemas: [NO_ERRORS_SCHEMA]
        });
        fixture = TestBed.createComponent(ScheduleItemComponent);
        component = fixture.componentInstance;
        // 给组件传入purchaseItem数据
        component.purchaseItem = purchaseItem;
        fixture.detectChanges();

    });

    // 测试输入数据中采购单号在页面显示的正确性
    it('should display same purchaseOrder as purchaseItem.purchaseOrder ', () => {
        // 查找显示采购单号的标签元素
        purchaseOrderEl = fixture.nativeElement.querySelectorAll('b')[0];
        const purchaseOrder = purchaseItem.purchaseOrder;
        expect(purchaseOrderEl.textContent).toContain(purchaseOrder);
    });
});
  1. 组件Dom测试

测试组件是否能正常渲染出来、响应用户的输入和查询或与它的父组件和子组件相集成

例子:略

二、Directive

Directive作为宿主元素的属性来被使用的, 需要创建宿主元素来对指令的行为进行测试

例子:ShowAndHideListDirective的功能是控制table行数展示,并有展开、收起功能

import { Directive, ElementRef, HostListener, Input } from '@angular/core'

@Directive({
    selector: '[showAndHideList]'
})
export class ShowAndHideListDirective {
    private el: HTMLElement;
    @Input('showAndHideList') initNum: number;

    constructor(el: ElementRef) {
        this.el = el.nativeElement;
        this.el.style.backgroundColor = '#f4f4f4';
    }
    @HostListener('click') onClick() {
        var isHide = false;
        var groups = this.el.parentElement.parentElement.getElementsByTagName('tr');
        for (var i = this.initNum; i < groups.length; i++) {
            var item = groups[i];
            if (item.id == 'showAndHide') {
                break;
            }
            if (item.classList.contains('hide')) {
                item.classList.remove('hide');
                isHide = true;
            } else {
                item.classList.add('hide');
                isHide = false;
            }
        }
        if (isHide) {
            this.el.innerHTML = "收起商品<i class = 'icon-up'></i>"
        } else {
            this.el.innerHTML = "全部商品<i class = 'icon-down'></i>"
        }
    }
}

ShowAndHideListDirective指令的测试代码

import { Component, OnInit } from '@angular/core'
import { TestBed } from '@angular/core/testing';
import { ShowAndHideListDirective } from './showAndHideList.directive'

@Component({
    template: `
    <div class="table-wrap margin-b-4x">
      <table class="table table-full text-center">
          <thead>
              <tr>
                  <th>严选SKU ID</th>
                  <th>商品名称</th>
                  <th>规格</th>
              </tr>
          </thead>
          <tbody class="text-center">
              <tr *ngFor="let item of skuItems;index as i " [ngClass]="{'hide':i > initNum-1}">
                  <td>{{item.skuId}}</td>
                  <td>{{item.itemName}}</td>
                  <td>{{item.specValueDesc}}</td>
              </tr>
              <tr [ngClass]="{'hide': initNum >= skuItems.length}" id="showAndHide">
                  <td [showAndHideList]="initNum" [attr.colspan]="3" class="text-center text-gold text-cursor-pointer ">全部商品
                      <i class="icon-down"></i>
                  </td>
              </tr>
          </tbody>
      </table>
    </div>`,
    styleUrls: ['./test.component.scss']
})
class TestComponent {
    initNum: any = 3;
    skuItems: any = [{
        itemId: 10843001,
        itemName: "简约收纳盒",
        specValueDesc: "尺寸:大",
        skuId: 10924002
    }, {
        itemId: 10843001,
        itemName: "简约收纳盒",
        specValueDesc: "尺寸:中",
        skuId: 10924003
    }, {
        itemId: 10843001,
        itemName: "简约收纳盒",
        specValueDesc: "尺寸:小",
        skuId: 10924004
    },
    {
        itemId: 10843001,
        itemName: "女士水桶包",
        specValueDesc: "颜色:红",
        skuId: 10924005
    },
    {
        itemId: 10843001,
        itemName: "女士水桶包",
        specValueDesc: "颜色:黑",
        skuId: 10924006
    },
    {
        itemId: 10843001,
        itemName: "女士水桶包",
        specValueDesc: "颜色:白",
        skuId: 10924007
    },
    {
        itemId: 10843001,
        itemName: "男士T恤",
        specValueDesc: "尺寸:M",
        skuId: 10924008
    },
    {
        itemId: 10843001,
        itemName: "男士T恤",
        specValueDesc: "尺寸:L",
        skuId: 10924009
    }
    ];
}
describe('ShowAndHideListDirective', () => {
    let fixture;
    beforeEach(() => {
        fixture = TestBed.configureTestingModule({
            declarations: [ShowAndHideListDirective, TestComponent]
        }).createComponent(TestComponent);
    });
    // 测试表格原本的行数
    it('should have 10 <tr>', () => {
        fixture.detectChanges();
        const tr: any = fixture.nativeElement.querySelectorAll('tr');
        expect(tr.length).toBe(10);
    });
    // 测试表格隐藏的行数
    it('should have 5 <tr> with hide', () => {
        fixture.detectChanges();
        const tr: any = fixture.nativeElement.querySelectorAll('.hide');
        expect(tr.length).toBe(5);
    });
    // 测试点击展开全部按钮后隐藏的行数
    it('should have 0 <tr> with hide', () => {
        const display = fixture.nativeElement.querySelector('#showAndHide');
        display.click();
        const tr: any = fixture.nativeElement.querySelectorAll('.hide');
        expect(tr.length).toBe(0);
    });

});

三、Service(非http服务)

service的测试 非http服务基本上不需要依赖Angular的测试工具集.按普通类对待就好,我们以下面的例子说明

eg .目录结构如下

|-- demo
    |-- master.service.js
    |-- master.service.spec.js
    |-- value.service.js
    |-- value.service.spec.ts

valueService 是一个简单的Service。包含有若干方法

// demo/value.service.js

import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'

@Injectable()
export class ValueService {
    constructor() { }

    getValue() {
        return { data: 123 }
    }

    getValue1() {
        return Observable.of({ data: 456 })
    }

    getValue2() {
        return new Promise((resolve, reject) => {
            resolve({ data: 789 })
        })
    }
}

对ValueService的测试如下:你会发现,和我们平时写代码是一样的,只不过多了一个断言判断

import { ValueService } from './value.service'

describe('MasterService', () => {
    let service: ValueService;

    beforeEach(() => {
        service = new ValueService();
    })

    it('#getValue should return real value', () => {
        expect(service.getValue()).toEqual({ data: 123 })
    })

    it('#getValue1 should return value from observable', async () => {
        service.getValue1().subscribe(value => {
            expect(value).toEqual({ data: 456 })
        })
    })

    it('#getValue2 should return value from promise', async () => {
        service.getValue2().then(value => {
            expect(value).toEqual({ data: 789 })
        })
    })
})

MasterService 依赖了ValueService

// demo/master.service.js

import { Injectable } from '@angular/core';
import { ValueService } from './value.service';

@Injectable()
export class MasterService {
    constructor(private valueService: ValueService) {

    }
    getValue() {
        return this.valueService.getValue();
    }
}

对于有依赖的服务。有很多种测试方案:

  1. 如果依赖的服务相对简单,而且可靠,这时候可以直接引入真实依赖。做法和产品代码环境一样:
// demo/master.service.spec.js

import { ValueService } from './value.service'
import { MasterService } from './demo'

describe('MasterService real', () => {
    let service: ValueService;
    let masterServie: MasterService;

    beforeEach(() => {
        service = new ValueService();
        masterServie = new MasterService(service);
    })

    it('#getValue should return real value', () => {
        expect(masterServie.getValue()).toEqual({ data: 123 })
    })
})
  1. Spy:利用spyObj来替换真实的依赖,并借助testBed创建类并注入服务

import { ValueService } from './value.service'
import { MasterService } from './master.service'
import { TestBed } from '@angular/core/testing';

describe('', () => {
    let masterService: MasterService;
    let valueServiceSpy: jasmine.SpyObj<ValueService>;
    // 每个it测试前都会运行的前置条件
    beforeEach(() => {
        // 创建一个spy对象
        const spy = jasmine.createSpyObj('ValueService', ['getValue']);
        // 在TestBed提供并注入服务
        TestBed.configureTestingModule({
            providers: [
                MasterService,
                { provide: ValueService, useValue: spy }
            ]
        });
        // 获取服务
        masterService = TestBed.get(MasterService);
        valueServiceSpy = TestBed.get(ValueService);
    });
    it('#getValue should return stubbed value from a spy', () => {
        const stubValue = { data: 123 };
        valueServiceSpy.getValue.and.returnValue(stubValue);
        // 数据 断言
        expect(masterService.getValue())
            .toBe(stubValue, 'service returned stub value');
        // 调用次数 断言
        expect(valueServiceSpy.getValue.calls.count())
            .toBe(1, 'spy method was called once');
        // 最近一次返回的数据 断言
        expect(valueServiceSpy.getValue.calls.mostRecent().returnValue)
            .toBe(stubValue);
    });
})

如果我们要测的服务含有http请求,一般是采用mock的方式,考虑到我们项目中的的实际情况,这里的测试验证点在于:
  1、是否发出了请求(代码是否执行)
  2、是否发出了预期的请求(请求路径是否正确、请求方法是否正确)
  3、是否发出了非预期的请求(有没有胡乱发送请求)
  4、对返回code的处理是否符合预期(code = 200正确预期是否成立 ,code = 400,错误的预期是否成立)
eg.
以大制造家中的supplier-info.service.ts为例

import { Injectable } from '@angular/core'
import { Ajax } from '@shark/shark-angularX'
import { Observable } from 'rxjs'

@Injectable()
export class SupplierInfo {
    private getSupplierInfoUrl = '/login/getSupplierInfo.json' 
    infoData: any = void 0
    constructor(
        private ajax: Ajax
    ) {
    }

    getSupplierInfo(obj: Object = {}) {
        if (this.infoData) {
            return Observable.of(this.infoData);
        }
        else {
            return Observable.fromPromise<any>(
                this.ajax.get(this.getSupplierInfoUrl, obj).then(data => {
                    this.infoData = data
                    return this.infoData
                })
            )
        }
    }
}

其测试用例的写法为:具体见注释

import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Common, Ajax, Cookie } from '@shark/shark-angularX'
import { TestBed, tick } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { SupplierInfo } from './supplier-info.service';

describe('#SupplierInfo', () => {
    let httpClient: HttpClient;
    let httpTestingController: HttpTestingController;
    let supplierInfo: SupplierInfo;
    let ajax: Ajax;
    beforeEach(() => {
        TestBed.configureTestingModule({
            // http test 依赖 HttpClientTestingModule
            imports: [HttpClientTestingModule],
            // SupplierInfo 依赖 Ajax,而Ajax依赖Common, Cookie,这些都必须在providers里注明
            providers: [Ajax, Common, Cookie, SupplierInfo],
            schemas: [NO_ERRORS_SCHEMA]
        });
        // 各服务获取
        httpClient = TestBed.get(HttpClient);
        httpTestingController = TestBed.get(HttpTestingController);
        ajax = TestBed.get(Ajax);
        supplierInfo = TestBed.get(SupplierInfo);
        // 成功数据 
        this.expectedSupplierInfoSuccess = {
            code: 200,
            data: {

            }
        };
        // 失败数据 
        this.expectedSupplierInfoFail = {
            code: 400,
            errorCode: 'xxxx'
        }
    });

    afterEach(() => {
        // 验证没有发起过预期之外的请求
        httpTestingController.verify();
    });

    it('SupplierInfo service should be defined',
        () => {
            expect(supplierInfo).toBeDefined;
        }
    )

    it('infoData should be undefinded before the first call for getSupplierInfo',
        () => {
            expect(supplierInfo.infoData).toBeUndefined;
        }
    )

    it('infoData should be the supplierInfo after the first call for getSupplierInfo',
        () => {
            supplierInfo.getSupplierInfo();
            const req1 = httpTestingController.expectOne('/login/getSupplierInfo.json');
            req1.flush(this.expectedSupplierInfoSuccess);
            supplierInfo.getSupplierInfo();
            const req2 = httpTestingController.expectOne('/login/getSupplierInfo.json');
            req2.flush(this.expectedSupplierInfoSuccess);
            expect(supplierInfo.infoData).toBeDefined;
        }
    )

    it('getSupplierInfo should return value from observable when the response is success',
        () => {
            // 操作
            supplierInfo.getSupplierInfo().subscribe(value => {
                expect(value).toEqual(this.expectedSupplierInfoSuccess, 'should return expected expectedSupplierInfoSuccess'),
                    data => fail('should have successed with the data')
            });
            // 请求路径验证
            const req = httpTestingController.expectOne('/login/getSupplierInfo.json');
            // 请求method验证
            expect(req.request.method).toEqual('GET', 'the request method shoud be GET');
            // 指定返回数据 resolve Observable 
            req.flush(this.expectedSupplierInfoSuccess);
        });

    it('getSupplierInfo should return value from observable when the response is fail',
        () => {
            // 操作
            supplierInfo.getSupplierInfo().subscribe(
                data => fail('should have failed with the errorCode'),
                value => {
                    expect(value).toEqual(this.expectedSupplierInfoFail, 'should return expected expectedSupplierInfoFail')
                });
            // 请求路径验证
            const req = httpTestingController.expectOne('/login/getSupplierInfo.json');
            // 请求method验证
            expect(req.request.method).toEqual('GET', 'the request method shoud be GET');
            // 指定返回数据 resolve Observable 
            req.flush(this.expectedSupplierInfoFail);
        });
});
  • pipe

pipe 除了 @Pipe元数据和一个接口基本上不依赖angular,所以它的测试很简单

eg. 以一个大制造家中的OrderBySkuId管道为例:

import { Pipe, PipeTransform } from '@angular/core';

/*
 * 按skuid排序 
*/
@Pipe({ name: 'orderBySkuId' })
export class OrderBySkuId implements PipeTransform {
    constructor(
    ) {
    }
    transform(list = []): any {
        if (list && list.length > 0) {
            return list.sort((a, b) => a.skuId - b.skuId);
        } else {
            return []
        }
    }
}

测试代码只需对其transform调用并断言即可:

import { OrderBySkuId } from './order-by-skuid.pipe';
import { each } from 'fpb';

describe('OrderBySkuIdPipe', () => {
    const orderBySkuIdPipe = new OrderBySkuId();
    const skuList = [{
        skuId: 3
    }, {
        skuId: 4
    }, {
        skuId: 1
    }, {
        skuId: 2
    }];

    const expectedData = [{
        skuId: 1
    }, {
        skuId: 2
    }, {
        skuId: 3
    }, {
        skuId: 4
    }];

    const expectedToEqualEmptyArr = each((v) => {
        expect(orderBySkuIdPipe.transform(v)).toEqual([]);
    })
    it('transforms null、 undefiend、""、[] to []', () => {
        expectedToEqualEmptyArr([null, undefined, '', []])
    });
    it('transforms list orderBy it\'s skuid property', () => {
        expect(orderBySkuIdPipe.transform(skuList)).toEqual(expectedData);
    });
})

常用测试技巧罗列:可参见 https://angular.cn/guide/testing


  1. 改写组件provider
  2. Dom测试封装(见angular测试指导 page类)
  3. spy的使用
  4. jasmine-marbles的使用