Angular2+ Cucumber测试中Nock拦截HTTP请求失败排查
我最近在做Angular应用的Cucumber E2E测试时,尝试用Nock拦截HTTP请求但一直没成功,请求根本没被拦截,而且Nock的日志也没输出。以下是我的相关代码:
REST请求服务类
@Injectable() export class RestService { getTestdata() { this.http.get<ITestData>('/info/testdata', { observe: 'response' }) .catch(this.handleError) .subscribe(response => this._testData.next({ ...response.body })); } }
Cucumber测试文件代码
import { browser, element, by, protractor } from 'protractor'; import { of } from 'rxjs/observable/of'; import * as testData from '../../utilities/data/testData.json'; const { Before, After, Given, When, Then, defineSupportCode } = require('cucumber'); const chai = require('chai').use(require('chai-as-promised')); const expect = chai.expect; const nock = require('nock'); defineSupportCode(function ({ setDefaultTimeout }) { setDefaultTimeout(120 * 1000); }); Before(() => { nock('http://localhost:9084') .log(console.log) .get('/info/testdata') .reply(200, of(testdata)); }); After(() => { nock.cleanAll(); }); Given('something', (next: any) => { next(); }); When('the user loads the page', (next: any) => { testData['testKey1'] = false; testData['testKey2'] = false; browser.get('/').then(next()); }); Then('element is { displayed } to the user', (displayed: boolean, next: any) => { const testElement = element(by.id('test-element')); testElement .isPresent().then(response => { expect(response).to.equal(displayed); next(); }); });
页面加载后组件会调用这个接口,但我修改testData后的值根本没生效,Nock日志也没输出,请问我哪里错了?还有其他可用的工具吗?
问题根源分析
我来帮你拆解下问题:
Nock的作用场景完全错了:
Nock是Node.js端的HTTP拦截工具,它只能拦截从Node.js进程内部发出的HTTP请求。但你的Angular应用是运行在浏览器里的,请求是浏览器发出去的,Nock根本碰不到这些请求,这就是为什么日志没输出、拦截失效的核心原因。Reply的用法错误:
退一步说,就算Nock能拦截到请求,你用of(testdata)作为返回值也是错的——Nock的reply只需要直接返回JSON对象(或者原始响应内容),不需要用RxJS的of包装,因为Nock是模拟HTTP响应,不是返回Observable。
正确的解决方案与替代工具
既然是浏览器端的E2E测试,得用能在浏览器环境中拦截HTTP请求的工具,推荐这几个:
1. Angular官方的HttpTestingController(适合集成E2E测试)
你可以在测试中配置Angular的测试模块,注入HttpTestingController来匹配和拦截HTTP请求。示例思路:
// 在测试步骤中获取Angular的注入器,然后拿到HttpTestingController browser.executeAsyncScript((callback) => { const injector = window.ng.injector.get(window.ng.coreTokens.HttpTestingController); // 匹配请求并返回模拟数据 const req = injector.expectOne('/info/testdata'); req.flush({ testKey1: false, testKey2: false }); injector.verify(); callback(); });
注意:需要确保你的测试配置中引入了HttpClientTestingModule。
2. ng-apimock(专为Angular E2E测试设计)
这是一个专门针对Angular的API mocking工具,它能在浏览器端拦截所有HTTP请求,你可以在Cucumber的Before或步骤里动态配置mock规则,非常适合E2E场景。
3. 直接替换浏览器中的服务(快速 hack 方案)
如果你想快速验证,可以通过Protractor的executeScript在浏览器中直接替换RestService的方法,跳过真实HTTP请求:
When('the user loads the page', (next: any) => { const mockTestData = { testKey1: false, testKey2: false }; // 在浏览器中替换RestService的getTestdata方法 browser.executeScript((data) => { // 假设RestService在全局范围内可访问,或者通过Angular调试API获取 const restService = window.ng.injector.get(window.app.RestService); restService.getTestdata = () => { restService._testData.next(data); }; }, mockTestData).then(() => { browser.get('/').then(next); }); });
总结
Nock不适合Angular E2E测试中的浏览器请求拦截,换成浏览器端的mock工具就能解决你的问题啦!
内容的提问来源于stack exchange,提问作者syy

