【问题标题】:Unusual behavior in testing: node.js/ Mocha测试中的异常行为:node.js/ Mocha
【发布时间】:2016-10-28 12:55:17
【问题描述】:

我最近开始使用 nodeJS 并开始使用 Express 和 Wagnor 构建中间件应用程序

下面是我的 package.JSON

{
"devDependencies": {
  "gulp": "3.8.11",
  "gulp-mocha": "2.0.1",
  "mocha": "2.2.4",
  "superagent": "1.2.0",
  "wagner-core": "0.1.0"
 },
  "dependencies": {
    "underscore": "1.5.2",
  "mongodb": "2.2.10",
  "express": "",
  "http-status": "0.1.8",
  "mongoose":""
 },
 "scripts": {
 "test": "mocha test.js"
 }
 }

我陷入了一个非常特殊的情况,并搜索了类似的事件报告,但找不到任何相关信息。我确信我犯了一个愚蠢的错误,需要帮助来识别它。

这是场景,我有一个带有 6 个端点的基本中间件设置,我正在使用 mocha 进行测试。

我有一个包含 6 个测试的 test.js 文件,问题是当我运行它时它会通过一次,然后再次失败,然后再次通过,而无需对环境、代码或测试进行任何干预。这让我非常担心我的应用程序在生产中的行为是否会类似,或者它是与测试相关的问题或已知的 mocha 问题(找不到任何问题)。

下面是我的测试文件

var URL_ROOT = 'http://localhost:3131';
var express = require('express');
var wagner = require('wagner-core');
var assert = require('assert');
var superagent = require('superagent');
var mongoose = require('mongoose');
//mongoose.set('debug', true);

var PRODUCT_ID = '0000000000000000000001';

