markdown 挑战10:Knex JS测试

Posted

tags:

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

# Noteful Challenge - Integration Testing

In this challenge you will re-implement testing on the Noteful app, this time we a real database. And you'll update the tests to cross-check the API results against the database.

## Requirements

* NPM install devDependencies for testing and update `package.json` file
* Update server.js to prevent `app.listen` from running during tests
* Add a `test` property to knexfile.js
* Create a test database
* Create Seed Data

## Install NPM Dev-Dependencies for testing

Install the main dependencies you need for testing

* `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

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

### 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"
  },
```

### Update server.js

#### Prevent `app.listen` execution and export `app`

Wrap the `app.listen` call in a `require.main === module` conditional to prevent it from running during tests. And add `module.exports = app;` to export your app to make it available to your test suite.

```js
// Listen for incoming connections
if (require.main === module) {
  app.listen(PORT, function () {
    console.info(`Server listening on ${this.address().port}`);
  }).on('error', err => {
    console.error(err);
  });
}

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

### Create a test database

Tests need to run against a separate test database. Create a `noteful-test` database.

```sh
createdb -U dev noteful-test
```

Or, if you have difficulties with the `createdb` then you can run `CREATE DATABASE` in the `psql` shell.

```psql
psql -U dev noteful-app
CREATE DATABASE "noteful-test";
```

### Add a `test` property to knexfile.js

Update the `knexfile.js` to include a `test` property with a `TEST_DATABASE_URL` which defaults to your local test database.

The complete `knexfile.js` will look like this.

```js
module.exports = {
  development: {
    client: 'pg',
    connection: process.env.DATABASE_URL || 'postgres://localhost/noteful-app',
    debug: true, // http://knexjs.org/#Installation-debug
    pool: { min: 1, max: 2 }
  },
  test: {
    client: 'pg',
    connection: process.env.TEST_DATABASE_URL || 'postgres://localhost/noteful-test',
    pool: { min: 1, max: 2 }
  },
  production: {
    client: 'pg',
    connection: process.env.DATABASE_URL
  }
};
```

### Add a seed database utility

In the `./db` directory, create a `seedData.js` file and copy in the following code.

```js
const knex = require('../knex');
const util = require('util');
const exec = util.promisify(require('child_process').exec);

module.exports = function(file, user = 'dev') {
  return exec(`psql -U ${user} -f ${file} -d ${knex.client.connectionSettings.database}`);
};
```

### Create `/test/server.test.js` file

Next, create the `/test/server.test.js` file. And copy [this gist](https://gist.github.com/cklanac/ead56e13159c7729f4cf9d0bcba70af8) into the file.


The file contains a suite of tests which are similar to the solution form the previous challenge. The main difference are the Mocah life-cycle hooks.

* `before` runs before *all* the tests in the `describe` block
* `beforeEach` runs before *each* test in the `describe` block
* `afterEach` runs after *each* test in the `describe` block
* `after` runs after *all* the tests in the `describe` block

```js
  beforeEach(function () {
    return seedData('./db/noteful.sql', 'dev');
  });

  after(function () {
    return knex.destroy(); // destroy the connection
  });
```

Run your tests using `npm test` to confirm the setup is correct.

```sh
npm test
```

You should see a set of successful tests. If any of the test fail, please fix them before proceeding.

### Update your tests to cross-check the DB

Now you are ready to update your test suite to to cross check the API results against the database. Below are 4 examples which show common approaches. Use these are guides to implement your own integrations tests

In `server.test.js` file, find the test with the description `should return the default of 10 Notes` and update it to the following. The test will first query the database to get the count of notes, then call the API and verify the response length is the same as the database count

```js
  it('should return the default of 10 Notes ', function () {
    let count;
    return knex.count()
      .from('notes')
      .then(([result]) => {
        count = Number(result.count);
        return chai.request(app).get('/api/notes');
      })
      .then(function (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(count);
      });
  });
```

Find the test with the description `should return correct search results for a valid query` and update it to the following. In this test you will call to the API with a `searchTerm` and then perform the same query against the database. The test And compare the results. You'll chain these requests using `.then()`.

```js
  it('should return correct search results for a valid query', function () {
    let res;
    return chai.request(app).get('/api/notes?searchTerm=gaga')
      .then(function (_res) {
        res = _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(1);
        expect(res.body[0]).to.be.an('object');
        return knex.select().from('notes').where('title', 'like', '%gaga%');
      })
      .then(data => {
        expect(res.body[0].id).to.equal(data[0].id);
      });
  });
