記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包就開發完成了~

參考資料:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM