返回博客首页
← 所有文章

使用 Angular 12 进行单元测试

2021 年 10 月 19 日 — 作者:Jason Cassidy

我们都知道测试很重要,得益于 NativeSript 中对 Angular 12 的支持更新,在 NativeScript 的 Angular 项目中进行测试变得比以往更容易。

示例项目演示

我们将逐步演示如何为示例的 SideDrawer Angular 模板添加测试。完成后,我们将拥有可以在我们选择的设备上执行的测试。

创建项目

  1. ns create
  2. 输入名称,例如:nstestingdemo
  3. 选择 Angular 版本
  4. 选择 SideDrawer 模板

现在你已经拥有了一个可以构建并运行的项目,你可以使用 ns run android 进行检查。

添加一个可以测试的服务

我们需要一些代码来进行测试,所以让我们添加一个服务。

  1. src/app/services 文件夹中创建一个名为 demo.service.ts 的文件

  2. 添加以下内容

    import { Injectable } from '@angular/core'
    
    @Injectable({
      providedIn: 'root',
    })
    export class DemoService {
      doSomething(): string {
        return 'correct message'
      }
    }
    

    这是一个简单的服务,我们可以将其注入到我们的控制器中,它只有一个返回值的函数。

  3. 将服务导入到搜索控制器(src/app/search/search.component.ts)中

    import {DemoService} from '../services/demo.service';

  4. 现在将服务注入到搜索控制器中

    constructor(private demo: DemoService) {
        // Use the component constructor to inject providers.
    }
    
  5. 在搜索控制器中添加一个使用该服务的方法

    doDemo(): string {
        return this.demo.doSomething();
    }
    

在项目中添加测试

  1. ns test init

  2. 选择 jasmine 框架

    这将安装各种框架等。

    如果你现在执行 ns test android,你会得到编译错误,这是因为我们还没有配置 tsconfig.json 来编译示例测试。

  3. 配置 tsconfig.json 用于测试

    添加一个名为 tsconfig.spec.json 的新文件(与 tsconfig.json 放在同一目录下),内容如下:

    {
      "extends": "./tsconfig.json",
      "include": ["./src/tests/test-main.ts", "./src/tests/*.spec.ts", "./src/tests/**/*.spec.ts", "**/*.d.ts"]
    }
    

    这里我们扩展了现有的配置,但添加了我们的测试规范文件。

  4. 将 example.ts 重命名为 example.spec.ts(以符合我们在上一步中输入的内容)。

  5. 配置 webpack 在运行测试时使用新的 tsconfig.spec.json

    在 webpack.config.js 中添加以下内容

    if (env.unitTesting == true) {
      webpack.chainWebpack((config) => {
        config.plugin('AngularWebpackPlugin').tap((args) => {
          args[0].tsconfig = './tsconfig.spec.json'
          return args
        })
      })
    }
    

    这里我们在运行测试时(换句话说,当 env.unitTesting 为 true 时)将 tsconfig 文件设置为我们的特定测试文件。

运行测试

现在我们可以执行示例测试。

  1. 启动 Android 模拟器

  2. 执行 ns test android

    当你观察模拟器时,你会看到测试正在运行,然后应用程序退出。如果在执行完测试后还可以看到应用程序,以便我们可以查看详细信息等,那就更好了。

    webpack.config.js 中,在上面添加的 chainWebpack 中添加以下内容。

    const webpack = require('@nativescript/webpack')
    const { merge } = require('webpack-merge')
    
    module.exports = (env) => {
      if (env.unitTesting == true) {
        webpack.chainWebpack((config) => {
          config.plugin('AngularWebpackPlugin').tap((args) => {
            args[0].tsconfig = './tsconfig.spec.json'
            return args
          })
          // keep the test runner open
          config.plugin('DefinePlugin').tap((args) => {
            args[0] = merge(args[0], {
              __TEST_RUNNER_STAY_OPEN__: true,
            })
            return args
          })
        })
      }
    }
    

    这将设置一个标志,在测试运行完成后让应用程序保持运行。

    再次执行 ns test android,你会看到所有测试都已完成。

设置 Angular TestBed

