【问题标题】:Testing Node/Express API with Mocha/Chai使用 Mocha/Chai 测试 Node/Express API
【发布时间】:2017-10-01 21:30:57
【问题描述】:

我目前正在尝试使用 mocha chai 测试我的节点 api。我遇到了一个测试实际上应该失败但正在通过的场景。如果您想使用它,我有一个我正在这里构建的当前 API 的回购:enter link description here。但是,我仍然要遍历这个问题中的代码。

我正在尝试使用以下代码测试控制器:

import chai, { expect } from 'chai';
import chaiHttp from 'chai-http';
import server from '../../src/app';

chai.use(chaiHttp);

describe('Authentication Controller', () => {

  const testUser = {
    email_address: 'test@test.com',
    password: 'test'
  };

  describe('login success', () => {
    it('responds with status 200', done => {
      chai.request(server)
        .post('/api/auth/login')
        .send(testUser)
        .end((err, res) => {
          expect(res).to.have.status(200);
          done();
        });
    });
  });

  describe('login failure', () => {
    it('responds with status 401', done => {
      chai.request(server)
        .post('/api/auth/login')
        .send(testUser.email_address = 'fake@news.com')
        .end((err, res) => {
          expect(res).to.have.status(200);
          done();
        });
    });
  });

});

显然我想测试成功登录和失败的登录尝试。但是,来自服务器的两个响应状态都是200,这不应该是这种情况。在Postman 中测试响应状态时,当个人尝试使用不存在的电子邮件地址或不匹配的密码登录时,它返回401 的状态。如果我写一个测试

expect(1).to.equal(1) => 测试通过。 expect(1).to.equal(2) => 测试失败。

这里是处理登录请求的controller函数:

export function login(req, res) {
  User.findOne({email: req.body.email})
    .then(user => {
      if (user && bcrypt.compareSync(req.body.password, user.password)) {
        generateToken(res, user);
      } else {
        res.status(401).json({
          success: false,
          message: 'Incorrect username or password.'
        });
      }
    })
    .catch(err => {
      res.json(err);
    });
}

处理请求的model

export function createUser(req) {
  return db('users').insert(Object.assign(req.body,{password: hashPassword(req.body.password)}))
    .then((id) => db('users').select().where('id', id).first());
}

如您所见,我使用的是Knex.js。我已经设置了一个测试数据库并且所有内容都已正确连接,所以我很困惑为什么我的服务器在测试时响应为 200 响应状态?

我只想感谢所有花时间帮助我了解 mocha chai 工作原理的人。我在测试应用程序方面经验很少,但我想开始熟悉自己这样做,因为我相信这是一种很好的做法。

【问题讨论】:

  • 您是否有某种要求将您与 Mocha/Chai 联系在一起?如果没有,我建议你使用 Jest,设置简单 100 倍,测试效果更好。
  • 我已回滚您的编辑以删除您添加到问题中的解决方案。请不要为您的问题添加解决方案。本网站的编辑惯例是问题应该是问题,而答案应该是答案。我看你已经有了答案。如果需要添加,您可以对其进行编辑。
  • 感谢路易斯这样做!我一定会牢记这一点,继续前进。 @Baruch 不,我与 Mocha/Chai 没有任何联系。我将不得不看一下Jest,因为如果它更简单、更容易实现,我完全不反对使用它。

标签: javascript node.js mocha.js chai web-api-testing


【解决方案1】:

我实际上克隆了您的 Github 存储库并尝试运行测试。从我所见,您的代码中有几个不同的问题,如下所示:

1。来自您在问题中发布的控制器功能:

```

export function login(req, res) {
    User.findOne({email: req.body.email})
    .then(user => {
      // [removed because of little relevancy]
    })
    .catch(err => {
      res.json(err);
    });
}

```

问题在于 res.json(err) 行,它实际上以 200 状态代码响应(即使在这种情况下它是一个错误)。这是因为res.json在“发送错误对象”时不会自动为你设置HTTP响应状态码。这使客户端(在本例中为 chai-http)误以为这是一个成功的请求。要正确响应错误状态代码,您可以改用:res.status(500).json(err)

