2018-09-29

单元测试


1.1 TDD 测试驱动开发 [#](#t11.1 TDD 测试驱动开发)

TDD指的是Test Drive Development,很明显的意思是测试驱动开发,也就是说我们可以从测试的角度来检验整个项目。大概的流程是先针对每个功能点抽象出接口代码,然后编写单元测试代码,接下来实现接口,运行单元测试代码,循环此过程,直到整个单元测试都通过。

1.2 BDD 行为驱动开发 [#](#t21.2 BDD 行为驱动开发)

  • BDD指的是Behavior Drive Development,也就是行为驱动开发。
  • 在TDD中,我们并不能完全保证根据设计所编写的测试就是用户所期望的功能。
  • BDD将这一部分简单和自然化,用自然语言来描述,让开发、测试、BA以及客户都能在这个基础上达成一致。

2. 工具 [#](#t32. 工具)

2.1 chai [#](#t42.1 chai)

const { expect } = require('chai');
//mocha -t 5000
describe('zftest', function () {
    it('#1', function () {
        expect(1 + 1).to.be.equal(2);
    })
    it('#2', function (done) {
        setTimeout(function () {
            expect(1 + 1).to.be.equal(2);
            done();
        }, 1000);
    })
    it('#3', function () {
        return new Promise(function (resolve) {
            setTimeout(function () {
                expect(1 + 1).to.be.equal(2);
                resolve();
            }, 3000);
        });
    });
});

2.2 supertest [#](#t52.2 supertest)

const express = require('express');
const app = express();
app.get('/', function (req, res) {
    res.status(200).json({ name: 'zxmf' });
});
app.listen(8080);
module.exports = app;
const app = require('../src/app');
const request = require('supertest');
describe('app', function () {
    it('/', function (done) {
        request(app)
            .get('/')
            .expect('Content-Type', /json/)
            .expect('Content-Length', "15")
            .expect(200)
            .end(done);
    });
});

3. egg.js [#](#t63. egg.js)

  • 约定test目录为存放所有的测试脚本的目录
  • 测试脚本文件统一按 ${filename}.test.js 命名,必须以 .test.js 作为文件后缀。

3.1 配置命令 [#](#t73.1 配置命令)

{
  "scripts": {
    "test": "egg-bin test"
  }
}

3.2 执行顺序 [#](#t83.2 执行顺序)

describe('exec order', () => {
    before(() => console.log(1));
    before(() => console.log(2));
    after(() => console.log(6));
    beforeEach(() => console.log(3));
    afterEach(() => console.log(5));
    it('should work', () => console.log(4));
});

3.2 测试接口返回值 [#](#t93.2 测试接口返回值)

supertest

it('promise 200', () => {
    return app.httpRequest()
        .get('/')
        .expect(200)
});

it('callback 200', done => {
    app.httpRequest()
        .get('/')
        .expect(200, done);
});

it('done 200', done => {
    app.httpRequest()
        .get('/')
        .expect(200)
        .end(done)
});

it('await 200', async () => {
    await app.httpRequest()
        .get('/')
        .expect(200);
});

3.3 测试控制器 [#](#t103.3 测试控制器)

const { app, mock, assert } = require('egg-mock/bootstrap');
describe('test/controller/home.test.js', function () {
    describe('GET /', () => {
        it('should 200 and get body', () => {
            return app.httpRequest()
                .get('/')
                .expect(200)
                .expect('hello')
        })

        it('should 200 and get reqeust body', () => {
            app.mockCsrf();
            return app.httpRequest()
                .post('/post')
                .type('form')
                .send({ name: 'zxmf' })
                .expect(200)
                .expect({ name: 'zxmf' })
        })
    });
});

3.4 测试服务 [#](#t113.4 测试服务)

Service 相对于 Controller 来说,测试起来会更加简单, 我们只需要先创建一个 ctx,然后通过 ctx.service.${serviceName} 拿到 Service 实例, 然后调用 Service 方法即可。 \app\service\user.js

const { Service } = require('egg');
class UserService extends Service {
    async create(user) {
        return await this.ctx.model.User.create(user);
    }
    async get(username) {
        return await this.ctx.model.User.findOne({ username });
    }
}
module.exports = UserService;

\test\service\user.test.js

const { app, assert } = require('egg-mock/bootstrap');
describe('get name', () => {
    it('create user', async () => {
        const ctx = app.mockContext();
        const doc = await ctx.service.user.create({ username: 'zxmf', password: '123456', email: '1@qq.com' });
        assert(doc);
        assert(doc.username == 'zxmf');
    });
    it('get user', async () => {
        const ctx = app.mockContext();
        const doc = await ctx.service.user.get('zxmf');
        assert(doc);
        assert(doc.username == 'zxmf');
    });
});

3.5 测试扩展 [#](#t123.5 测试扩展)

3.5.1 application [#](#t133.5.1 application)

app\extend\application.js

module.exports = {
    get name() {
        return 'app-name';
    }
}

\test\extend\application.test.js

const { app, assert } = require('egg-mock/bootstrap');
describe('test app', () => {
    it('test app', () => {
        assert(app.name == 'app-name');
    });
});

3.5.2 context [#](#t143.5.2 context)

app\extend\context.js

module.exports = {
    get isXHR() {
        return this.get('X-Requested-With') == 'XMLHttpRequest';
    }
}

\test\extend\context.test.js

const { app, assert } = require('egg-mock/bootstrap');
describe('test context', () => {
    it('XHR is true', () => {
        const ctx = app.mockContext({
            headers: {
                "X-Requested-With": "XMLHttpRequest"
            }
        });
        assert(ctx.isXHR == true);
    });

    it('XHR is true', () => {
        const ctx = app.mockContext({
            headers: {
                "X-Requested-With": "SuperAgent"
            }
        });
        assert(ctx.isXHR == false);
    });
});

3.5.3 request [#](#t153.5.3 request)

app\extend\request.js

module.exports = {
    get isChrome() {
        return this.get('User-Agent').toLowerCase().includes('chrome');
    }
}

\test\extend\request.test.js

const { app, assert } = require('egg-mock/bootstrap');
describe('test context', () => {
    it('XHR is true', () => {
        const ctx = app.mockContext({
            headers: {
                "X-Requested-With": "XMLHttpRequest"
            }
        });
        assert(ctx.isXHR == true);
    });

    it('XHR is true', () => {
        const ctx = app.mockContext({
            headers: {
                "X-Requested-With": "SuperAgent"
            }
        });
        assert(ctx.isXHR == false);
    });
});

3.5.4 response [#](#t163.5.4 response)

app\extend\response.js

module.exports = {
    get isSuccess() {
        return this.status == 200;
    }
}

\test\extend\response.test.js

const { app, assert } = require('egg-mock/bootstrap');
describe('test app', () => {
    it('response isSuccess is true', () => {
        const ctx = app.mockContext();
        ctx.status = 200;
        assert(ctx.response.isSuccess == true);
    });

    it('response isSuccess is false', () => {
        const ctx = app.mockContext();
        ctx.status = 404;
        assert(ctx.response.isSuccess == false);
    });
});

3.5.5 helper [#](#t173.5.5 helper)

app\extend\helper.js

module.exports = {
    money(val) {
        const lang = this.ctx.get('Accept-Language');
        if (lang.includes('zh-CN')) {
            return `¥ ${val}`;
        } else {
            return `$ ${val}`;
        }
    }
}

\test\extend\helper.test.js

const { app, assert } = require('egg-mock/bootstrap');
describe('test helper', () => {
    it('should RMB', () => {
        const ctx = app.mockContext({
            headers: {
                "Accept-Language": "zh-CN"
            }
        });
        assert(ctx.helper.money(100) == '¥ 100');
    });

    it('should Dollar', () => {
        const ctx = app.mockContext();
        assert(ctx.helper.money(100) == '$ 100');
    });
});

Gitalking ...

Markdown is supported

Be the first guy leaving a comment!