Jest不会自动模拟常见的js模块

时间:2015-03-05 01:23:45

标签: javascript unit-testing tdd reactjs jestjs

我正试着用Jest测试Flux和React应用程序。

我开始使用Scotch.io tutorial作为我的起点,并将创建一个完整的测试套件,以获得有关如何构建我的第一个真正的反应和通量应用程序的知识。

您可以find my code on github查看我的位置。

未运行的测试是FluxProduct-test.jsx



jest.dontMock('../FluxProduct');

var React
  , TestUtils
  , FluxProduct
  , FluxCartActions;

describe('FluxProduct', function() {


  var SAMPLE = {
    product: {
      id: '1',
      name: 'Name',
      image: 'image.png',
      description: 'this description',
      variants: [
        {
          sku: '123',
          type: 'First',
          price: 1.99,
          inventory: 1
        },
        {
          sku: '456',
          type: 'Second',
          price: 2.99,
          inventory: 3
        },
        {
          sku: '789',
          type: 'Third',
          price: 3.99,
          inventory: 2
        }
      ]
    },
  };


  function getElement(product, className)
  {
    return product.getDOMNode().getElementsByClassName(className)[0];
  }

  function getElementByTag(product, tagName)
  {
    return product.getDOMNode().getElementsByTagName(tagName)[0];
  }

  function selectIsActive(select, text)
  {
    for( var i = 0; i < select.options.length; i++)
    {
      if (select.options[i].textContent == text)
      {
        return true
      }
    }
    return false;
  }

  beforeEach(function(){
    React = require('react/addons')
    , TestUtils = React.addons.TestUtils
    , FluxProduct = require('../FluxProduct')
    , FluxCartActions = require('../../actions/FluxCartActions');
  });

  it('should have the display all of the fields', function() {
    var cartItems = [];
    var selected = SAMPLE.product.variants[1];
    var product = TestUtils.renderIntoDocument(
      <FluxProduct selected={selected} product={SAMPLE.product} cartitems={cartItems} />
    );

    expect(getElement(product, 'name').textContent).toEqual(SAMPLE.product.name);
    expect(getElement(product, 'description').textContent).toEqual(SAMPLE.product.description);
    expect(getElement(product, 'price').textContent).toEqual('Price: $' + selected.price);
    expect(selectIsActive(getElementByTag(product, 'select'), selected.type)).toEqual(true);
  });

  it('should allow to add another variant', function() {
    var cartItems = [];
    var selected = SAMPLE.product.variants[1];
    var targetVariantIndex = 2;
    var targetVariant = SAMPLE.product.variants[targetVariantIndex];
    var product = TestUtils.renderIntoDocument(
      <FluxProduct selected={selected} product={SAMPLE.product} cartitems={cartItems} />
    );
    var selectElement = getElementByTag(product, 'select');
    var addToCartBtn = getElementByTag(product, 'select');

    TestUtils.Simulate.change(selectElement, { target: { value: targetVariantIndex } });

    expect(selectIsActive(selectElement, targetVariant.type)).toEqual(true);

    TestUtils.Simulate.click(addToCartBtn);

    expect(FluxCartActions.addToCart.mock.calls.length).toBe(1);
    expect(FluxCartActions.addToCart.mock.calls[0][0]).toBe(targetVariant.sku);
    expect(FluxCartActions.addToCart.mock.calls[0][0]).toBe({
      name: targetVariant.name,
      type: targetVariant.type,
      price: targetVariant.price
    });
  });
});
&#13;
&#13;
&#13;

它返回&#34; TypeError:无法读取属性&#39;来电&#39;未定义&#34;在第100行。

当我注销FluxActions时,它似乎没有被自动锁定,这就是mock未定义的原因,并且访问calls属性会引发错误。

fyi:Jest要求节点0.10,不能在0.12上运行

有用的参考文件:

FluxProduct.jsx

&#13;
&#13;
var React = require('react');
var FluxCartActions = require('../actions/FluxCartActions');

// Flux product view
var FluxProduct = React.createClass({

  // Add item to cart via Actions
  addToCart: function(event){
    var sku = this.props.selected.sku;
    var update = {
      name: this.props.product.name,
      type: this.props.selected.type,
      price: this.props.selected.price
    }
    FluxCartActions.addToCart(sku, update);
    FluxCartActions.updateCartVisible(true);
  },

  // Select product variation via Actions
  selectVariant: function(event){
    FluxCartActions.selectProduct(event.target.value);
  },

  // Render product View
  render: function() {
    var ats = (this.props.selected.sku in this.props.cartitems) ?
      this.props.selected.inventory - this.props.cartitems[this.props.selected.sku].quantity :
      this.props.selected.inventory;
    return (
      <div className="flux-product">
        <img src={'assets/' + this.props.product.image}/>
        <div className="flux-product-detail">
          <h1 className="name">{this.props.product.name}</h1>
          <p className="description">{this.props.product.description}</p>
          <p className="price">Price: ${this.props.selected.price}</p>
          <select onChange={this.selectVariant}>
            {this.props.product.variants.map(function(variant, index){
              return (
                <option key={index} value={index}>{variant.type}</option>
              )
            })}
          </select>
          <button type="button" onClick={this.addToCart} disabled={ats  > 0 ? '' : 'disabled'}>
            {ats > 0 ? 'Add To Cart' : 'Sold Out'}
          </button>
        </div>
      </div>
    );
  },

});

