Angular Jasmine单元测试配置两个Spy Object仅单个生效如何解决
问题原因
报错核心是创建recipeSpy(RecipeService的mock对象)时,未将addIngredientsToShoppingList方法加入spy的方法声明列表,导致注入的mock RecipeService实例上不存在该方法,调用时自然抛出TypeError: service.addIngredientsToShoppingList is not a function错误。
注释掉任意一个provider配置后单测试能通过的逻辑非常明确:
- 注释RecipeService的mock配置时,TestBed会自动实例化真实的RecipeService,真实实例自带
addIngredientsToShoppingList方法,且能正确注入你mock的ShoppingListService,因此第二个测试可通过,但此时拿不到RecipeService的spy对象,第一个测试无法通过。 - 注释ShoppingListService的mock配置时,注入的是手动创建的mock RecipeService,上面只有你声明的3个spy方法,第一个测试可通过,但mock对象内部没有注入
slService依赖,调用addIngredientsToShoppingList时会触发内部依赖找不到的错误。
额外注意:jasmine.createSpyObj生成的是完全独立的空对象,不会继承原服务类的任何方法、属性,所有测试中会调用的方法、访问的属性都必须手动在参数中声明,漏写就会出现方法不存在的报错。
修复方案
根据你的测试场景二选一即可:
场景1:测试对象是RecipeService本身(匹配你当前的测试用例逻辑)
你当前的测试是直接调用RecipeService的方法做验证,属于RecipeService的单元测试,这种场景不需要把RecipeService整体替换为spyObj,只需要mock它的依赖项ShoppingListService即可,RecipeService本身用TestBed生成的真实实例,需要监视某个方法时用spyOn挂载即可。
完整配置代码:
let mockShoppingListServiceSpy: jasmine.SpyObj<ShoppingListService>; let service: RecipeService; beforeEach(() => { // 仅mock被测试服务的依赖 mockShoppingListServiceSpy = jasmine.createSpyObj('ShoppingListService', ['addIngredients']); TestBed.configureTestingModule({ providers: [ RecipeService, // 提供真实的RecipeService作为测试对象 {provide: ShoppingListService, useValue: mockShoppingListServiceSpy} ], }); service = TestBed.inject(RecipeService); });
对应测试用例调整:
it('should get the recipe per the index', () => { // 在真实服务实例上挂载spy监视方法调用 spyOn(service, 'getRecipe').and.returnValue(mockRecipes[1]); service.getRecipe(1); expect(service.getRecipe).toHaveBeenCalledWith(1); }); it('should add ingredients to the shopping list', () => { service.addIngredientsToShoppingList(mockIngredients); expect(mockShoppingListServiceSpy.addIngredients).toHaveBeenCalledWith(mockIngredients); });
场景2:测试对象是组件/其他类,RecipeService是依赖项
如果你是在测试组件等其他类,需要全量mock两个依赖服务,只要在创建RecipeService的spy时,把所有测试中会调用的方法全部加入方法声明列表即可:
// 所有用到的RecipeService方法都要加到第二个参数的数组中,不要遗漏 let recipeSpy = jasmine.createSpyObj('RecipeService', ['setRecipes', 'getRecipes', 'getRecipe', 'addIngredientsToShoppingList'], {recipesChanged: of(mockRecipes), recipes: mockRecipes} ); let mockShoppingListServiceSpy = jasmine.createSpyObj('ShoppingListService', ['addIngredients'] ); beforeEach(() => { TestBed.configureTestingModule({ declarations: [/* 你要测试的组件放在这里 */], providers: [ {provide: RecipeService, useValue: recipeSpy}, {provide: ShoppingListService, useValue: mockShoppingListServiceSpy} ], }).compileComponents(); });
内容的提问来源于stack exchange,提问作者Neil
相关产品推荐
相关产品推荐

