角度材料/茉莉花测试 - 加载材料线束时出错

Kam*_*ich 5 testing async-await jasmine angular-material angular

我正在尝试测试使用 Angular Material 构建的组件,但是我在Harness Loader 根据文档(“入门”部分)初始化 Material 元素时遇到了问题。我想在测试方法之外提取初始化它们的逻辑,使它们更简洁,但它似乎不起作用。

describe()

let usernameFormField: MatFormFieldHarness;
let registrationButton: MatButtonHarness;

beforeEach(async(() => {
    TestBed.configureTestingModule({
        imports: [MaterialModule, BrowserAnimationsModule, ReactiveFormsModule],
        declarations: [RegistrationComponent],
        providers: [ /*provide spies */ ]
    }).compileComponents().then(async () => {
        fixture = TestBed.createComponent(RegistrationComponent);
        loader = TestbedHarnessEnvironment.loader(fixture);

        // what works, but I don't like
        /*loader.getHarness(
            MatFormFieldHarness.with({selector: '#username-form-field'})
        ).then(harness => {
            usernameFormField = harness;
        });*/

        // what doesn't work
        usernameFormField = await loader
            .getHarness(MatFormFieldHarness.with({selector: '#username-form-field'}))

        // other form elements

        // to my confusion, this works without any problem
        registrationButton = await loader.getHarness(MatButtonHarness);
    });
}));
Run Code Online (Sandbox Code Playgroud)

awaitloader.getHarness()引起了大量的错误,看似约代码“ProxyZone”没有运行。

context.js:265 Unhandled Promise rejection: Expected to be running in 'ProxyZone', but it was not found. ; Zone: <root> ; Task: Promise.then ; Value: Error: Expected to be running in 'ProxyZone', but it was not found.
    at Function.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.assertPresent (zone-testing.js:210) [<root>]
    at Function.setup (testbed.js:61) [<root>]
    at new TestbedHarnessEnvironment (testbed.js:572) [<root>]
    at TestbedHarnessEnvironment.createEnvironment (testbed.js:633) [<root>]
    at TestbedHarnessEnvironment.createComponentHarness (testing.js:341) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (testing.js:384) [<root>]
    at Generator.next (<anonymous>) [<root>]
    at :9876/_karma_webpack_/node_modules/tslib/tslib.es6.js:74:1 [<root>]
    at new ZoneAwarePromise (zone-evergreen.js:960) [<root>]
    at __awaiter (tslib.es6.js:70) [<root>]
    at TestbedHarnessEnvironment._getQueryResultForElement (testing.js:379) [<root>]
    at :9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:366:1 [<root>]
    at Array.map (<anonymous>) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (testing.js:366) [<root>] Error: Expected to be running in 'ProxyZone', but it was not found.
    at Function.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.assertPresent (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:210:1) [<root>]
    at Function.setup (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing/testbed.js:61:1) [<root>]
    at new TestbedHarnessEnvironment (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing/testbed.js:572:1) [<root>]
    at TestbedHarnessEnvironment.createEnvironment (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing/testbed.js:633:1) [<root>]
    at TestbedHarnessEnvironment.createComponentHarness (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:341:1) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:384:1) [<root>]
    at Generator.next (<anonymous>) [<root>]
    at http://localhost:9876/_karma_webpack_/node_modules/tslib/tslib.es6.js:74:1 [<root>]
    at new ZoneAwarePromise (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:960:1) [<root>]
    at __awaiter (http://localhost:9876/_karma_webpack_/node_modules/tslib/tslib.es6.js:70:1) [<root>]
    at TestbedHarnessEnvironment._getQueryResultForElement (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:379:25) [<root>]
    at http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:366:1 [<root>]
    at Array.map (<anonymous>) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:366:1) [<root>]
Run Code Online (Sandbox Code Playgroud)

我还尝试使用全局async函数运行它(使用以下语法:)

