如何测试需要jquery的ES6类? [英] How to test an ES6 class that needs jquery?

查看:218
本文介绍了如何测试需要jquery的ES6类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个需要jquery的ES6模块。

  import'from'jquery'; 

export class天气{
/ **
*天气类的构造方法
*
* @param纬度
* @param longitude
* /
构造函数(纬度,经度){
this.latitude = latitude;
this.longitude = longitude;
}

/ **
*使用API​​获取天气
* /
getWeather(){
return $ .ajax({
url:'http:// localhost:8080 / weather?lat ='+ this.latitude +'& lon ='+ this.longitude,
method:GET,
})。诺言();
}
}

模块在我的 main 模块,但问题是我正在为其写的测试。



这是测试:

  import {Weather} from'../js/weather'; 
从柴导入柴
从sinon导入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();
});

它('应该返回一个解决承诺,如果通话成功',(done)=> {
let weather = new Weather(43.65967339999999,-79.72506369999999);

let data ='{coord:{ l - 79.73,lat:43.66},天气:[{id:521,主:雨,描述:淋浴雨,图标:09d} ], 基: 站, 主:{ 温度:15.28, 压力:1009, 湿度:82, temp_min:13, temp_max:17}, 可见性: 24140, 风:{ 速度:7.2, DEG:30}, 云:{ 所有:90}, DT:1496770020, SYS:{ 类型:1, ID :3722, 消息 :0.0047, 国 : CA 日出:1496741873, 夕阳:1496797083}, ID :5907364, 姓名 : 宾顿, 鳕鱼: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
},
脚本:{
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
]
},
依赖:{
bootstrap:^ 3.3 .7,
jquery:^ 3.2.1,
webpack:*
}
}

正如你所看到的,我只需要执行 npm test 来运行测试。 p>

何时进行 npm测试,我收到此错误:

  TypeError:_jquery2.default.ajax在Weather.getWeather(js / weather.js:19:18)上不是一个函数

在上下文< anonymous> ; (test / index.js:26:17)

但是我正在导入 jquery 在模块中,为什么会发生这种情况?

解决方案

这里的主要问题。第一个当然是您需要修复导入问题,但这与测试无关。您将需要在进行测试之前解决此问题,这可能与您的构建工具的配置相关,而不是在Node中运行。您应该为此打开单独的问题,虽然这可能有帮助。您可能需要做的是将这个 import *替换为jQuery from'jquery';



另一个大问题是您正在运行 Node (使用 npm test 触发摩卡),而您的代码需要浏览器的。 Sinon的假服务器实现意图在浏览器环境中使用,您正在服务器环境中运行测试。这意味着jQuery和假的服务器设置都不起作用,因为Node没有 XHR对象



所以虽然Sinon XHR安装程序似乎很好,除非你愿意改变你的测试运行器,在浏览器中运行测试环境( Karma 非常适合从CLI中执行此操作!),您需要以另一种方式来处理这个问题。我很难达到伪造XHR的目的,而是在较高层次上扼杀依赖关系。 @CarlMarkham的答案是触动了这一点,但他没有详细介绍如何使用你的代码。



运行时基本上留下两个选项您节点中的代码:


  1. 截取调用导入JQuery模块,并将其替换为您自己的对象,该对象的stubbed版本为 AJAX 。这需要一个模块加载器插件,例如 rewire proxyquire

  2. 使用依赖注入直接在你的模块中。

Sinon主页有 Morgan Roderick的一篇好文章第一个选项,以及几个链接到其他网站上的其他文章,但没有如何解释如何做第一个选项。当我有时间时,我应该写一个...但是这里:



在实例级使用依赖注入



最简单的方式是在您正在测试的实例上公开 ajax 方法。这意味着您不需要在模块本身中注入任何东西,而且您不需要再考虑清理:

  // weather.js 
