我有一个需要jquery的ES6模块。
import $ from 'jquery';
export class Weather {
/**
* Constructor for Weather class
*
* @param latitude
* @param longitude
*/
constructor(latitude, longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
/**
* Fetches the weather using API
*/
getWeather() {
return $.ajax({
url: 'http://localhost:8080/weather?lat=' + this.latitude + '&lon=' + this.longitude,
method: "GET",
}).promise();
}
}
模块在我的main
模块中使用时工作正常,但问题在于我正在为它编写的测试。
以下是测试:
import {Weather} from '../js/weather';
import chai from 'chai';
import sinon from 'sinon';
chai.should();
describe('weatherbot', function() {
beforeEach(function() {
this.xhr = sinon.useFakeXMLHttpRequest();
this.requests = [];
this.xhr.onCreate = function(xhr) {
this.requests.push(xhr);
}.bind(this);
});
afterEach(function() {
this.xhr.restore();
});
it('should return a resolved promise if call is successful', (done) => {
let weather = new Weather(43.65967339999999, -79.72506369999999);
let data = '{"coord":{"lon":-79.73,"lat":43.66},"weather":[{"id":521,"main":"Rain","description":"shower rain","icon":"09d"}],"base":"stations","main":{"temp":15.28,"pressure":1009,"humidity":82,"temp_min":13,"temp_max":17},"visibility":24140,"wind":{"speed":7.2,"deg":30},"clouds":{"all":90},"dt":1496770020,"sys":{"type":1,"id":3722,"message":0.0047,"country":"CA","sunrise":1496741873,"sunset":1496797083},"id":5907364,"name":"Brampton","cod":200}';
weather.getWeather().then((data) => {
expect(data.main.temp).to.equal(15.28);
done();
});
this.requests[0].respond("GET", "/weather?lat=43.659673399999996&lon=-79.72506369999999", [
200, {"Content-Type":"application/json"}, JSON.stringify(data)
]);
});
});
这是package.json
:
{
"devDependencies": {
"babel-core": "^6.24.1",
"babel-loader": "^6.1.0",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.1.18",
"chai": "^3.5.0",
"copy-webpack-plugin": "^0.2.0",
"css-loader": "^0.28.0",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.11.1",
"mocha": "^3.4.1",
"mocha-webpack": "^1.0.0-beta.1",
"qunitjs": "^2.3.2",
"sinon": "^2.2.0",
"style-loader": "^0.16.1",
"svg-inline-loader": "^0.7.1",
"webpack": "*",
"webpack-dev-server": "^1.12.1",
"webpack-node-externals": "^1.6.0"
},
"scripts": {
"build": "webpack",
"watch": "webpack --watch --display-error-details",
"start": "webpack-dev-server --hot --inline --port 8383",
"test": "mocha --compilers js:babel-core/register ./test/*.js",
"test:watch": "npm run test -- --watch"
},
"babel": {
"presets": [
"es2015"
]
},
"dependencies": {
"bootstrap": "^3.3.7",
"jquery": "^3.2.1",
"webpack": "*"
}
}
如您所见,我只需要npm test
来运行测试。
什么时候npm test
,我收到此错误:
TypeError: _jquery2.default.ajax is not a function
at Weather.getWeather (js/weather.js:19:18)
at Context.<anonymous> (test/index.js:26:17)
但我正在导入模块中的jquery
,为什么会发生这种情况?
答案 0 :(得分:2)
这里有两个主要问题。第一个当然是您需要修复导入问题,但这与测试无关。在进行测试之前,您需要解决此问题,这可能与构建工具的配置相比,而不是在Node中运行。你应该为此开一个单独的问题,although this might be of help。您可能需要做的就是使用此import * as jQuery from 'jquery';
另一个大问题是你在 Node 中运行它(使用npm test
触发Mocha),而你的代码需要一个浏览器。 Sinon的虚假服务器实现旨在用于浏览器环境,并且您正在服务器环境中运行测试。这意味着jQuery和假服务器设置都不起作用,因为Node没有XHR object。
因此,尽管Sinon XHR设置似乎很好,除非您愿意更改测试运行器以在浏览器环境中运行测试(Karma非常适合从CLI执行此操作!),您需要以另一种方式处理这个。我很少涉及伪造XHR,而是在更高层次上存在依赖关系。 @CarlMarkham的答案正在触及这一点,但他没有详细说明这将如何与您的代码一起使用。
在Node中运行代码时,基本上有两个选项:
ajax
的对象。这需要模块加载器拦截器,例如rewire
或proxyquire
。Sinon主页在第一个选项上有a good article by Morgan Roderick,还有几个links to other articles elsewhere on the net,但没有解释如何解释如何执行第一个选项。我有空的时候应该写一个...但是这里有:
在实例级别使用依赖项注入
最少侵入性的方法是在您正在测试的实例上公开ajax
方法。这意味着您不需要向模块本身注入任何东西,之后您不必考虑清理:
// weather.js
export class Weather {
constructor(latitude, longitude) {
this.ajax = $.ajax;
this.latitude = latitude;
this.longitude = longitude;
}
getWeather() {
return this.ajax({ ...
// weather.test.js
it('should return a resolved promise if call is successful', (done) => {
const weather = new Weather(43.65, -79.725);
const data = '{"coord":{"lon":-79.73, ... }' // fill in
weather.ajax = createStub(data);
还有另外一种方法,那就是更具侵略性,但是让你通过直接修改模块的依赖关系来保持类代码不变:
在模块级别使用依赖项注入
只需修改您的Weather类以导出依赖项的setter接口,以便可以覆盖它们:
export const __setDeps(jQuery) => $ = jQuery;
现在,您可以将测试简化为以下内容:
import weather from '../js/weather';
const Weather = weather.Weather;
const fakeJquery = {};
weather.__setDeps(fakeQuery);
const createStub = data => () => { promise: Promise.resolve(data) };
it('should return a resolved promise if call is successful', (done) => {
const weather = new Weather(43.65, -79.725);
const data = '{"coord":{"lon":-79.73, ... }' // fill in
fakeQuery.ajax = createStub(data);
weather.getWeather().then((data) => {
expect(data.main.temp).to.equal(15.28);
done();
});
}
这种方法的一个问题是你正在篡改模块的内部,因此你需要恢复jQuery对象,以防你需要在其他测试中使用Weather类。你当然也可以做反过来:你可以导出实际 jQuery对象而不是注入假的jQuery对象,直接修改ajax
方法。然后,您将删除上面示例代码中的所有注入代码,并将其修改为读取类似
// weather.js
export const __getDependencies() => { jquery: $ };
// weather.test.js
it('should return a resolved promise if call is successful', (done) => {
const weather = new Weather(43.65, -79.725);
const data = '{"coord":{"lon":-79.73, ... }' // fill in
__getDependencies().jquery.ajax = createStub(data);
// do test
// restore ajax on jQuery back to its original state
答案 1 :(得分:0)
除非是导入default
导出的ES6模块(jQuery不导出),否则导入它的正确方法是
import * as $ from 'jquery';
使用import
处理CommonJS模块的方式是构建工具的责任。至少在较旧的Webpack版本中,(错误地)支持非ES6模块的default
导入。
"webpack": "*"
依赖是破坏构建的直接方法。
答案 2 :(得分:0)
在我的项目中,我存储了我使用的所有jQuery方法,因此,我将存根$.ajax
,因为您需要测试的只是返回的promise。
在另一个档案中有这样的事情:
module.exports = {
ajax: sinon.stub(global.$, 'ajax')
}
然后,在你的测试中
import { ajax } from 'stubs'
这样,ajax
方法是存根的,您可以在每个测试的基础上定义它应返回的内容:
ajax.returns({
done: (callback) => { callback(someParam) },
fail: () => {},
always: (callback) => { callback() }
});
答案 3 :(得分:0)
我最近遇到了类似的问题。我发现,当我使用mocha和webpack进行测试时,jquery绑定的范围内没有“窗口”,因此它是未定义的。为了解决此问题,我发现我可以遵循this advice并将源文件中的GET
替换为:
import * as $ from jquery
但是随后源文件将不再与webpack捆绑在一起,因此我放弃了使用mocha和babel进行客户端javascript测试。相反,我发现可以使用karma和phantomjs的组合来正确测试客户端代码。
首先,我安装了所有依赖项:
// not the winning solution
const jsdom = require("jsdom");
const { window } = new jsdom.JSDOM();
const $ = require('jquery')(window);
然后我使用以下命令在名为npm install -D babel-loader @babel/core
npm install -D mocha chai sinon mocha-webpack
npm install -D phantomjs-prebuilt
npm install -D webpack
npm install -D karma karma-mocha karma-chai karma-sinon
npm install -D karma-mocha-reporter karma-phantomjs-launcher karma-webpack
的根目录中设置一个配置文件:
karma.config.js
最后,我在package.json中的脚本中添加了module.exports = function(config) {
config.set({
browsers: ['PhantomJS'],
files: [
'./test/spec/*.js'
],
frameworks: ['mocha', 'chai', 'sinon'],
reporters: ['mocha'],
preprocessors: {
'./test/spec/*.js': ['webpack']
},
webpack: {
module: {
rules: [
{ test: /\.js/, exclude: /node_modules/, loader: 'babel-loader' }
]
},
watch: true,
mode: 'none'
},
webpackServer: {
noInfo: true
},
singleRun: true
});
};
。现在,所有规格测试都可以使用"test": "karma start karma.config.js"
运行。