我有一个通过 HexesService 调用外部 API 的组件 (MapComponent)。此 API 调用的数据结果将加载到一些按钮(查看 map 、编辑 map )。我有另一个内部组件 (HexesComponent),单击“ View ”按钮会调用此内部组件(这又会进行另一个 API 调用并显示 HexesComponent 内部的数据)。
我已经使用外部服务的模拟创建了一个测试。我的测试检查主组件加载后是否调用模拟服务并正确加载数据(所有按钮均已填充)。这工作得很好。
接下来我要检查的是,单击按钮是否会调用 HexesComponent 的方法。
我遇到的问题是我的内部组件总是未定义。 我已经为我的内部组件创建了一个 stub ,但这没有帮助:甚至 stub 也是空的。
问题#1: 我做错了什么?我尝试过对“beforeEach”内的方法使用异步,但这没有帮助。
不用说,除了“测试”之外,该功能运行得非常好。
问题#2: 如何验证单击按钮是否会调用 HexComponent 的“loadMap”方法?
这是我的测试代码:
class MockHexesService extends HexesService {
getDataForButtons(){
return of(...);
}
...
}
@Component({ selector: 'app-hex', template: '' })
class HexStubComponent {
public loadMap = jasmine.createSpy('loadMap');
}
describe('MapsComponent', () => {
let component: MapsComponent;
let hexComponent: HexStubComponent;
let fixture: ComponentFixture<MapsComponent>;
let componentService: HexesService;
let hexComponentSpy: any;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
declarations: [MapsComponent, HexStubComponent],
providers: [HexesService],
}).compileComponents();
TestBed.overrideComponent(MapsComponent, {
set: {
providers: [{ provide: HexesService, useClass: MockHexesService }],
},
});
fixture = TestBed.createComponent(MapsComponent);
component = fixture.componentInstance;
componentService = fixture.debugElement.injector.get(HexesService);
fixture.detectChanges();
hexComponent = fixture.debugElement.injector.get(HexStubComponent);
hexComponentSpy = spyOn(hexComponent, 'loadMap');
});
it('Loaded map descriptions should load data for buttons', () => {
const tblRows =
fixture.debugElement.nativeElement.querySelectorAll('table tr');
const cells0 = tblRows[0].querySelectorAll('td');
expect(cells0[0].innerHTML).toBe('Map 1');
... checking other buttons are populated properly
expect(component.hexComponent).toBeTruthy(); // - this fails here
});
it('Click on "View" button should load the map', async(() => {
//spyOn(component.hexComponent.loadMap)
const btns = fixture.debugElement.nativeElement.querySelectorAll('button');
expect(btns.length).toBe(6);
const btnView = btns[0];
expect(btnView.innerHTML).toBe('View Map');
btnView.click();
fixture.whenStable().then(() => {
expect(component.hexComponent).toBeTruthy(); // - this fails
// and how do I check that 'loadMap' method of hexComponent is called with proper data?
});
}));
});
maps.component.html 的基本部分:
...
<tr *ngFor="let d1 of data">
...
<button (click)="loadMap(d1.mapId)">View Map</button>
...
</tr>
<app-hex id="hexComponent"></app-hex>
maps.component.ts 的基本部分:
export class MapsComponent implements OnInit {
@ViewChild(HexComponent) hexComponent: HexComponent;
constructor(public ngZone: NgZone, private hexesService: HexesService) {}
ngOnInit(): void {
this.hexesService
.getDataForButtons()
.subscribe((data: any) => this.populateButtons(data));
}
loadMap(mapId): void {
this.hexComponent.loadMap(mapId);
}
附注我发现了一个类似的问题( How can I use a fake/mock/stub child component when testing a component that has ViewChildren in Angular 10+? ),建议使用 ng-mocks,但无法使它们工作。
P.P.S。我尝试过使用
@Component({ selector: 'app-hex', template: '' })
class HexStubComponent {
//public loadMap(mapId: number) {}
public loadMap = jasmine.createSpy('loadMap');
}
并在“TestBed.configureTestingModule”的声明中使用 HexStubComponent,但我的测试给出错误:
NullInjectorError: R3InjectorError(DynamicTestModule)[HexStubComponent -> HexStubComponent]: NullInjectorError: No provider for HexStubComponent!
不知道如何解决这个问题。
最佳答案
我决定再次尝试并使其与 ng-mocks 一起使用,而不是插入使用 Stabs 的选项。
显然,我的问题接近 Mocking Child Components - Angular 2 好的指导是 https://www.npmjs.com/package/ng-mocks/v/11.1.4 不同之处在于我的情况,我还需要确保调用子组件的一些方法。
为了帮助其他人(也许将来也能帮助我自己),这里列出了我在路上遇到的问题。
使用“npm install ng-mocks --save-dev”命令安装 ng-mocks
添加导入行(很明显,但 VS Code 没有自动建议这些行,我很难意识到如何访问我需要的确切类):
import { MockComponent, MockInstance, ngMocks } from 'ng-mocks';
在 TestBed.configureTestingModule 的异步版本中添加组件的模拟:
declarations: [MapsComponent, MockComponent(HexComponent)],
在您需要检查的组件和方法上创建 spy :
beforeEach(() => MockInstance(HexComponent, 'loadMap', jasmine.createSpy()));
验证:
it('Click on "View" button should load the map', async(() => { const hexComponent = ngMocks.findInstance(HexComponent); expect(hexComponent).toBeDefined(); expect(hexComponent.loadMap).not.toHaveBeenCalled(); const btns = fixture.debugElement.nativeElement.querySelectorAll('button'); expect(btns.length).toBe(6); const btnView = btns[0]; expect(btnView.innerHTML).toBe('View Map'); btnView.click(); fixture.whenStable().then(() => { expect(hexComponent.loadMap).toHaveBeenCalled(); }); }));
我尚未解决的唯一问题是模拟 HexComponent 使用的某些服务。我稍后会尝试一下。
关于angular - 如何检查测试期间调用内部组件的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68325705/