想用sinon写我的第一个mocha单元测试,但无法获得正确的心理模型。

问题描述 投票:0回答:1

我昨天和今天上午一直在研究这个问题,试图通过在用户模型中伪造mockingstubbing mongoose调用的方式来适当地对userController进行单元测试。

我已经写好了我认为userController所需要的所有测试的脚手架,但是却在努力让mock工作。

目前我得到的结果是

1) userController
       getUserByID
         should return a user if id is valid & the user exists:
     TypeError: Cannot read property 'have' of undefined
      at Context.<anonymous> (test/controllers/userController.spec.js:58:25)

如果能得到一些提示,我将非常感激...

userController.spec.js

    var expect = require('chai').expect;
    var assert = require('chai').assert;
    var should = require('chai').should;
    var sinon = require('sinon');
    const userModel = require('../../models/user')
    var userController = require('../../controllers/userController');


    describe('userController', function() {
        

        const mockResponse = (fake) => {
            return {
                send: fake
            };
        }

        // this is just example how you can design the fake request, you can also add header property if your website needs one!
        // I'm not even going to use any of these stuff inside request
        const mockRequest = (session, body) => ({
            session,
            body,
        });

        before(() => {
            
        });

        after(() => {
            //user.findById.restore();
            //done();
        });

        describe('getUserByID', function() {
            
            it('should throw error if no arg passed in', function() {
              
            })

            it('should return a user if id is valid & the user exists', async function() {
                //fake a user document
                var fake = sinon.fake.returns(
                    {
                        "id": 1,
                        "password": "userPassword",
                        "firstName": "userFirstName",
                        "lastName": "userLastName",
                        "email": "[email protected]",
                        "allergens": ["Tree", "Grass"], 
                        "severity": "High",
                        "createdAt": Date.now
                    }
                ) 
                sinon.replace(userModel, 'findById', fake);
                //const users = await userController.getUserByID(1);
                const user = userController.getUserByID(1);
                user.should.have.length(1);
            })

            it('should return 200 OK if id is valid & the user exists', function() {
                 
            })

            it('should return 500 SERVER ERROR id is valid & the user does not exist', function() {
                 
            })

            
        })

        describe('getUserByEmail', function() {
            
            it('no argument should throw error', function() {
            
            })

            it('valid email & user exists should return a user', function() {
                 
            })

            it('valid email & user exists should return 200 OK', function() {
                 
            })

            it('valid email & user does not exist should return 500 SERVER ERROR', function() {
                 
            })

            
        })

        describe('addUser', function() {
            
            it('no argument should throw error', function() {
                //assert the error code
            })

            it('user with email already exists, get 400 error', function() {
                 //assert the error code
            })

            it('user with email does not exist, should return user', function() {
                 //check user object has all the fields
            })

            it('user with email does not exist, should return 201 OK', function() {
                 //assert the response code is 201
            })

            it('valid email & user does not exist should return 500 SERVER ERROR', function() {
                 //assert the error code
            })

            
        })

        describe('getProfile', function() {
            
            it('no argument should throw error', function() {
                //assert the error code
            })

            it('user found, response is 200 OK', function() {
                 //assert the response code is 200
            })

            it('user found, user is returned', function() {
                 //check user object has all the fields
            })

            it('valid email & user does not exist should return 500 SERVER ERROR', function() {
                 //assert the error code
            })

            
        })

        describe('updateUser', function() {
            
            it('no argument should throw 500 error', function() {
                //assert the error code
            })

            it('user with email exists, response is 400', function() {
                 //assert the response code is 200
            })

            it('attempt to update password should fail with 400 error', function() {
                 //check user object has all the fields
            })

            it('valid user updated, response is 200 OK', function() {
                 //assert the error code
            })

            it('valid user updated, updated user is returned', function() {
                //assert the error code
            })

            
        })

        describe('deleteUser', function() {
            
            it('no argument should throw 500 error', function() {
                //assert the error code
            })

            it('user with id exists, response is 200', function() {
                 //assert the response code is 200
            })

            it('user with id exists, failed to delete', function() {
                 //assert the 500 error code
            })

            it('user with id does not exist, repsonse is 500', function() {
                //assert the error code
            })

            
        })
    })

userController.js

// Provide the controller a link to the user model
const user = require("../models/user");
// For token generating and authentication
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");