describe('Store API', function(){
    var server; 
    var Product;
    var Catagory;
    var User;


    before(function() {
        var app = express();

        // Bootstrap sertver
        models = require('./models')(wagner);
        require('./dependencies')(wagner);
        app.use(require('./api')(wagner));
        wagner.invoke(require('./auth'), {app: app});
        server = app.listen(3131);

        //Make Catagory and Product model available in test
        Catagory = models.Catagory;
        Product = models.Product;
        User = models.User;


        app.use( function(req, res, next) {
          User.findOne({}, function(error, user) {
               console.log('Time:', Date.now());
               assert.ifError(error);
               req.user = user;
               next();
           });  //user.findone
       }); //app.use
    }); //before

    after(function(){
        //Shut the server
        server.close();
    }); //after

    beforeEach(function(done) {
        //Make sure Catagories are empty before each test
        Catagory.remove({}, function(error) {
            assert.ifError(error);
            Product.remove({}, function(error) {
                assert.ifError(error);
                User.remove({}, function(error) {
                    assert.ifError(error);
                    //Create Products, Catagories and Users
                    var catagories = [
                        { _id: 'Electronics'},
                        { _id: 'Phones', parent: 'Electronics'},
                        { _id: 'Laptop', parent: 'Electronics'},
                        { _id: 'Meat'}
                    ];

                    // create product data
                    var products = [
                        {
                            _id: '00000000000001',
                            name: 'LG G4',
                            catagory: { _id: 'Phones', ancestors: ['Electronics', 'Phones']},
                            price: {
                                amount: 300,
                                currency: 'USD'
                            }
                        },
                        {
                            _id: '00000000000002',
                            name: 'Asus Zenbook Prime',
                            catagory: { _id: 'Laptop', ancestors: ['Electronics', 'Laptop']},
                            price: {
                                amount: 2000,
                                currency: 'USD'
                            }
                        },
                        {
                            _id: '00000000000003',
                            name: 'MeatOne Goasht Wala',
                            catagory: {_id: 'Meat', ancestors: ['Meat']},
                            price: {
                                amount: 20,
                                currency: 'USD'
                            }
                        }
                    ];

                    var users = [{
                        profile: {
                            username: 'shoaibhb',
                            picture: 'http://pbs.twimg.com/profile_images/364903575/ShoaibHayat_Butt.jpg'
                        },
                        data: {
                            oauth: 'invalid',
                            cart: []
                        }
                    }];

                    Catagory.create( catagories, function(error) {
                        assert.ifError(error)
                        Product.create(products, function(error) {
                            assert.ifError(error)
                              User.create(users, function(error) {
                                  assert.ifError(error);
                                  User.findOne({}, function(err, user) {

                                  });
                             });
                        });
                    });
                });
                done();
            });
        });
    });

    it('can load a Catagory by id', function(done) {
        // Create a single Catagory
        // Catagory.create({ _id: 'Electronics' }, function(error, doc) {
        //     assert.ifError(error);
            var url = URL_ROOT + '/catagory/id/Electronics';
            //Make and HTTP request to localhost:3131/catagory/id/Electronics
            superagent.get(url, function(error, res) {
                assert.ifError(error);
                var result;
                // and make sure we got { _id: 'Electronics'} back
                assert.doesNotThrow(function() {
                    result = JSON.parse(res.text);
                });
                assert.ok(result.catagory);
                assert.equal(result.catagory._id, 'Electronics');
                done();
            });
        //});// ends here
    });

    it('can load all Catagories that have a certain parent', function(done) {

        // Create 4 catagories was here earlier, moved to beforeEach()

        //Catagory.create(catagories, function(error, catagories) {
            var url = URL_ROOT + '/catagory/parent/Electronics';
            //make HTTP request to /catagory/parent/Electronics
            superagent.get(url, function(error, res) {
                assert.ifError(error);
                var result;
                assert.doesNotThrow(function() {
                    result = JSON.parse(res.text);
                });
                assert.equal(result.catagories.length, 2);
                // should be in assending order by _id
                assert.equal(result.catagories[0]._id, 'Laptop');
                assert.equal(result.catagories[1]._id, 'Phones');
                done();
            });
        //});// create cat ends here
    });

    it('can load a product by id', function(done) {
        //Create a single product


        var oneProduct = {
                name: 'LG G4',
                _id: PRODUCT_ID,
                catagory: { _id: 'Phones', ancestors: ['Electronics', 'Phones']},
                price: {
                    amount: 300,
                    currency: 'USD'
                }
            };

        Product.create(oneProduct, function(error, doc) {
          assert.ifError( error);
            var url = URL_ROOT + '/product/id/' + PRODUCT_ID;
            // Make HTTP request to 
            // localhost:3131/product/id/0000000000000000000001
            superagent.get(url, function(error, res) {
                assert.ifError(error);
                var result = {};
                //And make sure we got LG G4 back

                assert.doesNotThrow(function() {
                    result = JSON.parse(res.text);
                });

                assert.ok(result.product);
                assert.equal(result.product._id, PRODUCT_ID);
                assert.equal(result.product.name, 'LG G4');
                done();
            });     
        });
    });

    it('can load all products in a Catagory with sub-catagories', function(done) {

        // moved to beforeEach function 

                var url = URL_ROOT + '/product/catagory/Electronics';
                //Make HTTP Request to loca:3131
                superagent.get(url, function(error, res) {
                    assert.ifError(error);
                    var result;
                    assert.doesNotThrow(function() {
                        result = JSON.parse(res.text);                
                    });
                    assert.equal(result.product.length, 2);
                    // should be assending order by name
                    assert.equal(result.product[0].name, 'Asus Zenbook Prime');
                    assert.equal(result.product[1].name, 'LG G4');

                    //Sort by price , assending
                    var url = URL_ROOT + '/product/catagory/Electronics?price=1';
                    superagent.get(url, function(error, res) {
                        assert.ifError(error);
                        var result;
                        assert.doesNotThrow(function() {
                            result = JSON.parse(res.text);
                        });
                        assert.equal(result.product[0].name, 'LG G4');
                        assert.equal(result.product[1].name, 'Asus Zenbook Prime');
                    });
                    //console.log(error);
                    assert.ifError(error);
                    done();
            });
    });


    it('can load user cart', function(done) {
        var url = URL_ROOT + '/me/';
        User.findOne({}, function( error, user) {
            console.log("error %j",error);
            //assert.ifError(error);
            user.data.cart = [{ product: PRODUCT_ID, quantity: 1}];
            console.log(user.data.cart);
            user.save(function(error) {
                assert.ifError(error);
                superagent.get(url, function(error, res) {
                    assert.ifError(error);
                    assert.equal(res.status, 200);
                    var result;
                    assert.doesNotThrow(function() {
                        result = JSON.parse(res.text).user;
                    });
                    assert.equal(result.data.cart.length,1);
                    assert.equal(result.data.cart[0].product.name, 'Asus Zenbook Prime');
                    assert.equal(result.data.cart[0].quantity, 1);

                });
            });
        });
        done();
    });



    it('can save user cart', function(done) {
        var url = URL_ROOT + '/me/cart/';

        superagent.put(url).send({
             data: {
                 cart: [{ product: PRODUCT_ID, quantity: 1}]
             }
         }).end(function(error, res) {
             assert.ifError(error);
             assert.equal(res.status, status.OK);
             User.findOne({}, function(error, user) {
                 assert.ifError(error);
                 assert.equal(user.data.cart.length, 1);
                 assert.equal(user.data.cart[0].product, PRODUCT_ID);
                 assert.equal(user.data.cart[0], quantity, 1);
             });
         });
         done();
    });
    enter code here

});