还值得注意的是,您的其他一些控制器功能也涉及此问题。


2。从您的 userModels.js 文件第 10 行,即:

```

return db('users').select().where(req).first();

```

您以不正确的方式使用 Knex API。应该是 ...where('email', req.email)... 这是您的请求失败的最初原因。


3。您以不同的方式设置单元测试:

测试编号1(登录成功):

```

chai.request(server)
        .post('/api/auth/login')
        .send(testUser)

```

测试编号2(登录失败):

```

chai.request(server)
        .post('/api/auth/login')
        .send(testUser.email_address = 'fake@news.com')

```

那么,发生了什么?

在第一个测试中,您将一个对象传递给.send(),而在第二个测试中,您只是传递了一个表达式。以这种方式完成后,模型处理程序 userModels.findOne() 在第一次测试中收到了一个带有键 email_addresspassword 的对象,但对于第二次测试,它没有。

此外,在您的第一个测试用例中,您发送了 testUser.email_address,但在您的控制器函数中,您引用了 req.body.email

所有这些,除了问题号。 1 正如我之前提到的,进一步复杂化了您的测试套件,最终导致您的误解。


免责声明:

我上面写的所有内容都是基于你的 Github repo 的源代码,所以如果你在推送代码后修复了一些问题,而我上面的部分(或全部)观点不再有效,请忽略。不过,我希望您已经找到,或者很快就会知道为什么您的代码没有按照您的预期运行!

干杯,

【讨论】:

  • 1.完全有道理! 2. 我之所以通过传入req 来查找用户,是因为我想要一个可重用的功能,而不仅仅是可以通过电子邮件查找用户的功能。只要我传入适当的参数,它就可以正常工作。我看不出有什么大问题。 3. 我调整了它,现在我只是传入单独的 obj。感谢您的所有见解和帮助。我已经编辑了上一个问题。
【解决方案2】:

我只是想在这里发布一个特定于我的经验的答案,以及帮助我​​完成所有这些设置的原因。我真正需要在Repo Proj 上更改的唯一一件事是我传递的用户对象上的属性email。我有email_address,因此在数据库中搜索该列,而它不存在!因此,一旦我改变了这一点,我就开始了正确的道路。

然后我的failed login 测试通过了。但是,我的successful login 没有通过。原因是我使用纯字符串密码为我的数据库播种。因此,当我执行以下条件语句时:

if (user && bcrypt.compareSync(req.body.password, user.password))

它没有通过,因为bcrypt.comparSync 正在寻找经过哈希处理的密码。为了让它工作,我需要在我的 knex 文件中要求babel-register。这让我可以使用es6 并执行我的hashPassword 功能:

test/userSeed.js

import hashPassword from '../../src/helpers/hashPassword';

exports.seed = function(knex, Promise) {
  return knex('users').truncate()
    .then(() => {
      return knex('users')
        .then(() => {
          return Promise.all([
            // Insert seed entries
            knex('users').insert([
              {
                first_name: 'admin',
                last_name: 'admin',
                email: 'admin@admin.com',
                password: hashPassword('test')
              },
              {
                first_name: 'test',
                last_name: 'test',
                email: 'test@test.com',
                password: hashPassword('test')
              }
            ]),
          ]);
        });
    })
};

hashPassword.js

import bcrypt from 'bcrypt';

export default function(password) {
  const saltRounds = 10;
  let salt = bcrypt.genSaltSync(saltRounds);
  return bcrypt.hashSync(password, salt);
}

这导致我在为数据库播种时对我的用户密码进行哈希处理。测试全部通过,并且 api 使用 Postman 按预期工作。

【讨论】:

    猜你喜欢
    • 2016-09-20
    • 2020-10-11
    • 1970-01-01
    • 2016-11-15
    • 2017-01-07
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2015-06-02
    相关资源
    最近更新 更多