Angular15+Cypress12组件测试因服务依赖问题无法运行
Angular 15 + Cypress 12 组件测试服务依赖问题
问题概述
使用Angular 15和Cypress 12进行组件测试时,引入UtilityService后测试失败;移除该服务后测试可正常运行。目前存在两个疑问:
- 堆栈跟踪中为何出现
StorageService相关错误,且UtilityService依赖未关联成功? - 是否可以Mock服务,避免像单元测试那样导入大量依赖?
相关代码
待测试组件代码
import { Component } from '@angular/core'; import { UtilityService } from '../../services'; @Component({ selector: 'example-view', templateUrl: './example.component.html', }) export class ExampleComponent { constructor( utilityService: UtilityService, ) { } }
组件HTML
<h2>Example Component</h2>
Cypress组件测试代码
import { ExampleComponent } from './example.component' import { UtilityService } from '../../services'; describe('Example component', () => { it('should mount the component', () => { const utilityService = new UtilityService(); cy.mount(ExampleComponent, { componentProperties: { utilityService, }, }); cy.get('h2').should('exist'); cy.get('h2').should('contain', 'Example Component'); }) })
Cypress配置文件
import { defineConfig } from "cypress"; export default defineConfig({ e2e: { setupNodeEvents(on, config) { // implement node event listeners here }, }, chromeWebSecurity: false, screenshotsFolder: "cypress/snapshots", trashAssetsBeforeRuns: true, viewportWidth: 1400, viewportHeight: 1200, video: false, env: { local: "http://localhost:4200/", staging: "hidden", user: { email: "hidden", password: "hidden", }, }, component: { devServer: { framework: "angular", bundler: "webpack", }, specPattern: "**/*.cy.ts", }, });
UtilityService代码
import { Injectable } from '@angular/core'; import { ReplaySubject, BehaviorSubject } from 'rxjs'; import { ErrorModel } from '../models'; @Injectable() export class UtilityService { private sideMenu: BehaviorSubject<boolean | null> = new BehaviorSubject(false); public sideMenu$ = this.sideMenu.asObservable(); constructor() { } sideMenuToggle(isVisible: boolean): void { this.sideMenu.next(isVisible); } sortData(dataSource, propertyToSortOn: any, event: any): Array<any> { return [ ...dataSource.sort((a: any, b: any) => { if (a[propertyToSortOn] < b[propertyToSortOn]) { return event === 'ASC' ? -1 : 1; } if (a[propertyToSortOn] > b[propertyToSortOn]) { return event === 'DESC' ? -1 : 1; } return 0; }), ]; } }
错误信息

堆栈跟踪
at Module.StorageService (http://localhost:8080/__cypress/src/spec-0.js:92860:116) at 49525 (webpack://angular/./src/app/services/http/certificate.http.ts:10:62) at __webpack_require__ (http://localhost:8080/__cypress/src/runtime.js:23:42) at 21866 (http://localhost:8080/__cypress/src/spec-0.js:92870:80) at __webpack_require__ (http://localhost:8080/__cypress/src/runtime.js:23:42) at 12110 (http://localhost:8080/__cypress/src/spec-0.js:89187:67) at __webpack_require__ (http://localhost:8080/__cypress/src/runtime.js:23:42) at 96239 (http://localhost:8080/__cypress/src/spec-0.js:89151:76) at Function.__webpack_require__ (http://localhost:8080/__cypress/src/runtime.js:23:42) at _ZoneDelegate.invoke (http://localhost:8080/__cypress/src/polyfills.js:5341:158) From previous event: at evalScripts (http://localhost:8080/__cypress/runner/cypress_runner.js:168260:58) at <unknown> (http://localhost:8080/__cypress/runner/cypress_runner.js:168269:152) From previous event: at runScriptsFromUrls (http://localhost:8080/__cypress/runner/cypress_runner.js:168269:136) at Object.runScripts (http://localhost:8080/__cypress/runner/cypress_runner.js:168283:12) at $Cypress.onSpecWindow (http://localhost:8080/__cypress/runner/cypress_runner.js:156917:75) at init (http://localhost:8080/__cypress/src/cypress-entry.js:66:11) at 48839 (http://localhost:8080/__cypress/src/cypress-entry.js:38:3) at __webpack_require__ (http://localhost:8080/__cypress/src/runtime.js:23:42) at render (http://localhost:8080/__cypress/src/cypress-entry.js:84:3) at 42795 (http://localhost:8080/__cypress/src/cypress-entry.js:87:1) at __webpack_require__ (http://localhost:8080/__cypress/src/runtime.js:23:42) at __webpack_exec__ (http://localhost:8080/__cypress/src/cypress-entry.js:7783:48) at <unknown> (http://localhost:8080/__cypress/src/cypress-entry.js:7784:62) at webpackJsonpCallback (http://localhost:8080/__cypress/src/runtime.js:312:39) at <unknown> (http://localhost:8080/__cypress/src/cypress-entry.js:1:67)
问题分析与解决方案
1. 错误原因解析
StorageService错误:项目中certificate.http.ts依赖StorageService,而该服务被UtilityService或其他关联代码间接引用。Cypress加载测试时,webpack解析所有依赖,因未提供StorageService的注入项,导致抛出错误。- 服务注入方式错误:测试中使用
componentProperties传递服务实例是错误的,该配置仅用于设置组件的@Input()属性,Angular服务需通过依赖注入系统的providers配置注入。
2. 解决方案:Mock服务(推荐)
通过Mock服务切断依赖链,避免导入不必要的模块,示例代码如下:
import { ExampleComponent } from './example.component' import { UtilityService } from '../../services'; describe('Example component', () => { it('should mount the component', () => { // Mock UtilityService,仅提供组件用到的方法/属性 const mockUtilityService = { sideMenu$: cy.stub().asObservable(), sideMenuToggle: cy.stub(), sortData: cy.stub().returns([]) }; cy.mount(ExampleComponent, { providers: [ { provide: UtilityService, useValue: mockUtilityService } ] }); cy.get('h2').should('exist'); cy.get('h2').should('contain', 'Example Component'); }) })
3. 备选方案:提供完整依赖链(不推荐)
若需使用真实的UtilityService,需补充所有间接依赖的服务实例或Mock:
import { ExampleComponent } from './example.component' import { UtilityService } from '../../services'; import { StorageService } from '../../services/storage.service'; import { CertificateHttpService } from '../../services/http/certificate.http'; describe('Example component', () => { it('should mount the component', () => { cy.mount(ExampleComponent, { providers: [ UtilityService, { provide: StorageService, useValue: {} }, // Mock StorageService { provide: CertificateHttpService, useValue: {} } // Mock CertificateHttpService ] }); cy.get('h2').should('exist'); cy.get('h2').should('contain', 'Example Component'); }) })
内容的提问来源于stack exchange,提问作者AngularM
相关产品推荐
相关产品推荐