```

In the example you'll query the database and call the api then compare the results. But this time you'll use `Promise.all()` so the async requests can be made simultaneously, and you can compare the results with create a variable at a higher scope. Find the test with the description `should return correct notes` and update to with the following.

```js
    it('should return correct notes', function () {

      const dataPromise = knex.first()
        .from('notes')
        .where('id', 1000);

      const apiPromise = chai.request(app)
        .get('/api/notes/1000');

      return Promise.all([dataPromise, apiPromise])
        .then(function ([data, res]) {
          expect(res).to.have.status(200);
          expect(res).to.be.json;
          expect(res.body).to.be.an('object');
          expect(res.body).to.include.keys('id', 'title', 'content');
          expect(res.body.id).to.equal(1000);
          expect(res.body.title).to.equal(data.title);
        });
    });
```

And when creating, you will `.post()` the new document to the endpoint, then using the id returned, query the database and verify it was saved correctly. Find the test with the correct description and update it with the following

```js
  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...',
      'tags': []
    };
    let body;
    return chai.request(app)
      .post('/api/notes')
      .send(newItem)
      .then(function (res) {
        body = res.body;
        expect(res).to.have.status(201);
        expect(res).to.have.header('location');
        expect(res).to.be.json;
        expect(body).to.be.a('object');
        expect(body).to.include.keys('id', 'title', 'content');
        return knex.select().from('notes').where('id', body.id);
      })
      .then(([data]) => {
        expect(body.title).to.equal(data.title);
        expect(body.content).to.equal(data.content);
      });
  });
```

Your Turn!

Using the patterns described above, update the rest of the tests for *Notes* to verify the changes have been persisted to the DB. Then create similar tests for *Folders* and *Tags*.

## Configure CICD

Open or create a `.travis.yml` file and add the following configuration. This configuration informs Travis that you intend to use a `postgresql` database. And it provides a script to create the test database.

```yml
language: node_js
node_js: node
services: postgresql
before_script:
  - psql -U postgres -c 'CREATE DATABASE "noteful-test";'
```

Complete the CDCD configuration process. You can follow the[ CICD guides

* [Point and Click Guide](https://gist.github.com/cklanac/789e902b2f03963fabaf9032f35b19d7#setup-travis-and-heroku-cicd-point-n-click)
* [CLI Ninja Guide](https://gist.github.com/cklanac/789e902b2f03963fabaf9032f35b19d7#setup-travis-and-heroku-cicd-cli-ninja)

## Configure Heroku

You have 2 options to configure Heroku with Postgres: ElephantSQL and Heroku's Postgres Add-on.

### Using ElephantSQL

On ElephantSQL, create a new database for your production site and copy the postgres connection URL.

In your terminal, run the following command to create your tables and insert sample data. You'll need to replace the sample connection with the URL you copied above

```bash
psql -f ./db/noteful.sql postgres://<UN>:<PW>@baasu.db.elephantsql.com:5432/<DB>
```

On Heroku open your application and go to the setting. Then click "Reveal Config Vars" button. In the KEY field type "DATABASE_URL" and in the VALUE field paste in the ElephantSQL connection URL. You may need to restart the dynos for the change to take effect. Look under the "More" dropdown for the restart dynos option.


### Using Heroku's Postgres Add-On

On Heroku, open your app and go to the Resources tabs. Then the Add-ons section, enter "Postgres" in the search box and select "Heroku Postgres". And then provision a "Hobby Dev - Free" instance of postgres.

Once it is provisions click on "Heroku Postgres :: Database". This should open a new window/tab.
Go to settings and click "View Credentials..." and copy the postgres URI.

In your terminal, run the following command to create your tables and insert sample data. You'll need to replace the sample connection with the Postgres URI you copies above

```bash
psql -f ./db/noteful.sql postgres://<UN>:<PW>@baasu.db.elephantsql.com:5432/<DB>
```

On Heroku open your application and go to the setting. Then click "Reveal Config Vars" button. In the KEY field type "DATABASE_URL" and in the VALUE field paste in the ElephantSQL connection URL. You may need to restart the dynos for the change to take effect. Look under the "More" dropdown for the restart dynos option.

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

如何在 knex.js 迁移中链接承诺

如何将字符集添加到 Knex.js 连接字符串?

如何在 knex.js 中的“join”的“on”条件下使用“and”

Knex.js 和 MySQL:将整数转换为布尔值以进行批量选择

markdown 挑战20测试JWT

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