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:猫鼬测试的主要内容,如果未能解决你的问题,请参考以下文章