当我运行这些测试时,会发生以下情况: a) 6 次测试通过 b) 一些测试失败 c) 有一个空白响应

D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js

D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (81ms)
    √ can load all Catagories that have a certain parent
    √ can load a product by id
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    1) "before each" hook


  5 passing (752ms)
  1 failing

  1) Store API "before each" hook:
     Uncaught TypeError: Cannot read property 'data' of null
      at test-6passing.js:243:17
      at node_modules\mongoose\lib\query.js:1173:16
      at node_modules\mongoose\node_modules\kareem\index.js:109:16




D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (89ms)
    √ can load all Catagories that have a certain parent (38ms)
    √ can load a product by id
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    1) "before each" hook


  5 passing (922ms)
  1 failing

  1) Store API "before each" hook:
     Uncaught TypeError: Cannot read property 'data' of null
      at test-6passing.js:243:17
      at node_modules\mongoose\lib\query.js:1173:16
      at node_modules\mongoose\node_modules\kareem\index.js:109:16




D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (96ms)
    √ can load all Catagories that have a certain parent
    √ can load a product by id
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    √ can save user cart


  6 passing (852ms)


D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js

D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (80ms)
    √ can load all Catagories that have a certain parent
    √ can load a product by id
    1) can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    2) "before each" hook


  4 passing (862ms)
  2 failing

  1) Store API can load all products in a Catagory with sub-catagories:

      Uncaught AssertionError: 0 == 2
      + expected - actual

      -0
      +2

      at test-6passing.js:215:28
      at Request.callback (node_modules\superagent\lib\node\index.js:785:12)
      at IncomingMessage.<anonymous> (node_modules\superagent\lib\node\index.js:
990:12)
      at endReadableNT (_stream_readable.js:913:12)

  2) Store API "before each" hook:
     Uncaught TypeError: Cannot read property 'data' of null
      at test-6passing.js:243:17
      at node_modules\mongoose\lib\query.js:1173:16
      at node_modules\mongoose\node_modules\kareem\index.js:109:16




D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (86ms)
    √ can load all Catagories that have a certain parent (38ms)
    √ can load a product by id
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    1) "before each" hook


  5 passing (744ms)
  1 failing

  1) Store API "before each" hook:
     Uncaught TypeError: Cannot read property 'data' of null
      at test-6passing.js:243:17
      at node_modules\mongoose\lib\query.js:1173:16
      at node_modules\mongoose\node_modules\kareem\index.js:109:16




D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (97ms)
    √ can load all Catagories that have a certain parent (43ms)
    √ can load a product by id
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    √ can save user cart


  6 passing (785ms)


D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (83ms)
    √ can load all Catagories that have a certain parent
    √ can load a product by id (39ms)
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    1) "before each" hook


  5 passing (797ms)
  1 failing

  1) Store API "before each" hook:
     Uncaught TypeError: Cannot read property 'data' of null
      at test-6passing.js:243:17
      at node_modules\mongoose\lib\query.js:1173:16
      at node_modules\mongoose\node_modules\kareem\index.js:109:16




D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js

D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>mocha test-6passing.js


  Store API
express-session deprecated undefined resave option; provide resave option auth.j
s:57:39
express-session deprecated undefined saveUninitialized option; provide saveUnini
tialized option auth.js:57:39
    √ can load a Catagory by id (93ms)
    √ can load all Catagories that have a certain parent (38ms)
    √ can load a product by id
    √ can load all products in a Catagory with sub-catagories
    √ can load user cart
error null
    √ can save user cart


  6 passing (806ms)


D:\Programs_Insalled\nodejs\finalMEAN\edx\retailStore>

