You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:37:05