【发布时间】:2020-11-23 21:37:52
【问题描述】:
我正在尝试测试使用 Angular Material 构建的组件,但是在使用 Harness Loader as per documentation (section 'Getting started'.) 初始化 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);
});
}));
loader.getHarness() 上的 await 会导致很多错误,似乎与代码未在“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>]
我还尝试使用全局 async 函数运行它(使用以下语法:)
beforeEach(async( async () => {
// magic happening here
}));
我什至尝试将这些线束提取到单独的函数中,以便尽可能晚地调用它们,但它也效果不佳:
const usernameFormField = () => {
loader.getHarnes(...);
}
// later in code; not the most elegant, but good enough
const usernameField = await usernameFormField();
expect(await usernameField().hasErrors()).toBeFalsy();
正如this post 所讨论的,“双异步”结构是有效的,虽然有点笨拙。但是,它对我不起作用。唯一的变体是beforeEach(async( () => { ... } ));。是否可以在异步区域的beforeEach 内使用 async-await,还是我坚持使用 Promises 手动处理所有内容?
编辑:类似的问题不仅出现在 beforeEach() 中,而且出现在测试方法本身中,即使我没有预先初始化线束:
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();*/
});
当我只取消注释第一行时,代码会中断并且测试不会通过。 当我包含异步区域时,测试通过,但错误仍然存在。我最初认为这是初始化组件的问题,但现在似乎它与 HarnessLoader 更相关。
编辑 2:Coderer 的回答链接到 karma.conf.js 的一些问题,所以这里是我的一些配置文件:
karma.conf.js:
// 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']
}
}
});
};
src/test.ts:(按照here 的描述导入也不起作用)
// 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);
tsconfig.base.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
}
}
tsconfig.spec.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"
]
}
angular.json的片段:
"architect": {
...
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss",
"src/dimens.scss"
],
"scripts": []
}
}
}
}
【问题讨论】:
-
你找到答案了吗?我注意到,如果我不包含
zone-patch-rxjs-fake-async,“预计在 ProxyZone”中的错误就会消失,但对loader.getAllHarnesses的调用永远不会解决。 -
请参阅下面关于以正确顺序包含
zone-patch-etc的答案,而且,我对loader.getAllHarnesses的调用实际上 未能解决,因为他们在下面调用fixture.whenStable()引擎盖,如果被测组件完全使用setInterval,whenStable永远不会解析。我还在戳那个。 -
嘿,我现在才注意到你的回答。我过会儿去看看,然后告诉你。
标签: angular testing async-await angular-material jasmine