With the advent of Node.js, which was intended to solve some of the problems of the back end, it unwittingly brought a disruptive revolution to the front end and opened the door to modern development. Today, As a front-end development, Node.js is already a front-end must be mastered whether you want to advance or broaden the boundaries of your knowledge. What are you hesitating to take down Node.js?
Koa.js is a development framework based on Node.js. It is small and flexible, and is relatively friendly to some small and medium-sized projects. Koa is easy to use, so it has become one of the choices for many partners to use node development. This article mainly explains the core part of KOA back-end development from the following aspects, so that everyone is a small full stack expert:
- Install Koa2 with a Hello World service
- Interface development for Koa
- What is a REST API?
- Develop REST-style apis at Koa
- Use PostMan to test our interface
- Installing the MongoDb Database
- Use of MongoDb visualization tools
- Use Mongoose to operate MongoDb database
- Encrypt and decrypt the front-end submitted password
- Use JWT for login authentication in Koa
- Data submitted by users is filtered to prevent XSS attacks
- Use Mocha for synchronous, asynchronous, and interface unit testing
- Unit test coverage
Install Koa2 with a Hello World service
To develop the Node backend, you need to install the Node.js environment and NPM package management tool. First create a file folder as our project directory:
/ / terminal. Create koa-test and go to that directory
mkdir koa-test
cd koa-test
Copy the code
Next, install the KOA library like we normally do for front-end projects:
// Terminal: install KOA2
cnpm i koa -S
Copy the code
Create app.js in the root directory as the entry for our program, just like main.js in our vue project:
// Introduce the Koa library and initialize it
const Koa = require('koa');
const app = new Koa();
// Start the service
// Listen on port 3000, as vue defaults to port 8080
// You can also listen to other ports
const appService = app.listen(3000, () = > {console.log('[Koa]Server is starting at localhost:3000');
});
// Export service (for single test use)
// If you do not export, you can still run the project
module.exports = appService;
Copy the code
Finally, start our program service at the terminal:
// Node is the node.js command
node app.js
Copy the code
Localhost :3000 in the browser, you can see the service we have started:
Koa Routing (interface writing)
About the interface, I believe everyone is not unfamiliar. As a front end development, I have to deal with the interface provided by the back end people every day. Now, let’s look at how to develop interfaces for front-end use in Koa developed services.
- Interface development, in fact, is to write routes in Koa, need to use
koa-router
This library, like the vue-router in the front-end VUE development, is also a front-end route:
// Install the routing library
// -s is short for --save, indicating use in the build environment
// -d is short for --save-dev, which means to use in a development environment
cnpm i koa-router -S
// For the front end parameters, we need to get the use of
// Get submits parameters that we can easily obtain,
// We need to parse the post data before we can use it
// The koA-BodyParser library needs to be installed to process the post data
// Terminal execution:
cnpm i koa-bodyparser -S
Copy the code
- Mount the KOa-BodyParser and API routes
For standards, we need to keep the API-related content separate. For example, in our VUE project development, there are components, Pages, API, assets and so on under SRC. Here we create the API folder in the root directory to store our routing files, as shown in the figure below:
Koa-test/API /index.js is the exit of our API module. The modules file is used to store all API modules. For example, there are user interfaces in user.js. The details will be discussed later.
Let’s look at how to mount routes and other middleware in app.js:
// koa-test/app.js
// Introduce koa-bodyParser for parsing post data
const bodyParser = require('koa-bodyparser');
// Import API routes from the root directory
// Import routes exposed by koa-test/ API /index.js
const router = require('./api')
// Mount koa-BodyParser in app.js
// Note: Mount koa-bodyParser before the route is mounted
app.use(bodyParser());
// Mount the route
// After the service starts, you can enter localhost:3000 in the browser and see the prompt
app.use(async ctx => ctx.body = 'Service started successfully');
app.use(router.routes());
app.use(router.allowedMethods());
// Omit the other code above
Copy the code
- Write index.js for the API
Koa / / in the router
const Router = require('koa-router');
// Import the routing module from the Modules folder
const articleRouter = require('./modules/articles');
// Instantiate Router middleware
const router = new Router();
// Register the route
// Note that the route module file was registered with the prefix '/articles
// All interface addresses under this module are prefixed with /articles
router.use('/articles', articleRouter.routes(), articleRouter.allowedMethods())
// Export the registered route
// for the koA mount in app.js
module.exports = router;
Copy the code
- Write specific routing files, eg:
articles.js
File:
// You need to import the KOA-Router first
const Router = require('koa-router');
// Instantiate the router
const router = new Router();
// Register the get method
// You can use ctx.query to obtain the parameters after parse
// Or get the serialized parameters from ctx.queryString
router.get('/list', (ctx, next) => {
ctx.body = {
code: 200.data: [{id: 1.name: 'Ming'.sex: 0.age: 22}].message: 'ok'
};
});
// Register the POST method
// After mounting the koA-Bodyparse middleware in app.js,
// The post parameter can be obtained through ctx.request.body
// eg: the data is the data submitted by the front end during post
router.post('/update', (ctx, next) => {
let data = ctx.request.body
ctx.body = {
code: 200,
data,
message: 'ok'
};
});
// Expose the route (API interface) of the module
// for API /index.js route registration
module.exports = router;
Copy the code
Ctx. body, in this case, is the JSON data returned to the front end.
So much for the basic routing, of course, in real business development there will be interfaces for the PUT, DELETE types, and so on. The basic writing method is similar, here attached koA-Router official website document address, see more routing details.
What is a REST API?
To quote the definition on the Internet:
REST refers to a set of architectural constraints and principles. An application or design that meets these constraints and principles is RESTful
Here’s how to define a REST-style API:
// Get the operation using get:
// For example: get all articles
get /api/articles
// Retrieve articles with search criteria (e.g. number of pages, items per page, article type, etc.)get /api/articles? page=1=pageSize=50&type=1
// Obtain a single article with id 12345
get /api/articles/12345
// Resource classification,
// select * from post where id = 12345
get /api/articles/12345/comments
// Retrieve comments with search criteria for the article with id 12345
get /api/articles/12345/comments? page=1&pageSize=50
// Submit data using the POST type:
// Create an article
post /api/articles
// Update data using the put type:
// For example: update the content of the post with ID 12345
put /api/articles/12345
// Delete the article whose ID is 12345
delete /api/articles/12345
Copy the code
REST style API writing can refer to liao Xuefeng’s big REST API writing article
Develop REST-style apis at Koa
It is also easy to develop reST-style apis in Koa. The KOA-Router provides params objects for our CTX objects to get the parameters in the REST-style API. Does it look like the dynamic route in vue-router?
router.get('/user/:userId'.async ctx => {
// Get the parameters of the dynamic route
// Obtain from the ctx.params object provided by the KOA-Router
const id = ctx.params.userId
// Omit the other code
}
Copy the code
Use PostMan to test our interface
In the process of interface development, we definitely need to test whether the interface we wrote is correct and does not return results as expected. So how do we access our interface to see if it’s correct? You can install postman and use it as follows:
In this way, we can create an interface to access the interface service we wrote, and can also carry various parameters, debugging is very convenient, this is not to say, online search Postman download can be, free open source.
Installing the MongoDb Database
OK, the above said finished writing the interface, the corresponding must need us to operate the database, and then return data to the front end, such as basic add, delete, change and check ah. Here’s a look at the basic mongodb data installation, with Mac OS as the column:
You can install mongo with curl on the terminal, and you can install mongo with curl on the terminal.
- Install mongoDb
# enter/usr/local
cd /usr/local
# downloadSudo curl - O https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.9.tgz# decompressionSudo tar - ZXVF mongo osx - SSL - x86_64-4.0.9. TGZ# rename mongodb directorySudo mv mongodb-osx-x86_64-4.0.9/ mongodb // Add environment variablesexport PATH=/usr/local/mongodb/bin:$PATH// Create a database storage directory sudo mkdir -p /data/db // Start mongod sudo mongodCopy the code
The mongodb installation process is simple, so I won’t go into detail. For more details, see the mongodb Installation reference
Use of MongoDb visualization tools
The visualization tool of mongodb, which I recommend here is Studio 3T, can easily connect to the database, view the contents of the database, or operate the database, etc.
Finally here attached Robo3 download installation address, installation is very simple, just like a QQ, not to repeat.
Mongoose operates the MongoDb database
In Node, we basically use Mongoose to connect to and manipulate the database. First, we need to install mongoose:
/ / install the mongoose
cnpm i mongoose -S
Copy the code
Next, create a new databse folder at the root of the file to specifically place the files related to the connection and manipulation data:
In database/index.js, we write the method to connect to the database, and then export it for app.js connection:
// Introduce the Mongoose library
const mongoose = require('mongoose');
// Define a constant for the database address
// Create a new data profile,
// Used to store data related configuration, such as account password, etc
const DB_ADDRESS = 'mongodb://localhost/koa-test';
mongoose.Promise = global.Promise;
mongoose.set('useCreateIndex'.true);
// Encapsulate the log
const log = console.log.bind(console);
// Define the connection function
const connect = (a)= > {
// Number of reconnections
let connectTimes = 0;
// Set the maximum number of reconnections
const MAX_CONNECT_TIMES = 3;
// Re-connect
const reconnectDB = (resolve, reject) = > {
if (connectTimes < MAX_CONNECT_TIMES) {
connectTimes++;
mongoose.connect(DB_ADDRESS, connectConfig);
} else {
log('[mongodb] database connect fail! '); reject(); }}// Connect to the database
mongoose.connect(DB_ADDRESS, connectConfig);
return new Promise((resolve, reject) = > {
// Listen to the database disconnect, reconnect
mongoose.connection.on('disconnected', () => {
reconnectDB(reject);
});
// Failed to listen to the database connection, reconnect
mongoose.connection.on('error', err => {
log(err);
reconnectDB(reject);
});
// Listen for connection success
mongoose.connection.on('open', () => {
log('[mongodb server] database connect success! ');
resolve();
});
});
};
// Expose
exports.connect = connect;
// You also need to introduce a schema, as shown below
/ /...
Copy the code
The main function here is:
(1) Connect to the database through mongoose.connect(); (2) Listen for disconnected and error events and reconnect the database up to three times; (3) Return a promise to indicate whether the connection was successfulCopy the code
- Introduce all schemas
As you can see from the previous screenshot of the folder, we created the Schema folder to store all the database modeling related content, in fact, to operate the database through the Schema. Let’s see how to import all the files in our schema (although it is possible to import them one by one, but we are eager programmers ~ ~) :
Js: in databse/index.
/ / into the glob
const glob = require('glob');
// Introduce the path method
// Can read, parse, concatenate paths, and so on
const path = require('path');
// Expose an initSchemas method
// Used to import all schemas in the database/ Schema folder
exports.initSchemas = (a)= > {
// Read the schema folder from glob
glob.sync(path.resolve(__dirname, './schema/'.'**/*.js')).forEach(require);
}
Copy the code
If you don’t know how to use glob, here is the glob document address.
More mongoose content can see mongoose Chinese document address;
- The path module uses the following methods:
Path is a module provided by node that deals with the path:
// Before using path, you still need to import first
const path = require('path');
// The join method can join all parameters and return a path
path.join()
/ / eg:
path.join('a'.'b'.'c'.'d'); // a/b/c/d
path.join(__dirname, '/a'.'//b'.'///c'.'d'); // /Users/yoreirei/Documents/demo/node-demo/a/b/c/d
path.join(__dirname, 'a'.'b'.'.. /c'.'d'); // /Users/yoreirei/Documents/demo/node-demo/a/c/d
path.join(__dirname, 'a'.'./b'.'./c'.'./d'); // /Users/yoreirei/Documents/demo/node-demo/a/b/c/d
// The parse method resolves the path into a path object
path.parse()
/ / eg:
path.parse(path1) // { root: '', dir: 'a/b/c', base: 'd', ext: '', name: 'd' }
path.parse(path2) // { root: '/', dir: '/Users/yoreirei/Documents/demo/node-demo/a/b/c', base: 'd', ext: '', name: 'd' }
// The format method converts the path object to the path address
path.format(parse1) // a/b/c/d
Copy the code
Note: __dirname is the absolute path of the current file module. This front end friend should be familiar from the entry in vue-CLI.
OK, let’s go ahead and do schema modeling.
- Schema modeling, using Schema to operate the database
// database/schema/User.js
/ / into the mongoose
const mongoose = require('mongoose');
// Get the mongoose.Schema method for modeling
const { Schema } = mongoose;
/ / generated id
let ObjectId = Schema.Types.ObjectId;
// Create a schema for the user
// For example, create a user name, password, creation time,
// The schema of the last login time, liked content and favorite content
const userSchema = new Schema({
UserId: ObjectId,
// We can define the type of each field, such as String, Number, Array, and so on
// You can define whether the value of this field is unique. If unique is set,
// If the same value is inserted later, an error will be reported
userName: {
unique: true.type: String
},
password: String.likes: {
type: Array.default: []},collect: {
type: Array.default: []}}, {// If this configuration item is added, the creation time is automatically generated
// When the document is updated, the time is also automatically updated
timestamps: {
createdAt: 'createdAt'.updatedAt: 'updatedAt'}});// Finally, use mongoose to publish the model
mongoose.model('User', userSchema);
Copy the code
Schema modeling is as simple as that, and you can extend it to create other schemas. This operation is similar to creating a table in other data.
Encrypt and decrypt the front-end submitted password
Basically our service will involve user registration and login and so on. As for the password registered by the user, we will not save it in plain text, which is not safe. Generally, the plaintext password is encrypted and stored, and then the encrypted password is compared with the encrypted password in the database to check whether the password is correct. In the front end, common methods such as MD5 encryption can also be submitted when the user submits.
For encryption, you can use Bcript to encrypt and decrypt passwords.
- encryption
We need to encrypt the user’s password at the time of registration to make it irreversible. First, we need to install the Bcript library:
/ / bcript installation
cnpm i bcript -S
Copy the code
database/schema/User.js
/ / introduce bcript
const bcrypt = require('bcrypt');
// Define the bCRIP encryption configuration constant
const SALT_ROUNDS = 10;
// Encrypt the password each time you save it
// Note that the second argument of the pre cannot be an arrow function, otherwise you will not get this
userSchema.pre('save'.function(next) {
bcrypt.genSalt(SALT_ROUNDS, (err, salt) => {
if (err) return next(err);
bcrypt.hash(this.password, salt, (err, hash) => {
if (err) return next(err);
// Replace the user-submitted password with the encrypted hash
this.password = hash;
next();
});
});
});
Copy the code
Note: In our encryption practice, we listen for the save event while the user is modeling, that is, every time the user stores data, we execute the callback we define, and we do the encryption in the callback function.
- decryption
To authenticate a user’s password for login, we need to get the user’s password and verify with Bcript that it is the same as the encrypted data.
The database/schema/User. Js:
// Define the instance method of userSchema
// Decrypt the user password
// Add s to mehtod
userSchema.methods = {
// Define a method to compare the correct password
// userPassword Indicates the password submitted by the user
// passwordHash Specifies the encrypted password found in the database
comparePassword (userPassword, passwordHash) {
return new Promise((resolve, reject) = > {
bcrypt.compare(userPassword, passwordHash, (err, res) => {
// Validation is complete
/ / res value to false | true, said different/the same password
if(! err)return resolve(res);
// Validation error
returnreject(err); }); }); }}Copy the code
Note: What we’re doing here is adding an instance method to the schema, so we (for example, after writing the login interface, then the user’s password) call the instance of the schema to check if the password is correct.
- A brief demonstration of the Login interface (without parameter validation and JWT)
/** * user login * @param {String} userName userName * @param {String} password password */
router.post('/login'.async ctx => {
// Premise to introduce mongoose
// Get the User collection (a table similar to other data)
const userModal = mongoose.model('User');
// An instance of the collection
const userInstance = new userModal();
// Define query parameters
const query = { userName: data.userName };
// First check whether the user exists
await userModal.findOne(query).exec()
.then(async res => {
// User exists, get user data
// Call the instance method of the collection to verify that the password is correct
// The then callback indicates that the validation operation is complete
// Verify the correctness by returning the parameter isMatch (true/false)
await userInstance.comparePassword(data.password, res.password).then((isMatch) = > {
// Verify that the password is correct
if (isMatch) {
// Token generation is omitted, which will be explained later
/ / * * * * *
return ctx.body = {
code: 200.message: 'ok'
};
}
return ctx.body = {
code: 400.message: 'Account password error'
};
}).catch((a)= > {
return ctx.body = {
code: 500.message: error.message
};
})
// The user does not exist
}).catch((a)= > {
return ctx.body = {
code: 400.data: null.message: 'Current user does not exist'
};
});
});
Copy the code
For information about Bcript, see the NPM document address of Bcript
In fact, as for login, we need to do login authentication, such as whether expired or not, and more complex will also have Redis persistence and so on. JWT login authentication has been omitted, as described below.
Use JWT for login authentication in Koa
JWT is a common user login authentication mode:
(1) The front end obtains the token through the login interface and saves it locally. The front end will carry the token in the request header when adding, deleting, modifying and checking.
(2) Based on the authorization (that is, user tokens) carried in the request, the back-end will determine whether the user login has expired (unified interception), and return 401 when the login expires, or determine whether the current user has the permission to perform the operation.
To use JWT in KOA2, there are two middleware to mention:
(1) JsonWebToken generates and parses tokens
(2) KOA-JWT intercepts (all/part) user requests and verifies tokens
- To generate the token
First install jsonWebToken:
/ / jsonwebtoken installation
cnpm i jsonwebtoken -S
Copy the code
The API/modules/user. Js
const { createToken } = require('.. /.. /utils/account');
// According to the above login interface, after the user account and password are queried correctly
// createToken to return to the front end, createToken method to look back
const token = createToken(res)
return ctx.body = {
code: 200.data: token,
message: 'ok'
};
Copy the code
Create a new utils folder in your root directory,
utils/account.js
/ / introduce jsonwebtoken
const JWT = require('jsonwebtoken');
// Customizes the key to generate the token (arbitrary string)
// In terms of security, it cannot be exposed to the front end, otherwise you can get the token at will
const JWT_SECRET = 'system-user-token';
// Generate JWT Token
// You can also set the expiration time
exports.createToken = (config = {}, expiresIn = '7 days') = > {
const { userName, _id } = config;
const options = { userName, _id };
const custom = { expiresIn };
// The token is generated by configuring parameters and calling the jwt. sign method
return JWT.sign(options, JWT_SECRET, custom);
};
// Expose the key
// The key is exposed for later verification
// For the sake of uniformity, you don't need to write the string 'system-user-token' everywhere
exports.JWT_SECRET = JWT_SECRET;
Copy the code
- Request intercept validation token
Now that the login interface generates the token problem, we still need to intercept the user request and verify that it has expired. Next, of course, is koA-JWT:
First install:
/ / install koa - JWT
cnpm i koa-jwt -S
Copy the code
App.js performs unified interception and sets interfaces that do not require tokens (such as login and registration interfaces) :
/ / into the JWT
const jwt = require('koa-jwt');
// Get our key string
const { JWT_SECRET } = require('./utils/account');
// JWT validation error handling
// JWT returns a 401 status code for a route that fails authentication
// We intercept the error through KOA and return a message with no permission for status code 401
// Note: Need to be placed before JWT middleware mount
app.use(function(ctx, next){
return next().catch((err) = > {
if (401 == err.status) {
ctx.status = 401;
ctx.body = {
code: 401.message: 'No permissions yet'
};
} else {
throwerr; }}); });// Mount JWT middleware
// The secret parameter is the key used for authentication
// unless method to set the interface that does not require the token
app.use(
jwt({ secret: JWT_SECRET }).unless({
path: [
'/login'.'/register']}));Copy the code
In fact, KOA-JWT encapsulates two middleware, KOA-Less and JsonWebToken.
The purpose of KOA-less is to execute the previous middleware only if the KOA-less parameters do not match. Jsonwebtoken is the one we used above to generate and parse tokens.
Look at this, do you want to say:
- Parsing the token
In many cases we need to get the token carried by the front end, get the user information from the token, and then do something else. For example, after receiving the token of a user, you can parse out the id of the user based on the token, query the existence of the user based on the ID, and perform some operations.
In the API /article.js interface file, we read the user information according to the authorization in the header when we get the data submitted by the user:
// Omitted part of the code
const Router = require('koa-router');
const mongoose = require('mongoose');
// First import the two methods we encapsulated
const { decodeToken, parseAuth } = require('.. /.. /utils/account');
// Define the post publishing interface
router.post('/article'.async ctx => {
let data = ctx.request.body;
// Omit parameter validation
/ / * * * *
// Parse the user token
const authorization = parseAuth(ctx);
// The user id in the token is resolved based on the token
const tokenDecoded = decodeToken(authorization);
const { _id } = tokenDecoded;
const userModal = mongoose.model('User');
// Check whether the user exists and get the user information
await userModal.findById(_id).exec()
// Create an article after the user finds it
.then(async res => {
data.author = res.userName
const articleModal = mongoose.model('Article');
const newArticle = articleModal(data);
// Store the article data
await newArticle.save()
.then((a)= > {
return ctx.body = {
code: 200.message: 'Saved successfully'
};
}).catch(error= > {
return ctx.body = {
code: 500.message: error.message || 'Save failed'
};
});
})
.catch(err= > {
return ctx.body = {
code: 500.message: err.message || 'User does not exist'}})});module.exports = router;
Copy the code
utils/account.js:
const JWT = require('jsonwebtoken');
// Parse authorization from CTX
exports.parseAuth = ctx= > {
if(! ctx || ! ctx.header.authorization)return null;
const parts = ctx.header.authorization.split(' ');
if (parts.length < 2) return null;
return parts[1];
}
// Parse the JWT Token
exports.decodeToken = (token) = > {
return JWT.decode(token);
};
Copy the code
Note: (1) The main idea is that we parse out the user’s _ID through the authorization contained in the header requested by the user, and then use the _ID to find out the corresponding information of the user in the database (if the project is large, the caching technology such as Redis will be used to read the user’s information. Instead of working directly with the database every time) and then creating the article.
(2) It is very simple to parse authorization, even if you directly call the decode method of jsonWebToken library, which is explained in the official website.
(3) As for how to get authorization, it should be explained that what we get is the following data, which actually contains two parts, so we need to parse out the content we want (that is, the content after the space) :
The parsing method is also very simple, according to the space partition, take the last part.
Filter user data to prevent XSS attacks
malicious code is more likely to be passed if the user submits the data without filtering.
In this case, we can filter the user’s content and translate some key characters.
// Install the XSS library
cnpm i xss -S
// Use, which is processed in the schema where we store the article data
// Filter each time it is stored
// schema/Article.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const xss = require('xss');
const articleSchema = new Schema({
// Omit part of the content
});
// Encrypt the password each time you save it
// Note that the second argument of the pre cannot be an arrow function, otherwise you will not get this
articleSchema.pre('save'.function(next) {
// XSS filters the title and content
this.title = xss(this.title);
this.content = xss(this.content);
next();
});
mongoose.model('Article', articleSchema);
Copy the code
As shown in the figure below, you can see the comparison of database data before and after filtering:
More information about XSS can be found in the XSS Chinese documentation
Note: If it is a relational database such as mySql, we will also handle the operation of preventing SQL injection, which can be studied by interested partners.
Use Mocha for synchronous, asynchronous, and interface unit testing
The common styles of unit testing are behavior driven development (BDD) and test driven development (TDD). So what’s the difference?
(1) BDD focuses on whether the final implementation of the whole system is consistent with user expectations.
(2) TDD focuses on getting quick feedback so that all features are available.
- Use Mocha for unit testing
First install:
/ / installation
cnpm i mocha -D
Copy the code
Configure the NPM test command:
// Configure the NPM script command
"scripts": {
"test": "mocha"
}
Copy the code
Create text/test.js and write the test code:
// Introduce node's assertion library
// It's like a tool library
const assert = require('assert');
// Import the files to be tested
const lib = require('./index');
// Test the iterate function
// Describe defines the description of tests
// It defines what is printed
describe('Math', () => {
describe('#iterate', () => {
it('should return 10', () = > {// Pass the test with an assertion
assert.equal('10', lib.iterate(5.5));
});
it('should return 0', () => {
assert.equal('0', lib.iterate());
});
it('should return 10', () => {
assert.equal('10', lib.iterate(1.1));
});
});
});
// test/index.js
const iterate = (. arg) = > {
if(! arg.length)return 0;
return arg.reduce((val, cur) = > val + cur);
};
module.exports = {
iterate
}
// Terminal run
// All test files in the test folder are automatically found and the tests are executed
npm test
Copy the code
It can be seen that the test fails when the results do not meet expectations:
More information can be found in the Mocha documentation address
- Use the SHOULD assertion library
For the above assertion library, we use the Asset assertion module provided by Node. In fact, I have many other options, such as should.js, chai, etc., because these libraries provide a much richer supply than asset.
Here are some examples of the should assertion library:
Should / / installation
cnpm i should -D
/ / introduction
const should = require('should');
// Use (similar to node above)
require('should');
const lib = require('./index');
/** * unit tests */
describe('Math', () => {
describe('#iterate', () => {
it('should return 10', () => {
lib.iterate(5.5).should.be.equal(10);
});
it('should return 0', () => {
lib.iterate().should.be.equal(0);
});
it('should return 10', () => {
lib.iterate(1.1).should.be.equal(10);
});
});
});
Copy the code
The should assertion library is richer than Node’s assert assertion capabilities. The effect of the assertion is as follows:
For more information, see the Shuold document address
By far, chai is actually more popular, offering both TDD and BDD styles. Interested partners can browse through its documents to learn to view, the basic usage is also the same.
- Use Mocha asynchronous testing
All of these are synchronous tests, so what does Mocha do with asynchronous tests? It’s as simple as manually calling a callback function:
// The file to be tested
// Mock defines an ordinary asynchronous function
const asyncFunc = (cb) = > {
setTimeout((a)= > {
console.log('async init after 1000')
cb()
}, 1000)}// Test the code
// PS: omitted import/export operation
// Test normal asynchronous functions
describe('Async', () => {
describe('#asyncFunc', () => {
it('should an async function', done => {
// eg: the important thing is that after the asynchronous call is complete,
// You need to call the done callback manually,
// to tell Mocha that the asynchronous call is complete
asyncTest.asyncFunc(done)
})
})
});
Copy the code
The effect is as follows:
- For cases where asynchronous invocation is required
// async Tests asynchronous functions
// Simulate an asynchronous function:
const getInfo = async (bool) => {
return new Promise((resolve, reject) = > {
setTimeout((a)= > {
// Return success if the argument is true
// Otherwise, failure is returned
if (bool) return resolve('success');
return reject('fail');
}, 1000);
});
};
/ / test
describe('Async', () => {
describe('#getInfo', () => {
it('should return success', done => {
// The callback function needs to be called manually
// The async function needs to be written internally
(async function () {
try {
// Wait for asynchrony to complete, then call done() manually
await asyncTest.getInfo(true);
done();
} catch (error) {
done(error)
}
})();
});
});
})
Copy the code
The effect is as follows:
If await asyncTest. GetInfo (true); Pass false to simulate the effect of a failed test. You should see something like this:
- More simply, we can write the second argument of the IT callback as an asynchronous function:
// The effect is the same
describe('#getInfo', () => {
it('should return success'.async() = > {await asyncTest.getInfo(false);
});
});
Copy the code
- Unit testing of the interface
The first step is to install the supertest library
/ / installation
cnpm i supertest -D
Copy the code
Test Content:
// Import our service
// Export the started service in the app.js file
//eg: moudle.exports = app.listen(3000)
const app = require('.. /app');
// Import the supertest for the test of the interface
const request = require('supertest');
describe('GET /', () => {
it('should return status with 200', (done) => {
/ / test
// Get is a test get request
// Expect is what is expected
request(app)
.get('/')
.expect(200)
.end((err, res) = > {
// Get the contents of the interface in end
// Then call done manually according to the situation
if (err) return done(err);
done();
});
});
});
Copy the code
The test result is passed, as shown in the figure below:
// The article details interface requires authorization
// Our test case wanted to return a 200 status code, but returned a 401
// So the current test fails
describe('GET /artiles/:id', () => {
it('should an article info', (done) => {
request(app)
.get('/api/articles/5d2edc370fddf68b438b6b53')
.expect(200, done); })})Copy the code
See the supertest document address for more information
Unit test coverage
As for unit test coverage, a quick mention is that we can test how well our test code is covered.
- The installation
// First install the Istanbul depot
cnpm i istanbul -D
Copy the code
- use
// In the test folder, open the terminal and run the following command
// tests the code test coverage of the index.js file
istanbul cover index.js
Copy the code
As shown in the figure, we can see that 8 blocks of code cover 3, 2 branches but none of them cover, 0 functions, 6 lines of code cover 1, and what the corresponding coverage is.
At the same time, in the test folder, you can see the generated Coverage folder, which saves the coverage results, you can click index.html to view the results.
Index.html is more intuitive:
See the Istanbul documentation for more information.
Ok, that’s all about Koa2 and mongodb. The second phase is tentatively planning to expand the following content about Koa:
- Extension: Koa install mySql database
- Extension: Koa mySql basic operation (add, delete, modify), make curl
- Extension: Visual tool for mySql database
reference
- Koa Official Website
- REST API Specification by Xuefeng Liao
- Challenge full stack Koa2 free video tutorial (total 13 episodes) author: Technology fat
- “Node.js development practice” author: suddenly such as send
Sample code synchronization on Github, welcome to visit!
I am leng hammer, welcome to exchange and share