• 记npm包开发全过程


    概述

    • 为什么开发npm包?
    • 如何开发?
    • 如何写单元测试?
    • package.json
    • 如何发布模块?
    • 如何使用?

    为什么开发npm模块?

    NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。
    npm官网

    如何开发?

    接下来由带领大家完成一个简单的npm包,功能:读写文件
    是不是很复杂呢???

    npm init

    初始化

    创建基础目录或文件

    • mkdir tests lib
    • touch index.js README.md
    .
    ├── README.md //说明文档
    ├── index.js //主入口
    ├── lib //功能文件
    ├── package.json //包信息
    └── tests //测试用例
    

    开发功能

    • cd lib/
    • touch file.js utils.js

    utils.js文件内容

    var Promise = require("es6-promise").Promise;
    
    /**
     * 提供各种工具方法
     * @type {{*}}
     */
    module.exports = {
        /**
         * 获取Defer对象
         * @return {[type]} [description]
         */
        getDefer: function (){
            var deferred = {};
            deferred.promise = new Promise(function(resolve, reject){
                deferred.resolve = resolve;
                deferred.reject = reject;
            });
            return deferred;
        },
        /**
         * promise when方法
         * @param promises promise数组
         * @returns {[type]} [description]
         */
        when: function(promises) {
            var deffered = this.getDefer();
            Promise.all(promises).then(function(data) {
                deffered.resolve(data);
            }, function(err) {
                deffered.reject(err);
            });
            return deffered.promise;
        }
    }
    

    注:npm install es6-promise --save --verbos

    file.js内容

    var fs = require('fs');
    var path = require('path');
    var utils = require('./utils.js');
    
    module.exports = {
    	/**
    	 * 写文件
    	 * @param file 文件路径
    	 * @param data 数据
    	 */
    	writeFile: function(file, data) {
    		var deferred = utils.getDefer();
    		file = path.resolve(file);
    
    		fs.writeFile(file, data, 'utf-8', function(err) {
    			if(err){
    				deferred.reject(err);
    			}else {
    				deferred.resolve(true);
    			}
    		});
    		return deferred.promise;
    	},
    	/**
    	 * 读文件
    	 * @param file 文件路径
    	 */
    	readFile: function(file) {
    		var deferred = utils.getDefer();
    		file = path.resolve(file);
    
    		fs.readFile(file, 'utf-8', function(err, data) {
    			if(err){
    				deferred.reject(err);
    			}else {
    				deferred.resolve(data);
    			}
    		});
    		return deferred.promise;
    	}
    };
    

    主函数index.js内容

    var file = require('./lib/file.js');
    
    module.exports = {
    	writeFile: file.writeFile,
    	readFile: file.readFile
    }
    

    注:主要是在被使用时暴露的接口

    功能开发基本完毕

    此时我们并不知道我们实现的功能是否可行、可用
    我们需要进行单元测试

    如何写单元测试?

    单元测试原则

    • 对全新的代码或修改过的代码进行单元测试
    • 单元测试根据单元测试计划和方案进行,排除测试的随意性
    • 必须保证单元测试计划、单元测试方案、单元测试用例等经过评婶
    • 当测试用例的测试结果与预期结果不一致时,单元测试的执行人员需如实记录实际的测试结果
    • 只有当测试计划中的结束标准达到时,单元测试才能结束
    • 对被测试单元需达到的一定的代码覆盖率要求

    书写测试用例

    前提条件
    测试库mocha  教程

    npm install -g mocha --verbos
    npm install --save-dev chai --verbos
    

    测试utils.js

    var utils = require('../lib/utils.js');
    var expect = require('chai').expect;
    
    describe('utils:工具方法测试', function() {
        //defer对象测试
        describe('utils.getDefer', function() {
            var deferred = utils.getDefer();
    
            it('defer成功', function() {
                deferred.resolve(true);
                return deferred.promise.then(function(data) {
                    expect(data).to.be.equal(true);
                });
            });
    
            it('defer失败', function() {
                deferred.reject(true);
                return deferred.promise.then(function() {}, function(data) {
                    expect(data).to.be.equal(true);
                });
            });
        });
        //when测试
        describe('utils.when', function() {
            var deferred1 = utils.getDefer();
            var deferred2 = utils.getDefer();
            var deferred3 = utils.getDefer();
            var deferred4 = utils.getDefer();
    
            it('when成功', function() {
                deferred1.resolve(true);
                deferred2.resolve(true);
                return utils.when([deferred1.promise, deferred2.promise]).then(function(data) {
                    expect(data).to.be.deep.equal([true, true]);
                });
            });
    
            it('when失败', function() {
                deferred3.resolve(true);
                deferred4.reject(false);
                return utils.when([deferred3.promise, deferred4.promise]).then(function() {},function(data) {
                    expect(data).to.be.equal(false);
                });
            });
        });
    });
    

    注:mocha tests/utils.js

    测试file.js

    var file = require('../lib/file.js');
    var expect = require('chai').expect;
    
    describe('file:功能测试', function() {
    
        //writeFile功能测试
        describe('file.writeFile', function() {
    
            it('写文件:成功', function() {
                var path = 'README.md';
                var data = '说明文档';
                return file.writeFile(path, data).then(function(flag) {
                    expect(flag).to.be.equal(true);
                });
            });
    
            it('写文件:失败', function() {
                var path = 'write-test.txt';
                var data = '我是写入的数据';
                return file.writeFile(path, data).then(function(){}, function(err) {
                    expect(true).to.be.equal(true);
                });
            });
        });
    
        //readFile功能测试
        describe('file.readFile功能测试', function() {
    
            it('读文件:成功', function() {
                var path = 'README.md';
                return file.readFile(path).then(function(data) {
                    expect(data).to.be.equal('说明文档');
                });
            });
    
            it('读文件:失败', function() {
                var path = 'write-test.txt';
                return file.readFile(path).then(function(){}, function(err) {
                    expect(true).to.be.equal(true);
                });
            });
        });
    });
    
    

    注:mocha tests/file.js

    持续测试...

    一遍遍测试,查看测试结果,都要输入命令很无语~~
    新技能:mocha --watch tests

    还能做什么?

    通过上面测试感觉好了不少,有没有更好的展现形式呢?

    • 测试结果生成html报表(mocha --reporter mochawesome tests)
    • 能不能看看测试覆盖率呢?(istanbul cover ./node_modules/mocha/bin/_mocha -- -t 2000 --recursive -R spec tests/)

    注:
    npm install --save-dev mochawesome --verbos
    npm install --save-dev mocha --verbos
    sudo npm install -g istanbul --verbos

    package.json

    经过上述操作之后我们发现,package.json发生了变化

    {
      "name": "read-write-file",
      "version": "1.0.0",
      "description": "简单的读写文件",
      "main": "index.js",
      "scripts": {
        "test": "mocha --reporter spec --timeout 2000 --recursive tests/"
      },
      "keywords": [
        "read",
        "write"
      ],
      "author": "黑MAO",
      "license": "MIT",
      "dependencies": {
        "es6-promise": "^3.0.2"
      },
      "devDependencies": {
        "chai": "^3.4.1",
        "istanbul": "^0.4.1",
        "mocha": "^2.3.4",
        "mochawesome": "^1.2.1"
      }
    }
    

    回顾

    发现不爽的地方,要执行一对命令,还有一些参数特别长,不容易记忆
    该怎么办呢?

    scripts脚本

    npm 提供简单的执行脚本,可以通过npm run * 执行scripts写的简单脚本
    于是乎~~

    常用脚本

        "scripts": {
            "watch": "mocha --watch tests/",
            "test": "mocha --reporter spec --timeout 2000 --recursive tests/",
            "test-cov": "istanbul cover ./node_modules/mocha/bin/_mocha -- -t 2000 --recursive  -R spec tests/",
            "test-html": "mocha --reporter mochawesome tests/"
          },
    

    命令行

    命令行模式又是怎么做的呢?

        "bin": {
            "writeFile": "./bin/writeFile.js",
            "readFile": "./bin/readFile.js"
        },
    

    注:
    开发模式可以使用:
    sudo npm link(添加命令行模式)
    sudo npm unlink(取消命令行模式)


    截至现在,我们的一个npm包就开发完成了~
    是不是也没有想想中那么复杂呢?

    如何发布?

    • npm adduser(用户名、密码、邮箱)-- 注册帐号
    • npm whoami(查看当前帐号)
    • sudo npm publish(发布到npmjs.org)-- 注意:sudo权限

    如何使用?

    npm install read-write-file

    var file = require('read-write-file');
    
    var path = 'test.txt';
    var data = '我是测试文本';
    
    file.writeFile(path, data).then(function() {
      return file.readFile(path);
    }).then(function(txt) {
      console.log('测试结果:'+txt);
    });
    

    注:sudo install -g read-write-file可以使用命令行形式

    到目前为止,一个npm包就开发完成了~

    参考资料:

  • 相关阅读:
    YL杯超级篮球赛 (Standard IO)
    Window (Standard IO)
    toj1026 Network 双连通分量
    poj3177 Redundant Paths 双连通分量
    poj1144 Network 双连通分量
    bzoj1269
    bzoj1800
    CF911D
    CF910C
    CF910B
  • 原文地址:https://www.cnblogs.com/xiaoheimiaoer/p/5041266.html
Copyright © 2020-2023  润新知