//Function to search users by ID
const getUserByID = async (req, res) => {
  try {
    const foundUser = await user.findById(req.params.id);
    
    // Do not return user's password in JSON!
    const returnUser = JSON.parse(JSON.stringify(foundUser));
    delete returnUser.password;
    
    res.status(200).json({ user: returnUser });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

//Set-up mongoose.
const mongoose = require("mongoose");
//To silence depcraction warnings
mongoose.set('useCreateIndex', true);
const uniqueValidator = require("mongoose-unique-validator");

//Create a new schema for users
const Schema = mongoose.Schema;
const userSchema = new Schema({
  password: { 
    type: String,
    required: [true, 'Please add a password'],
  },
  firstName: {
    type: String, 
    required: [true, 'Please add a first name']
  },
  lastName: String,
  email: {
    type: String, 
    required: [true, 'Please add an email address'],
    unique: true,
    uniqueCaseInsensitive: true
  },
  allergens: [{
    type: String, 
    enum:["Tree", "Grass", "Weed", "Pollution"]
  }],
  severity: {
    type: String, 
    enum: ["High", "Medium", "Low", "None"],
    required: [true, "Please indicate the hayfever severity"]
  },
  createdAt: {
    type: Date,
    default: Date.now()
  }
});

//Create model from the user schema
userSchema.plugin(uniqueValidator, {message: "Error, that {PATH} is already taken."});
const users = mongoose.model("user", userSchema, "user");

module.exports = users;
javascript node.js mocha chai sinon
1个回答
1
投票

你的代码上有几个错误。

  1. 如果你想你使用 should,你需要调用它。(参考资料).
  2. 职能 getUserByID() 需要2个参数。
  3. 功能 getUserByID() 返回Promise<void>,不需要将其赋值给变量并检查其值。
  4. 方法 Model.findById() 返回Query,你需要 exec() 以从查询中获得价值。(参考资料).
  5. 为了使方法的存根 findByIduserModel您需要创建存根,而不是替换它。

注意事项 我根据我上面的观点添加了很多注释,你可以查看第1到5点。

文件 userController.js

const user = require('./userModel');

// 2. This function request 2 arguments!
// 3. This function return Promise<void>.
const getUserByID = async (req, res) => {
  try {
    // 4. Do not forget to add .exec()
    // https://mongoosejs.com/docs/api.html#model_Model.findById
    const foundUser = await user.findById(req.params.id).exec();

    const returnUser = JSON.parse(JSON.stringify(foundUser));
    delete returnUser.password;

    res.status(200).json({ user: returnUser });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

module.exports = { getUserByID };

文件 userController.spec.js。

// 1. You just need to pick one between expect, assert, should.
// For this example, we try should.
// https://www.chaijs.com/guide/styles/#should
require('chai').should();
const sinon = require('sinon');

const userController = require('./userController');
const userModel = require('./userModel');

describe('userController', function () {
  describe('getUserByID', function () {
    it('should return a user if id is valid & the user exists', async function () {
      // Fake a user document.
      const fakeUser = {
        id: 1,
        password: 'userPassword',
        firstName: 'userFirstName',
        lastName: 'userLastName',
        email: '[email protected]',
        allergens: ['Tree', 'Grass'],
        severity: 'High',
        createdAt: Date.now,
      };
      // 5. Create stub userModel method findById.
      const stubUserFindById = sinon.stub(userModel, 'findById');
      stubUserFindById.returns({
        exec: sinon.fake.resolves(fakeUser),
      });

      // Create fake function for res.status and res.status.json.
      const fakeResJson = sinon.fake();
      const fakeResStatus = sinon.fake.returns({
        json: fakeResJson,
      });

      // Create dummy request: to satisfy getUserById input (1).
      const req = { params: { id: 1 } };
      // Create dummy response.
      const res = {
        status: fakeResStatus,
      };

      // 2. Function getUserById requires 2 arguments!
      // 3. Function getUserById return Promise<void>, so no need to check the result.
      await userController.getUserByID(req, res);

      // But verify that stub & fake get called with correct parameter.
      // Verify stub called.
      stubUserFindById.calledOnce.should.equal(true);
      // Verify stub called with correct arguments.
      stubUserFindById.calledWith(req.params.id).should.equal(true);
      // Verify fake res.status get called.
      fakeResStatus.calledOnce.should.equal(true);
      // Verify fake res.status called with argument 200.
      fakeResStatus.calledWith(200).should.equal(true);
      // Verify fake res.status.json called.
      fakeResJson.calledOnce.should.equal(true);
      // Verify fake res.status.json called with correct argument.
      fakeResJson.args[0][0].should.be.an('object');
      fakeResJson.args[0][0].should.have.property('user');
      // Verify property password removed.
      fakeResJson.args[0][0].user.should.not.have.property('password');
      fakeResJson.args[0][0].user.should.have.property('id', fakeUser.id);
      fakeResJson.args[0][0].user.should.have.property('firstName', fakeUser.firstName);
      fakeResJson.args[0][0].user.should.have.property('lastName', fakeUser.lastName);
      // And check other properties as well.

      // Restore stub.
      stubUserFindById.restore();
    });
  });
});

用mocha运行。

$ mocha userController.spec.js 


  userController
    getUserByID
      ✓ should return a user if id is valid & the user exists


  1 passing (9ms)

$

希望对你有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.