context.js:265 Unhandled Promise rejection: Expected to be running in 'ProxyZone', but it was not found. ; Zone: <root> ; Task: Promise.then ; Value: Error: Expected to be running in 'ProxyZone', but it was not found.
    at Function.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.assertPresent (zone-testing.js:210) [<root>]
    at Function.setup (testbed.js:61) [<root>]
    at new TestbedHarnessEnvironment (testbed.js:572) [<root>]
    at TestbedHarnessEnvironment.createEnvironment (testbed.js:633) [<root>]
    at TestbedHarnessEnvironment.createComponentHarness (testing.js:341) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (testing.js:384) [<root>]
    at Generator.next (<anonymous>) [<root>]
    at :9876/_karma_webpack_/node_modules/tslib/tslib.es6.js:74:1 [<root>]
    at new ZoneAwarePromise (zone-evergreen.js:960) [<root>]
    at __awaiter (tslib.es6.js:70) [<root>]
    at TestbedHarnessEnvironment._getQueryResultForElement (testing.js:379) [<root>]
    at :9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:366:1 [<root>]
    at Array.map (<anonymous>) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (testing.js:366) [<root>] Error: Expected to be running in 'ProxyZone', but it was not found.
    at Function.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.assertPresent (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:210:1) [<root>]
    at Function.setup (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing/testbed.js:61:1) [<root>]
    at new TestbedHarnessEnvironment (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing/testbed.js:572:1) [<root>]
    at TestbedHarnessEnvironment.createEnvironment (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing/testbed.js:633:1) [<root>]
    at TestbedHarnessEnvironment.createComponentHarness (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:341:1) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:384:1) [<root>]
    at Generator.next (<anonymous>) [<root>]
    at http://localhost:9876/_karma_webpack_/node_modules/tslib/tslib.es6.js:74:1 [<root>]
    at new ZoneAwarePromise (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:960:1) [<root>]
    at __awaiter (http://localhost:9876/_karma_webpack_/node_modules/tslib/tslib.es6.js:70:1) [<root>]
    at TestbedHarnessEnvironment._getQueryResultForElement (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:379:25) [<root>]
    at http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:366:1 [<root>]
    at Array.map (<anonymous>) [<root>]
    at TestbedHarnessEnvironment.<anonymous> (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/fesm2015/testing.js:366:1) [<root>]
Run Code Online (Sandbox Code Playgroud)

我什至尝试将这些线束提取到单独的函数中,尽可能晚地调用它们,但它也不能很好地工作:

beforeEach(async( async () => {
    // magic happening here
}));
Run Code Online (Sandbox Code Playgroud)

正如这篇文章所讨论的,“双异步”结构是有效的,虽然有点笨拙。但是,它对我不起作用。唯一的变体是beforeEach(async( () => { ... } ));. 是否可以beforeEach在 async zone内部使用 async-await ,或者我是否坚持使用 Promises 手动处理所有内容?

编辑:类似的问题不仅出现在beforeEach(),而且出现在测试方法本身中,即使我没有预先初始化线束:

const usernameFormField = () => {
    loader.getHarnes(...);
}

// later in code; not the most elegant, but good enough
const usernameField = await usernameFormField();
expect(await usernameField().hasErrors()).toBeFalsy();
Run Code Online (Sandbox Code Playgroud)

当我只取消注释第一个注释行时,代码会中断并且测试不会通过。当我包含异步区域时,测试通过,但错误仍然存​​在。我最初认为这是初始化组件的问题,但现在似乎与HarnessLoader有关。

编辑 2:Coderer 的回答链接到一些问题karma.conf.js,所以这里是我的一些配置文件:

karma.conf.js

it('should display \'log out\' and \'my account\' buttons when user is authenticated',
    async () => {
        const EXAMPLE_USERNAME = 'username';

        spyOnProperty(authenticationService, 'authenticatedUser')
            .and.returnValue(EXAMPLE_USERNAME);

       expect(fixture.componentInstance.authenticatedUser)
.toEqual(EXAMPLE_USERNAME);

        const logOutButton = await loader
            .getHarness(MatButtonHarness.with({text: BUTTON_LOG_OUT_TEXT}));
        expect(await logOutButton.isDisabled()).toBeFalsy();

        // the following line causes a problem
        /*const myAccountButton = await loader
            .getHarness(MatButtonHarness.with({text: BUTTON_MY_ACCOUNT_TEXT}));
        expect(await myAccountButton.isDisabled()).toBeFalsy();
        await myAccountButton.click();
        expect(routerSpy.navigateByUrl).toHaveBeenCalled();*/
    });
Run Code Online (Sandbox Code Playgroud)

src/test.ts(导入描述这里也没有工作)

// custom headless chrome from
// https://coryrylan.com/blog/building-angular-cli-projects-with-github-actions

module.exports = function (config) {
  config.set({
    // adding any files here didn't seem to work
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false, // leave Jasmine Spec Runner output visible in browser
      jasmine: {
        random: false
      }
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, './coverage/elx-front-end'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    restartOnFileChange: true,
    customLaunchers: {
      ChromeHeadlessCI: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox', '--disable-gpu']
      }
    }
  });
};
Run Code Online (Sandbox Code Playgroud)