export class Weather {
constructor(纬度,经度){
this.ajax = $ .ajax;
this.latitude =纬度;
this.longitude = longitude;
}

getWeather(){
return this.ajax({...

// weather.test.js

它(如果调用成功,应该返回解决的承诺),(done)=> {
const weather = new Weather(43.65,-79.725);
const data ='{ coord:{lon: - 79.73,...}'//填写
weather.ajax = createStub(data);

另一种方法是更具侵入性,但是可以通过直接修改模块的依赖关系来保持类代码不变:



在模块级使用依赖注入



只需修改Weather类,即可导出依赖关系的setter界面,被覆盖:

  export const __setDeps(jQuery)=> $ = jQuery; 

现在,您可以简化您的测试,如下所示:

 从'../js/weather'导入天气; 
const天气天气

const fakeJquery = {};
weather .__ setDeps(fakeQuery);

const createStub = data => ()=> {promise:Promise.resolve(data)};

它(如果调用成功,应该返回解决的承诺),(done)=> {
const weather = new Weather(43.65,-79.725);
const data ='{coord:{lon: - 79.73,...}'//填写
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 __getDependencie s()=> {jquery:$};


// weather.test.js

它(如果调用成功,应该返回已解决的承诺),(done)=> {
const weather = new Weather(43.65,-79.725);
const data ='{coord:{lon: - 79.73,...}'//填写

__getDependencies()。jquery.ajax = createStub(data);

//测试

//将jQuery上的ajax恢复到原来的状态


I have an ES6 module that needs jquery.

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();
    }
}

Module works fine when use it in my main module but the issue is with the test that I am writing for it.

Here's the test:

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)
        ]);
    });
});

And here's my 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": "*"
  }
}

As you can see I only have to do npm test to run the test.

When do npm test, I get this error:

TypeError: _jquery2.default.ajax is not a function
      at Weather.getWeather (js/weather.js:19:18)
      at Context.<anonymous> (test/index.js:26:17)

But I am importing the jquery in the module, why it might be happening?

解决方案

There are two main problems here. The first is of course that you need to fix your import problem, but that is unrelated to testing. You will need to resolve this before going into testing, and this might have to do with the configuration of your build tool versus running in Node. You should open a separate question for this, although this might be of help. Probably all you need to do is replace the import with this import * as jQuery from 'jquery';

The other big issue is that you are running it inside of Node (using npm test that triggers Mocha) while your code requires a browser. The fake server implementation of Sinon is meant to be used in a browser environment, and you are running the tests in a server environment. That means neither jQuery nor the fake server setup will work, as Node does not have a XHR object.

So although the Sinon XHR setup seems fine, unless you are willing to change your test runner to run your tests inside of a browser environment (Karma is great for doing this from the CLI!), you need to handle this in another way. I seldom reach for faking XHR, and instead I stub out dependencies at a higher level. The answer from @CarlMarkham is touching upon this, but he does not go into details on how this would work with your code.

You are basically left with two options when running your code in Node:

  1. Intercept calls that import the JQuery module and replace it with your own object that has a stubbed version of ajax. This requires a module loader intercepter such as rewire or proxyquire.
  2. Use dependency injection directly in your module.

The Sinon homepage has a good article by Morgan Roderick on the first option, as well as several links to other articles elsewhere on the net, but no how to that explains how to do the first option. I should write one when I have time ... but here goes:

Using dependency injection on the instance level

The least invasive way is to just expose the ajax method on the instance you are testing. That means you would not need to inject anything into the module itself, and you don't have to think about cleanup afterwards:

// 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);

There is another way, that is more invasive, but lets you keep the class code unaltered by directly modifying the dependencies of the module:

Using dependency injection on the module level

Just modify your Weather class to export a setter interface for your dependencies so that they can be overwritten:

export const __setDeps(jQuery) => $ = jQuery;

Now you can simplify your test to read like this:

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();
    });
}

One problem with this approach is that you are tampering with the internals of the module, and so you need to restore the jQuery object in case you need to use the Weather class in other tests. You could of course also do the inverse: instead of injecting a fake jQuery object you can export the actual jQuery object and modify the ajax method directly. You would then delete all the injection code in the sample code above and modify it to read something like

// 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

这篇关于如何测试需要jquery的ES6类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