markdown 挑战13:猫鼬测试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown 挑战13:猫鼬测试相关的知识,希望对你有一定的参考价值。

In this challenge you will add integration tests to your API. The goal is to create tests which cover all the correct/positive scenarios as well as tests for negative scenarios such as a 404 Not Found.

## Requirements

* NPM install devDependencies for testing and update `package.json` file
* Update `server.js` to prevent `mongoose.connect` and `app.listen` from running during tests
* Export `app` for testing
* Update `config.js` with the test database URL
* Create a `/test/notes.test.js` file and tests for each endpoint

## Install NPM Dev-Dependencies for testing

You need several packages for testing which are included in the starter. But you should ensure they are correct. Double check the `package.json` for the following packages.

* `mocha` the testing framework
* `chai` the assertion library
* `chai-http` a `chai` plug-in which allows you to make `http` requests to the API
* `cross-env` which provides a convenient way to set environment variables across platforms

If they are not installed, you can install as follows.

```sh
npm install mocha chai chai-http cross-env --save-dev
```

Remember to add the `--save-dev` flag so the packages are saved under `devDependencies`.

### Configure NPM script to run tests

Add `"test": "mocha"` to your `scripts` property in `package.json`. Notice, the `cross-env` command will set the `NODE_ENV` to `test` when run your test suite using the `npm test` command.

```json
  "scripts": {
    "start": "node server.js",
    "test": "cross-env NODE_ENV=test mocha --file test/server.js"
  },
```

### Update the `server.js`

You need to prevent `mongoose.connect` and `app.listen` from executing when running tests. Wrap the calls in a `process.env.NODE_ENV !== 'test'` condition. And export `app`.

```js
if (process.env.NODE_ENV !== 'test') {

  mongoose.connect(MONGODB_URI)
    .then(instance => { 
      REMOVED FOR BREVITY

  app.listen(PORT, function () { ...
    REMOVED FOR BREVITY
  }

module.exports = app; // Export for testing
```

### Update the `config.js`

Next, in `config.js`, add an entry for the test database URL. Add the following to your `module.exports` object

```js
  TEST_MONGODB_URI: process.env.TEST_MONGODB_URI || 'mongodb://localhost/noteful-test'
```

### Create `notes.test.js`

Create a new test file `/test/notes.test.js`. Recall that Mocha will automatically run any file in the `/test/` directory. At the top of the file, require the following packages.

* Require
  * `chai`, `chai-http` and `mongoose` packages
  * `app` from `server.js`
  * `config.js` and destructure the `TEST_MONGODB_URI` into a constant
  * `note` Mongoose model for Notes
  * `/db/seed/notes` seed data

Finally, configure `expect` as your assertion library and load `chai-http` with `chai.use()`

The start of your file should look something like this:

```js
const chai = require('chai');
const chaiHttp = require('chai-http');
const mongoose = require('mongoose');

const app = require('../server');
const { TEST_MONGODB_URI } = require('../config');

const Note = require('../models/note');

const seedNotes = require('../db/seed/notes');

const expect = chai.expect;
chai.use(chaiHttp);
```

Next, configure the [Mocha hooks](https://mochajs.org/#hooks) manage the database during the tests..

* `before()`: connect to the database before all tests
* `beforeEach()`: seed data runs before each test
* `afterEach()`: drop database runs after each test
* `after()`: disconnect after all tests

Add the following code to your script, place it inside a `describe()` which wraps your tests.

```js
  before(function () {
    return mongoose.connect(TEST_MONGODB_URI)
      .then(() => mongoose.connection.db.dropDatabase());
  });

  beforeEach(function () {
    return Note.insertMany(seedNotes);
  });

  afterEach(function () {
    return mongoose.connection.db.dropDatabase();
  });

  after(function () {
    return mongoose.disconnect();
  });
```

### Create Tests for each endpoint

Your challenge is to create tests for the Notes endpoints that check both positive (successful) and negative (error) scenarios.

### Integration Test Recipes

To help get you started, here are a few sample tests which demonstrate different "recipes".

#### Serial Request - Call API then call DB then compare

1) First, call the API to insert the document
2) then call the database to retrieve the new document
3) then compare the API response to the database results