tsconfig.base.json

// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import "zone.js/dist/zone-testing";
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);
Run Code Online (Sandbox Code Playgroud)

tsconfig.spec.json

{
    "compileOnSave": false,
    "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "downlevelIteration": true,
        "experimentalDecorators": true,
        "module": "es2020",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "allowSyntheticDefaultImports": true,
        "importHelpers": true,
        "target": "es2018",
        "strict": true,
        "typeRoots": [
            "node_modules/@types"
        ],
        "lib": [
            "es2018",
            "dom"
        ],
        "paths": { /* custom paths */}
    },
    "angularCompilerOptions": {
        "fullTemplateTypeCheck": true,
        "strictInjectionParameters": true
    }
}
Run Code Online (Sandbox Code Playgroud)

片段angular.json

{
    "extends": "./tsconfig.base.json",
    "compilerOptions": {
        "outDir": "./out-tsc/spec",
        "types": [
            "jasmine",
            "node"
        ]
    },
    "files": [
        "src/test.ts",
        "src/polyfills.ts"
    ],
    "include": [
        "src/**/*.spec.ts",
        "src/**/*.d.ts"
    ]
}
Run Code Online (Sandbox Code Playgroud)

Tre*_*nis 5

我针对 Angular 11 提交了有关此问题的问题21632 ,因为设置如文档的入门中所述。罪魁祸首是beforeEach函数,存在三种可能的解决方案。

选项1

async函数已被弃用并被替换为waitForAsync. 从 Angular 11(演示)开始,使用waitForAsyncinbeforeEach代替异步函数是解决该问题的一种方法。

beforeEach(waitForAsync(() => {
  TestBed.configureTestingModule({
    imports: [MaterialModule, BrowserAnimationsModule, ReactiveFormsModule],
    declarations: [RegistrationComponent]
  }).compileComponents().then(async () => {
    fixture = TestBed.createComponent(RegistrationComponent);
    loader = TestbedHarnessEnvironment.loader(fixture);
    usernameFormField = await loader
      .getHarness(MatFormFieldHarness.with({selector: '#username-form-field'}))
    registrationButton = await loader.getHarness(MatButtonHarness);
  });
}));
Run Code Online (Sandbox Code Playgroud)

选项2

将异步函数传递给beforeEachwithoutwaitForAsync也适用于您的特定情况(演示)。但是,当不处理compileComponents承诺时,就像大多数文档那样,将调用移至TestbedHarnessEnvironment.loader函数itdemo)。

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [MatButtonModule],
    declarations: [ButtonHarnessExample]
  }).compileComponents();
  fixture = TestBed.createComponent(ButtonHarnessExample);
});

it('should click a button', async () => {
  const loader = TestbedHarnessEnvironment.loader(fixture);
  const button = await loader.getHarness(MatButtonHarness.with({text: 'Basic button'}));
});
Run Code Online (Sandbox Code Playgroud)

选项3

在 tsconfig.json ( demo )中将目标更改为 ES2016 。

"compilerOptions": {
  "target": "es2016"
}
Run Code Online (Sandbox Code Playgroud)