当前位置:网站首页>Knowledge of egg testing -- mock, Supertest, coffee
Knowledge of egg testing -- mock, Supertest, coffee
2022-04-23 05:23:00 【process1212】
EGG Framework testing
initialization
const mock = require('egg-mock');
describe('test/index.test.js', () => {
let app;
before(() => {
app = mock.app({
// convert to test/fixtures/apps/example
baseDir: 'apps/example',
// important : To configure framework
framework: true,
});
return app.ready();
});
after(() => app.close());
afterEach(mock.restore);
it('should success', () => {
return app.httpRequest()
.get('/')
.expect(200);
});
});
- The framework and application are different , Apply test current code , And the framework is to test the framework code , So it will be replaced frequently baseDir Achieve the purpose of testing various applications .
- baseDir Hidden rules , We usually put the tested application code into
test/fixturesNext , So automatic completion , You can also pass in an absolute path . - Must specify
framework: true, Inform that the current path is the frame path , You can also pass in an absolute path . - app Applications need to be in before wait for ready, Otherwise, in the testcase There is no way to get part of API
- The framework needs to be used after testing
app.close()close , Otherwise, there will be leftover problems , For example, the log write file is not closed, resulting in fd Not enough .
cache
When testing multi environment scenarios, you need to use cache Parameters , because mm.app There is a cache by default , When it is loaded again after the first time, it will directly read the cache , Then the setting environment will not take effect .
const mock = require('egg-mock');
describe('/test/index.test.js', () => {
let app;
afterEach(() => app.close());
it('should test on local', () => {
mock.env('local');
app = mock.app({
baseDir: 'apps/example',
framework: true,
cache: false,
});
return app.ready();
});
it('should test on prod', () => {
mock.env('prod');
app = mock.app({
baseDir: 'apps/example',
framework: true,
cache: false,
});
return app.ready();
});
});
Multi process testing
Few scenarios use multiprocess testing , Because multiple processes cannot proceed API Grade mock Resulting in high test costs , The process starts slowly in the scenario with coverage , The test will time out . But multi process testing is the best way to verify the multi process model , You can also test stdout and stderr.
Multi process testing and mm.app Parameters are consistent , but app Of API Completely different , however SuperTest Still available .
const mock = require('egg-mock');
describe('/test/index.test.js', () => {
let app;
before(() => {
app = mock.cluster({
baseDir: 'apps/example',
framework: true,
});
return app.ready();
});
after(() => app.close());
afterEach(mock.restore);
it('should success', () => {
return app.httpRequest()
.get('/')
.expect(200);
});
});
It's OK to test more processes stdout/stderr, because mm.cluster Is based on coffee Extended , Process testing can be carried out .
const mock = require('egg-mock');
describe('/test/index.test.js', () => {
let app;
before(() => {
app = mock.cluster({
baseDir: 'apps/example',
framework: true,
});
return app.ready();
});
after(() => app.close());
it('should get `started`', () => {
// Judge the terminal output
app.expect('stdout', /started/);
});
});
unit testing
Web Unit testing in applications is more important , stay Web The period of rapid product iteration , Each test case provides a layer of guarantee for the stability of the application . API upgrade , The test case It is a good way to check whether the code is downward compatible . For all possible inputs , Once the test covers , Can clarify its output . After the code changes , Can pass The test results determine whether the code changes affect the determined results .
Try to make the modified code can be 100% cover .
Test directory structure
Appointment test The directory is the directory where all test scripts are stored , The... Used in the test fixtures And related auxiliary scripts should be placed in this directory .
The test script files are unified according to ${filename}.test.js name , Must be .test.js As a file suffix .
Unified use egg-bin To run the test script , The built-in Mocha、co-mocha、power-assert,nyc And other module combinations are introduced into the test script , Let us Focus on writing test code On , Instead of struggling to choose the tools and modules around the test .
egg-bin
mock
One app Create and launch code , The initialization script needs to be written , And you need to do some cleaning after the test run , Such as deleting temporary files , The destruction app.
There are often simulations of various network anomalies , Special situations such as service access exceptions .
Mocha Use before/after/beforeEach/afterEach To handle pre and post tasks , Basically able to handle all problems . Each example will be according to before -> beforeEach -> it -> afterEach -> after Sequential execution , And you can define multiple .
describe('egg test', () => {
before(() => console.log('order 1'));
before(() => console.log('order 2'));
after(() => console.log('order 6'));
beforeEach(() => console.log('order 3'));
afterEach(() => console.log('order 5'));
it('should worker', () => console.log('order 4'));
});
Asynchronous test
// Use return Promise The way
it('should redirect', () => {
return app.httpRequest()
.get('/')
.expect(302);
});
// Use callback The way
it('should redirect', done => {
app.httpRequest()
.get('/')
.expect(302, done);
});
// Use async
it('should redirect', async () => {
await app.httpRequest()
.get('/')
.expect(302);
});
mock CSRF
The default security plug-in of the framework will automatically open CSRF protective , If you leave completely CSRF Check logic , Then the test code needs to request a page first , Through analysis HTML Get CSRF token, Then use this token launch POST request .
therefore egg-mock Yes app Added app.mockCsrf() Method to simulate CSRF token The process of . It's working SuperTest request app Will automatically pass through CSRF check .
mock service
it('should mock fengmk1 exists', () => {
app.mockService('user', 'get', () => {
return {
name: 'fengmk1',
};
});
return app.httpRequest()
.get('/user?name=fengmk1')
.expect(200)
// Returned the user information that did not exist
.expect({
name: 'fengmk1',
});
});
return service Error Test of
it('should mock service error', () => {
app.mockServiceError('user', 'get', 'mock user service error');
return app.httpRequest()
.get('/user?name=fengmk2')
// service abnormal , Trigger 500 Respond to
.expect(500)
.expect(/mock user service error/);
});
mock HttpClient
describe('GET /httpclient', () => {
it('should mock httpclient response', () => {
app.mockHttpclient('https://eggjs.org', {
// Simulated parameters , It can be buffer / string / json,
// Will be converted into buffer
// As requested options.dataType To do the corresponding conversion
data: 'mock eggjs.org response',
});
return app.httpRequest()
.get('/httpclient')
.expect('mock eggjs.org response');
});
});
SuperTest
His motivation for using this module is to test HTTP Provide high-level abstraction , While still allowing you to download to superagent The low level of the offer API.
example:
You can use http.Server Or function passed to request() - If the server is not listening for connections , Then it will be bound to the temporary port , Therefore, there is no need to track ports .
const request = require('supertest');
const express = require('express');
const app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'john' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});
mocha Example :
describe('GET /user', function() {
it('respond with json', function(done) {
request(app)
.get('/user')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});
One thing to note about the above statement is , If you don't add a status code expect( namely .expect(302)),superagent Now any HTTP error ( except 2XX Anything other than the response code ) Send callback as the first parameter .
If you use .end() Method .expect() Failed assertions do not throw - They will return the assertion as an error to .end() Callback . To make a test case fail , You need to re throw or pass the error to done()
describe('POST /users', function() {
it('responds with json', function(done) {
request(app)
.post('/users')
.send({name: 'john'})
.set('Accept', 'application/json')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
describe('GET /users', function() {
it('responds with json', function() {
return request(app)
.get('/users')
.set('Accept', 'application/json')
.expect(200)
.then(response => {
assert(response.body.email, '[email protected]')
})
});
});
Expect to run in a defined order . Before executing the assertion , This property can be used to modify the response body or header .
describe('POST /user', function() {
it('user.name should be an case-insensitive match for "john"', function(done) {
request(app)
.post('/user')
.send('name=john') // x-www-form-urlencoded upload
.set('Accept', 'application/json')
.expect(function(res) {
res.body.id = 'some fixed id';
res.body.name = res.body.name.toLowerCase();
})
.expect(200, {
id: 'some fixed id',
name: 'john'
}, done);
});
});
You can use it. superagent Do anything , You can use it. supertest do - For example, multi part file upload !
request(app)
.post('/')
.field('name', 'my awesome avatar')
.attach('avatar', 'test/fixtures/avatar.jpg')
...
Every pass app or url Are unnecessary , If you are testing the same host , You can simply initialize app or url Reassign request variables , Every request.VERB() Call will create a new Test.
request = request('http://localhost:5555');
request.get('/').expect(200, function(err){
console.log(err);
});
request.get('/').expect('heya', function(err){
console.log(err);
});
coffee
test command line on Node.js
npm i coffee --save-dev
mm.cluster Is based on coffee Extended .
Coffee is useful for test command line in test frammework (like Mocha).
fork
- fork for spawning Node process
const coffee = require('coffee');
describe('cli', () => {
it('should fork node cli', () => {
return coffee.fork('/path/to/file.js')
.expect('stdout', '12\n')
.expect('stderr', /34/)
.expect('code', 0)
.end();
});
});
in file:
console.log(12);
console.error(34);
pass args and opts to child_process fork.( Through some parameters and optional parameters )
coffee.fork('/path/to/file.js', [ 'args' ], { execArgv: [ '--inspect' ]})
.expect('stdout', '12\n')
.expect('stderr', '34\n')
.expect('code', 0)
.end();
and more:
coffee.fork('/path/to/file.js')
// print origin stdio
.debug()
// inject a script
.beforeScript(mockScript)
// interact with prompt
.waitForPrompt()
.write('tz\n')
// string strict equals
.expect('stdout', 'abcdefg')
// regex
.expect('stdout', /^abc/)
// multiple
.expect('stdout', [ 'abcdefg', /abc/ ])
.expect('code', 0)
.end();
Spawn
coffee.spawn('cat')
.write('1')
.write('2')
.expect('stdout', '12')
.expect('code', 0)
.end();
Output normal shell Script .
Rule
code
coffee.fork('/path/to/file.js', [ 'args' ])
.expect('code', 0)
// .expect('code', 1)
.end();
stdout / stderr
coffee.fork('/path/to/file.js', [ 'args' ])
.expect('stdout', '12\n')
.expect('stderr', '34\n')
.expect('code', 0)
.end();
custom
// test/fixtures/extendable
const { Coffee, Rule } = require('coffee');
class FileRule extends Rule {
constructor(opts) {
super(opts);
// `args` is which pass to `expect(type, ...args)`, `expected` is the first args.
const { args, expected } = opts;
}
assert(actual, expected, message) {
// do sth
return super.assert(fs.existsSync(expected), true, `should exists file ${expected}`);
}
}
class MyCoffee extends Coffee {
constructor(...args) {
super(...args);
this.setRule('file', FileRule);
}
static fork(modulePath, args, opt) {
return new MyCoffee({
method: 'fork',
cmd: modulePath,
args,
opt,
});
}
}
// test/custom.test.js
const coffee = require('MyCoffee');
coffee.fork('/path/to/file.js', [ 'args' ])
.expect('file', `${root}/README.md`);
.notExpect('file', `${root}/not-exist`);
API
- coffee.spawn
- coffee.fork
- coffee.Coffee
Assertion object
- coffee.expect(type,…aegs)
coffee.spawn('echo', [ 'abcdefg' ])
.expect('stdout', 'abcdefg')
.expect('stdout', /^abc/)
.expect('stdout', [ 'abcdefg', /abc/ ])
.end();
stdout / stderr / code / error
- coffee.notExcept(type,…args)
- coffee.write(data)
Write data to stdin.
coffee.fork(path.join(fixtures, 'stdin.js'))
.write('1\n')
.write('2')
.expect('stdout', '1\n2')
.end();
- coffee.writeKey(…args)
UP / DOWN / LEFT / RIGHT / ENTER / SPACE
coffee.fork(path.join(fixtures, 'stdin.js'))
.writeKey('1', 'ENTER', '2')
.expect('stdout', '1\n2')
.end();
All parameters use key Connect
- coffee.waitForPrompt(bool)
If set false,coffee Will be output immediately , Otherwise, you will wait for the prompt message
coffee.fork('/path/to/cli', [ 'abcdefg' ])
.waitForPrompt()
.write('tz\n')
// choose the second item
.writeKey('DOWN', 'DOWN', 'ENTER');
.end(done);
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function ask(q, callback) {
process.send({ type: 'prompt' });
rl.question(q, callback);
}
ask('What\'s your name? ', answer => {
console.log(`hi, ${answer}`);
ask('How many coffee do you want? ', answer => {
console.log(`here is your ${answer} coffee`);
rl.close();
});
});
- coffee.end([callback])
The callback function will return... After executing the assertion , If an exception is thrown, the first parameter is Error
coffee.fork('path/to/cli')
.expect('stdout', 'abcdefg')
.end(done);
// recommended to left undefind and use promise style.
const { stdout, stderr, code } = await coffee.fork('path/to/cli').end();
assert(stdout.includes(abcdefg));
- coffee.debug(level)
Write data to process.stdout and process.stderr for debug
level can be
- 0 (default): pipe stdout + stderr
- 1: pipe stdout
- 2: pipe stderr
- false: disable
- coffee.coverage()
- coffee.beforeScript(scriptFile)
- coffee.Rule
Loader
- The framework introduction plug-in can pull out its own framework j be based on Egg Expand a framework ,, So the method of enabling plug-ins in a project becomes simple .
- Plug in loading priority
plug-in unit → frame → application
The plugins are loaded sequentially , The dependent party first loads
The framework loads... In the order of inheritance , The lower the layer, the first to load
版权声明
本文为[process1212]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230519445717.html
边栏推荐
- CORS and proxy (づ  ̄ 3  ̄) in egg ~ the process of stepping on the pit and filling the pit ~ tot~
- 源码剖析Redis中如何使用跳表的
- Summary of MySQL knowledge points
- The 2021 IT industry project management survey report was released!
- Low code and no code considerations
- Asynchronous iterator & asynchronous generator & asynchronous context manager
- MFC implementation resources are implemented separately by DLL
- Kanban Quick Start Guide
- 双击.jar包无法运行解决方法
- 无线网怎么用手机验证码登录解决方案
猜你喜欢

Three 之 three.js (webgl)旋转属性函数的简单整理,以及基于此实现绕轴旋转的简单案例

Three of three JS (webgl) simple sorting of rotation attribute function, and a simple case of rotating around the axis based on this

What role do tools play in digital transformation?

2021-10-08

When is it appropriate for automated testing? (bottom)

Low code and no code considerations

CPT 104_ TTL 09

青岛敏捷之旅,来了!

如何在Word中添加漂亮的代码块 | 很全的方法整理和比较

MySQL external connection, internal connection, self connection, natural connection, cross connection
随机推荐
C test calls the paddlesharp module to recognize pictures and words
即将毕业的大学生找技术开发工作的焦虑根源
Excel 2016 打开文件第一次打不开,有时空白,有时很慢要打开第二次才行
Anti crawler (0): are you still climbing naked with selenium? You're being watched! Crack webdriver anti crawler
2021-09-27
WTL self drawn control library (cqscheckcomboxbox)
The introduction of lean management needs to achieve these nine points in advance
PIP free export with path (@ file: / / /) notes
2021-11-01
Kanban Quick Start Guide
egg中的多进程模型--egg文档搬运工
4 个最常见的自动化测试挑战及应对措施
Multi process model in egg -- egg document Porter
Tensorflow realizes web face login system
Minimum spanning tree -- unblocked project hdu1863
双击.jar包无法运行解决方法
How to add beautiful code blocks in word | a very complete method to sort out and compare
学习笔记:Unity CustomSRP-13-ColorGrading
Mairadb数据库基本操作之数据管理
Three 之 three.js (webgl)简单实现根据点绘制线/弧线(基于LineGeometry / Line2 / LineMaterial,绘制两点基于圆心的弧线段)