module.exports = FluxProduct;
&#13;
&#13;
&#13;

FluxCartActions.js

&#13;
&#13;
var AppDispatcher = require('../dispatcher/AppDispatcher');
var FluxCartConstants = require('../constants/FluxCartConstants');

// Define action methods
var FluxCartActions = {

  // Receive inital product data
  receiveProduct: function(data) {
    AppDispatcher.handleAction({
      actionType: FluxCartConstants.RECEIVE_DATA,
      data: data
    })
  },

  // Set currently selected product variation
  selectProduct: function(index) {
    AppDispatcher.handleAction({
      actionType: FluxCartConstants.SELECT_PRODUCT,
      data: index
    })
  },

  // Add item to cart
  addToCart: function(sku, update) {
    AppDispatcher.handleAction({
      actionType: FluxCartConstants.CART_ADD,
      sku: sku,
      update: update
    })
  },

  // Remove item from cart
  removeFromCart: function(sku) {
    AppDispatcher.handleAction({
      actionType: FluxCartConstants.CART_REMOVE,
      sku: sku
    })
  },

  // Update cart visibility status
  updateCartVisible: function(cartVisible) {
    AppDispatcher.handleAction({
      actionType: FluxCartConstants.CART_VISIBLE,
      cartVisible: cartVisible
    })
  }

};

module.exports = FluxCartActions;
&#13;
&#13;
&#13;

这是我的package.json文件:

&#13;
&#13;
{
  "name": "flux-pricing",
  "version": "0.0.1",
  "description": "Pricing component with flux",
  "main": "app/assets/javascripts/cart.js",
  "dependencies": {
    "flux": "^2.0.0",
    "react": "^0.12.0",
    "underscore": "^1.7.0"
  },
  "devDependencies": {
    "browserify": "~>6.3.0",
    "envify": "~3.0.0",
    "jest-cli": "^0.4.0",
    "react-tools": "^0.12.2",
    "reactify": "^1.0",
    "watchify": "~2.1.0"
  },
  "scripts": {
    "start": "watchify -o app/assets/javascripts/app.js -v -d .",
    "build": "browserify . | uglifyjs -cm > app/assets/javascripts/app.min.js",
    "test": "jest"
  },
  "jest": {
    "rootDir": "app/assets/javascripts",
    "scriptPreprocessor": "<rootDir>/__tests__/preprocessor.js",
    "testFileExtensions": [
      "js",
      "jsx"
    ],
    "unmockedModulePathPatterns": ["react"],
    "testPathIgnorePatterns": [
      "preprocessor.js",
      "node_modules"
    ]
  },
  "browserify": {
    "transform": [
      "reactify",
      "envify"
    ]
  }
}
&#13;
&#13;
&#13;

我通过在app目录中运行npm test来运行测试。

2 个答案:

答案 0 :(得分:2)

我认为问题可能是您需要beforeEach中的模拟,这可能会取消genMockFunction为您做的自动jest。我只是改变了我编写的一些测试,要求beforeEach中的模块以相同的方式,并且它们也会破坏,就像函数不是模拟一样。

尝试在测试之外只需要依赖项。

var React = require('react/addons')
    , TestUtils = React.addons.TestUtils
    , FluxProduct = require('../FluxProduct')
    , FluxCartActions = require('../../actions/FluxCartActions');

describe(...);

据推测,你这样做是为了“重置”模拟,以便测试没有副作用,但如果jest还没有为你处理,那我会感到惊讶。

答案 1 :(得分:1)

是的,不久前我遇到了同样的问题。每当我从事件中触发某些东西时,Jest的自动模拟似乎不起作用。也许值得在Github上打开一个问题。

与此同时,我通过手动模拟页面顶部的操作来解决问题:

jest.mock('../../actions/FluxCartActions');

在Jest中不是一个非常优雅的解决方案,但它有效。我认为你的意思是:

var addToCartBtn = getElementByTag(product, 'button');

而不是

var addToCartBtn = getElementByTag(product, 'select');

因此,您可以模拟按钮的单击并调用addToCart。