我们在上面创建的服务由 Angular 注入到我们的组件中,因此我们需要使用 Angular TestBed 来测试我们的组件,并将这些服务提供给我们的测试。

  1. 添加设置代码

    tests 文件夹中添加一个名为 test-main.ts 的新文件

    添加以下内容

    import '@nativescript/core/globals'
    import '@nativescript/angular/polyfills'
    import '@nativescript/zone-js/dist/pre-zone-polyfills'
    import 'zone.js'
    import '@nativescript/zone-js'
    import 'zone.js/testing'
    import { TestBed } from '@angular/core/testing'
    import { NativeScriptTestingModule } from '@nativescript/angular/testing'
    import { platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'
    
    TestBed.initTestEnvironment([NativeScriptTestingModule], platformBrowserDynamicTesting())
    
  2. 更新 karma.config.js 以使用新文件

    tests/test-main.ts 添加到 filePatterns 数组的开头。

  3. 为 Search 组件的 doDemo 函数添加测试。

    tests 文件夹中添加一个名为 angular-example.spec.ts 的新文件,内容如下:

    import { TestBed } from '@angular/core/testing'
    import { SearchComponent } from '../app/search/search.component'
    import { DemoService } from '../app/services/demo.service'
    import { Page } from '@nativescript/core'
    describe('search.component', () => {
      beforeEach(async () => {
        await TestBed.configureTestingModule({
          declarations: [SearchComponent],
          providers: [DemoService, Page],
        }).compileComponents()
      })
    
      it('should fail', async () => {
        const fixture = TestBed.createComponent(SearchComponent)
        const component = fixture.componentInstance as SearchComponent
        expect(component.doDemo()).toEqual('angular-component-testing')
      })
    
      it('should succeed', async () => {
        const fixture = TestBed.createComponent(SearchComponent)
        const component = fixture.componentInstance as SearchComponent
        expect(component.doDemo()).toEqual('correct message')
      })
    })
    
    • beforeEach 中,我们正在设置 TestBed,告诉它我们的组件以及它正常运行所需的服务提供者。
    • 我们有两个测试,其中一个会失败,因为它测试的是错误的消息。

    运行 ns test android 现在会显示我们额外的测试,其中一些会失败。

    由于这是一个实时监视测试模式,我们可以继续更改 "应该失败" 的测试以测试正确的值,保存文件,然后看到测试现在通过了。

示例项目

供参考,示例项目可以在这里找到:https://github.com/jcassidyav/nativescript-testing-demo

在 CI 环境中运行测试

如果你有一个为你的构建设置了 CI 环境,并且希望将测试纳入其中,这里有一些额外的提示……

配置报告器

一些 CI 环境允许你在构建过程中发布测试结果,例如在 Azure 中,你可以使用 发布测试任务 来发布结果。

为此,你需要配置一个报告器来输出测试结果。

  1. karma.conf.js 文件中,将 'junit' 添加到报告器列表中
  2. 根据需要配置报告器

例如:

reporters: ['progress', 'junit'],

junitReporter: {
    outputDir: '../TestResults',
    outputFile: 'TESTS_RESULTS.xml'
},

启动模拟器

CI 环境在启动/关闭运行测试的模拟器实例方面带来了挑战。

  • 如果你的 CI 设置为每次只运行一次测试,那么最好在每次运行的开始和结束时关闭模拟器。

  • -no-window 传递给 emulator.exe 会导致一个无头模拟器

  • 你可以使用 adb 轮询模拟器,直到它启动后才能尝试运行测试。

    例如,在 Windows PowerShell 上

    do {
         $outVar=cmd /c "C:\Android\android-sdk\platform-tools\adb.exe shell getprop sys.boot_completed" '2>&1'
         $outVar=$outVar.Split([Environment]::NewLine) | Select -First 1
         echo $outVar
         Start-Sleep -Seconds 10
      } Until ($outVar -eq "1")
    

联系 Avaeon 获取更多帮助

Jason Cassidy 自 90 年代就开始开发软件,并在 2001 年帮助创立了总部位于 爱尔兰戈尔韦Avaeon Solutions