所以问题:

  1. 我做错了吗
  2. 我的方法不正确
  3. mocha 中是否存在我不知道的问题
  4. 就是这样

我的环境是 MongoDB、Node、Express、Windows

编辑:完整的代码可以在这里找到:

https://github.com/shoaibhb/retailStore

【问题讨论】:

    标签: node.js express testing tdd mocha.js


    【解决方案1】:

    我不会称之为问题的解决方案,但它解决了我的问题。我更改了beforeEach() 函数,以便在每个测试蚂蚁稳定数据之前分别处理数据的创建和删除。

    这是我所做的代码。

     beforeEach(function(done) {
        // Make sure categories are empty before each test
        Category.remove({}, function(error) {
          assert.ifError(error);
          Product.remove({}, function(error) {
            assert.ifError(error);
            User.remove({}, function(error) {
              assert.ifError(error);
              done();
            });
          });
        });
      });
    
      beforeEach(function(done) {
        var categories = [
          { _id: 'Electronics' },
          { _id: 'Phones', 'parent': 'Electronics' },
          { _id: 'Laptops', 'parent': 'Electronics' },
          { _id: 'Bacon' }
        ];
    
        var products = [
          {
            name: 'LG G4',
            category: { _id: 'Phones', ancestors: ['Electronics', 'Phones'] },
            price: {
              amount: 300,
              currency: 'USD'
            }
          },
          {
            _id: PRODUCT_ID,
            name: 'Asus Zenbook Prime',
            category: { _id: 'Laptops', ancestors: ['Electronics', 'Laptops'] },
            price: {
              amount: 2000,
              currency: 'USD'
            }
          },
          {
            name: 'Flying Pigs Farm Pasture Raised Pork Bacon',
            category: { _id: 'Bacon', ancestors: ['Bacon'] },
            price: {
              amount: 20,
              currency: 'USD'
            }
          }
        ];
    
        var users = [{
          profile: {
            username: 'vkarpov15',
            picture: 'http://pbs.twimg.com/profile_images/550304223036854272/Wwmwuh2t.png'
          },
          data: {
            oauth: 'invalid',
            cart: []
          }
        }];
    
        Category.create(categories, function(error) {
          assert.ifError(error);
          Product.create(products, function(error) {
            assert.ifError(error);
            User.create(users, function(error) {
              assert.ifError(error);
              done();
            });
          });
        });
      });
    

    【讨论】:

      【解决方案2】:

      所以,这里确实没有足够的信息,但我可以告诉你要寻找什么。让我们从您遇到的错误开始:

      Uncaught TypeError: Cannot read property 'data' of null
        at test-6passing.js:243:17
        at node_modules\mongoose\lib\query.js:1173:16
        at node_modules\mongoose\node_modules\kareem\index.js:109:16
      

      所以,第二行告诉我们错误是在 test-6passing.js 第 243 行第 17 列中引发的。所以,不幸的是,SO 没有显示行号,所以我不知道具体在哪里是,但是您可以在文本编辑器中找到该行,它会准确告诉您发生错误时正在执行的函数。

      第一行告诉我们您正在尝试读取对象的“数据”属性,但该对象是null。假设第 243 行第 17 列的函数是 Catagory.remove——我们可以查看该函数以准确了解您的测试中的 null 是什么对象。然后,您必须查看为什么它是null

      这有无数可能的原因。也许您在Catagory.remove 函数中拼错了对象名称?也许信息还没有填充——javascript 是一种异步语言,对于许多来自其他编程语言的人来说很难习惯。

      如果这没有帮助,请尝试使用定义模型及其方法/函数的代码更新您的帖子。

      【讨论】:

        【解决方案3】:

        看起来您的测试并非每次都使数据库处于相同状态。错误在这里:

        it('can load user cart', function(done) {
            var url = URL_ROOT + '/me/';
            User.findOne({}, function( error, user) {
                console.log("error %j",error);
                //assert.ifError(error);
                user.data.cart = [{ product: PRODUCT_ID, quantity: 1}];
        

        其中user 为空。也许某个测试正在删除一个用户而另一个正在添加它?

        【讨论】:

        • 据我所知,beforeEach() 函数按顺序执行,必须为每个测试创建用户。如果它不是出于任何原因创建的,它应该断言一个错误。事实并非如此。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 2017-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-13
        相关资源
        最近更新 更多