> Notice you need to set the `res.body` to `body` declared at a higher scope so that the next `.then()` has access to the value.

```js
  describe('POST /api/notes', function () {
    it('should create and return a new item when provided valid data', function () {
      const newItem = {
        'title': 'The best article about cats ever!',
        'content': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...'
      };

      let res;
      // 1) First, call the API
      return chai.request(app)
        .post('/api/notes')
        .send(newItem)
        .then(function (_res) {
          res = _res;
          expect(res).to.have.status(201);
          expect(res).to.have.header('location');
          expect(res).to.be.json;
          expect(res.body).to.be.a('object');
          expect(res.body).to.have.keys('id', 'title', 'content', 'createdAt', 'updatedAt');
          // 2) then call the database
          return Note.findById(res.body.id);
        })
        // 3) then compare the API response to the database results
        .then(data => {
          expect(res.body.id).to.equal(data.id);
          expect(res.body.title).to.equal(data.title);
          expect(res.body.content).to.equal(data.content);
          expect(new Date(res.body.createdAt)).to.eql(data.createdAt);
          expect(new Date(res.body.updatedAt)).to.eql(data.updatedAt);
        });
    });
  });
```

#### Serial Request - Call DB then call API then compare:

1) First, call the database to get an ID
2) then call the API with the ID
3) then compare database results to API response

> Notice again, you need to capture the `_data` response and set it to the variable `data` declared at a higher scope so that the next `.then()` has access to the value.

```js
  describe('GET /api/notes/:id', function () {
    it('should return correct note', function () {
      let data;
      // 1) First, call the database
      return Note.findOne()
        .then(_data => {
          data = _data;
          // 2) then call the API with the ID
          return chai.request(app).get(`/api/notes/${data.id}`);
        })
        .then((res) => {
          expect(res).to.have.status(200);
          expect(res).to.be.json;

          expect(res.body).to.be.an('object');
          expect(res.body).to.have.keys('id', 'title', 'content', 'createdAt', 'updatedAt');

          // 3) then compare database results to API response
          expect(res.body.id).to.equal(data.id);
          expect(res.body.title).to.equal(data.title);
          expect(res.body.content).to.equal(data.content);
          expect(new Date(res.body.createdAt)).to.eql(data.createdAt);
          expect(new Date(res.body.updatedAt)).to.eql(data.updatedAt);
        });
    });
  })
```

#### Parallel Request - Call both DB **and** API, then compare:

1) Call the database **and** the API
2) Wait for both promises to resolve using `Promise.all`
3) then compare database results to API response

> The advantage of this approach is that both responses are available in the same scope, so you do not need to set a variable at a higher scope. But this only works with GET endpoints because there are no DB changes performed.

```js
  describe('GET /api/notes', function () {
    // 1) Call the database **and** the API
    // 2) Wait for both promises to resolve using `Promise.all`
    return Promise.all([
        Note.find(),
        chai.request(app).get('/api/notes')
      ])
      // 3) then compare database results to API response
        .then(([data, res]) => {
          expect(res).to.have.status(200);
          expect(res).to.be.json;
          expect(res.body).to.be.a('array');
          expect(res.body).to.have.length(data.length);
        });
  });
```

## Continuous Integration and Deployment

Follow the [Setup Travis and Heroku CICD: CLI Ninja](https://gist.github.com/cklanac/789e902b2f03963fabaf9032f35b19d7#file-cicd-config-ninja-md) to deploy your app to Heroku

以上是关于markdown 挑战13:猫鼬测试的主要内容,如果未能解决你的问题,请参考以下文章

markdown 挑战10:Knex JS测试

markdown 挑战20测试JWT

markdown 挑战:19.1测试本地身份验证

markdown 挑战19:测试Auth

markdown 挑战05:使用摩卡柴进行测试

带有猫鼬的 GraphQL