设置模拟服务并使用 jasmine/karma 和 Angular 调用它时出现问题
Problems setting up mock services and calling it with jasmine/ karma and Angular
我有一个调用另一个方法的方法。在该方法中,我订阅了一项服务,如果有匹配项,则使用 Angular 路由器服务进行导航。这是一个stackBlitz example
goToMain(): void {
this.viewFunc(this.queryString);
}
viewFunc(queryString): void {
this.reportService.getAnotherReport(queryString).subscribe(x => {
if (x.label.toLowerCase() === "accept & view") {
const url = "main";
this.router.navigate([url], {
queryParams: { queryString: queryString }
});
}
});
}
我成功地在我的规范文件中创建了一个测试来测试调用了 viewFunc
方法。然后我尝试从下一个方法中调用的服务模拟订阅。我尝试测试两件事 A. 订阅被称为 B. 如果匹配则导航。
我收到错误“预期间谍 getAnotherReport 已被调用。”
it("should call service", () => {
const queryString = "10987";
const testee = component;
expect(testee).toBeTruthy();
const routerSpy = { navigate: jasmine.createSpy("navigate") };
const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();
testee.viewFunc(queryString);
expect(mySpy).toHaveBeenCalled();
expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
queryParams: { queryString: queryString }
});
});
我还不太了解 jasmine 和单元测试,我怀疑我为我的规范文件设置了不正确的描述/测试平台。因此,如果有人可以帮忙看一下 stackBlitz example,我将不胜感激。
有几个错别字:
首先,我认为您打算将 getAnotherReport
声明为值,而不是类型:
let getAnotherReport = {
text: null,
ref: "7478B45D4D4F1400223BD2F1",
num: 19,
urn: "123",
title: "Test report",
label: "accept & view"
};
然后,在 SearchComponent.viewFunc
中你有 url = 'main'
并且在你的规范中你有:
expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
queryParams: { queryString: queryString }
});
应该没有/
:
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
queryParams: { queryString: queryString }
});
现在,让我们看看有趣的东西。
我们将首先探讨第一个断言:
const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();
/* ... */
testee.viewFunc(queryString);
/* .... */
expect(mySpy).toHaveBeenCalled();
这里的问题与你如何决定监视有关 viewFunc
。在本例中,您使用了 spyOn(component, "viewFunc");
:
这些是当您调用 spyOn
:
时在幕后发生的相关位
var originalMethod = obj[methodName],
spiedMethod = createSpy(methodName, originalMethod),
其中 createSpy
将是一个 empty 间谍,即它将仅用于跟踪函数被调用的次数,参数等。 但是它不会使用原来的实现,这不是你想要的,因为你还有关于ReportService
的断言,它驻留在viewFunc
的原始实现中。
为了使用初始实现,您可以使用.and.callThrough()
:
// a quick look at the fact that it uses the original function
SpyStrategy.prototype.callThrough = function() {
this.plan = this.originalFn;
return this.getSpy();
};
reportService = TestBed.get(ReportService);
// and here we are using it
spyOn(component, "viewFunc").and.callThrough();
放在一起:
// you can get ahold of the current spy like this
const mySpy = mockReportService.getAnotherReport.and.returnValue(
of(getAnotherReport)
);
expect(testee).toBeTruthy();
testee.viewFunc(queryString);
expect(mySpy).toHaveBeenCalled();
现在进入第二个断言:
// inside providers array
{ provide: Router, useValue: routerSpy },
/* ... */
const routerSpy = { navigate: jasmine.createSpy("navigate") };
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
queryParams: { queryString: queryString }
});
在这种情况下,请注意您使用的 与您模拟 Router
class 的对象不同。
你必须使用你用来模拟有问题的 class 的同一个对象,所以解决这个问题的方法是:
let routerSpy = { navigate: jasmine.createSpy("navigate") };
/* ... */
// inside providers array
{ provide: Router, useValue: routerSpy },
/* ... */
const mySpy = mockReportService.getAnotherReport.and.returnValue(
of(getAnotherReport)
);
expect(testee).toBeTruthy();
testee.viewFunc(queryString);
expect(mySpy).toHaveBeenCalled();
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
queryParams: { queryString: queryString }
});
快速提示
我对 Jasmine 也不是很熟悉,但它帮助我了解了它在幕后的作用。无需了解每一个微小的细节,您至少可以直观地了解如何解决您的问题。因此,在 StackBlitz 应用程序(如您链接的应用程序)中,您可以:
- 打开开发工具
- 按
CTRL + P
并输入 jasmine.js
(可能有多个文件,正确的是包含 callThrough
等已知方法的文件 - 您可以通过按 CTRL + SHIFT + O
并输入方法名称)
此外,您可以搜索 search.component.ts
并在其中放置一些断点,以获得更流畅的调试体验。
我有一个调用另一个方法的方法。在该方法中,我订阅了一项服务,如果有匹配项,则使用 Angular 路由器服务进行导航。这是一个stackBlitz example
goToMain(): void {
this.viewFunc(this.queryString);
}
viewFunc(queryString): void {
this.reportService.getAnotherReport(queryString).subscribe(x => {
if (x.label.toLowerCase() === "accept & view") {
const url = "main";
this.router.navigate([url], {
queryParams: { queryString: queryString }
});
}
});
}
我成功地在我的规范文件中创建了一个测试来测试调用了 viewFunc
方法。然后我尝试从下一个方法中调用的服务模拟订阅。我尝试测试两件事 A. 订阅被称为 B. 如果匹配则导航。
我收到错误“预期间谍 getAnotherReport 已被调用。”
it("should call service", () => {
const queryString = "10987";
const testee = component;
expect(testee).toBeTruthy();
const routerSpy = { navigate: jasmine.createSpy("navigate") };
const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();
testee.viewFunc(queryString);
expect(mySpy).toHaveBeenCalled();
expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
queryParams: { queryString: queryString }
});
});
我还不太了解 jasmine 和单元测试,我怀疑我为我的规范文件设置了不正确的描述/测试平台。因此,如果有人可以帮忙看一下 stackBlitz example,我将不胜感激。
有几个错别字:
首先,我认为您打算将 getAnotherReport
声明为值,而不是类型:
let getAnotherReport = {
text: null,
ref: "7478B45D4D4F1400223BD2F1",
num: 19,
urn: "123",
title: "Test report",
label: "accept & view"
};
然后,在 SearchComponent.viewFunc
中你有 url = 'main'
并且在你的规范中你有:
expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
queryParams: { queryString: queryString }
});
应该没有/
:
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
queryParams: { queryString: queryString }
});
现在,让我们看看有趣的东西。
我们将首先探讨第一个断言:
const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();
/* ... */
testee.viewFunc(queryString);
/* .... */
expect(mySpy).toHaveBeenCalled();
这里的问题与你如何决定监视有关 viewFunc
。在本例中,您使用了 spyOn(component, "viewFunc");
:
这些是当您调用 spyOn
:
var originalMethod = obj[methodName],
spiedMethod = createSpy(methodName, originalMethod),
其中 createSpy
将是一个 empty 间谍,即它将仅用于跟踪函数被调用的次数,参数等。 但是它不会使用原来的实现,这不是你想要的,因为你还有关于ReportService
的断言,它驻留在viewFunc
的原始实现中。
为了使用初始实现,您可以使用.and.callThrough()
:
// a quick look at the fact that it uses the original function
SpyStrategy.prototype.callThrough = function() {
this.plan = this.originalFn;
return this.getSpy();
};
reportService = TestBed.get(ReportService);
// and here we are using it
spyOn(component, "viewFunc").and.callThrough();
放在一起:
// you can get ahold of the current spy like this
const mySpy = mockReportService.getAnotherReport.and.returnValue(
of(getAnotherReport)
);
expect(testee).toBeTruthy();
testee.viewFunc(queryString);
expect(mySpy).toHaveBeenCalled();
现在进入第二个断言:
// inside providers array
{ provide: Router, useValue: routerSpy },
/* ... */
const routerSpy = { navigate: jasmine.createSpy("navigate") };
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
queryParams: { queryString: queryString }
});
在这种情况下,请注意您使用的 与您模拟 Router
class 的对象不同。
你必须使用你用来模拟有问题的 class 的同一个对象,所以解决这个问题的方法是:
let routerSpy = { navigate: jasmine.createSpy("navigate") };
/* ... */
// inside providers array
{ provide: Router, useValue: routerSpy },
/* ... */
const mySpy = mockReportService.getAnotherReport.and.returnValue(
of(getAnotherReport)
);
expect(testee).toBeTruthy();
testee.viewFunc(queryString);
expect(mySpy).toHaveBeenCalled();
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
queryParams: { queryString: queryString }
});
快速提示
我对 Jasmine 也不是很熟悉,但它帮助我了解了它在幕后的作用。无需了解每一个微小的细节,您至少可以直观地了解如何解决您的问题。因此,在 StackBlitz 应用程序(如您链接的应用程序)中,您可以:
- 打开开发工具
- 按
CTRL + P
并输入jasmine.js
(可能有多个文件,正确的是包含callThrough
等已知方法的文件 - 您可以通过按CTRL + SHIFT + O
并输入方法名称)
此外,您可以搜索 search.component.ts
并在其中放置一些断点,以获得更流畅的调试体验。