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

Angular测试报错:Hooks不可在测试内定义,beforeEach嵌套于测试用例

测试ServiceA时触发钩子嵌套错误的解决办法

问题背景

我需要测试名为ServiceA的服务,其代码如下:

export class ServiceA {
  constructor(protected serviceB: ServiceB) {}
  // 此处省略更多代码
}

ServiceA依赖ServiceB,ServiceB依赖ServiceC,而ServiceC依赖注入令牌SYSTEM_NAME,代码如下:

@Injectable({
  providedIn: 'root'
})
export class ServiceC {
    constructor(@Inject(SYSTEM_NAME) private systemName:string) {}
    // 此处省略更多代码
}

为测试ServiceA,我创建了两个服务工厂:一个用于创建ServiceC(供ServiceB依赖),另一个用于创建ServiceA。但调用createServiceA(serviceB)时出现以下错误:

Error: Hooks cannot be defined inside tests. Hook of type "beforeEach" is nested within "should create an instance".

测试代码如下:

let spectatorServiceC: SpectatorService<ServiceC>;
let spectatorServiceA: SpectatorService<ServiceA>;
let serviceB: ServiceB;

describe('ServiceA', () => {
    
    const createServiceC = createServiceFactory({
        service: ServiceC,
        providers: [
            { provide: SYSTEM_NAME, useValue: SYSTEM_NAME_VALUE }
        ],
    });

    const createServiceA = (serviceB: ServiceB) => createServiceFactory({
        service: ServiceA,
        providers: [
            { provide: ServiceB, useValue: serviceB },
            ServiceC
        ],
    }) as any;


  beforeEach(() => {
        spectatorServiceC = createServiceC();
        serviceB = new ServiceB(spectatorServiceC.service);

        spectatorServiceA = createServiceA(serviceB); // 添加此行后触发错误
  });
    
  it('should create an instance', () => {
    expect(serviceB.service).toBeTruthy();
  });
})

问题原因

createServiceFactory内部会自动注册测试钩子(如beforeEach),你在beforeEach函数内部调用createServiceA(它又调用了createServiceFactory),导致钩子嵌套定义,触发框架的错误提示。

解决方案

不要在beforeEach或测试用例(it)内部调用createServiceFactory,应将工厂创建逻辑移到describe块的顶层,再在beforeEach中使用工厂创建实例。同时,优化依赖注入方式,避免重复声明依赖:

方案1:手动传入ServiceB实例

let spectatorServiceC: SpectatorService<ServiceC>;
let spectatorServiceA: SpectatorService<ServiceA>;
let serviceB: ServiceB;

describe('ServiceA', () => {
    // 将工厂定义在describe顶层
    const createServiceC = createServiceFactory({
        service: ServiceC,
        providers: [
            { provide: SYSTEM_NAME, useValue: SYSTEM_NAME_VALUE }
        ],
    });

    const createServiceA = createServiceFactory({
        service: ServiceA,
        providers: [] // 先不指定ServiceB,留到创建实例时传入
    });

  beforeEach(() => {
        spectatorServiceC = createServiceC();
        serviceB = new ServiceB(spectatorServiceC.service);
        
        // 创建ServiceA实例时传入自定义的ServiceB依赖
        spectatorServiceA = createServiceA({
            providers: [{ provide: ServiceB, useValue: serviceB }]
        });
  });
    
  it('should create an instance', () => {
    expect(spectatorServiceA.service).toBeTruthy();
    expect(serviceB.service).toBeTruthy();
  });
})

方案2:复用依赖让DI自动创建ServiceB

如果不需要手动控制ServiceB的创建,可以直接复用ServiceC的依赖,让依赖注入系统自动处理ServiceB的实例化:

let spectatorServiceA: SpectatorService<ServiceA>;

describe('ServiceA', () => {
    const createServiceC = createServiceFactory({
        service: ServiceC,
        providers: [
            { provide: SYSTEM_NAME, useValue: SYSTEM_NAME_VALUE }
        ],
    });

    // 复用ServiceC的依赖,同时声明ServiceB,让DI自动创建实例
    const createServiceA = createServiceFactory({
        service: ServiceA,
        providers: [ServiceB, ...createServiceC.providers]
    });

  beforeEach(() => {
        spectatorServiceA = createServiceA();
  });
    
  it('should create an instance', () => {
    expect(spectatorServiceA.service).toBeTruthy();
  });
})

内容的提问来源于stack exchange,提问作者Iris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 09:27:38