【OK】创建新节点实现四则运算及单元测试
创建新节点实现四则运算
一.所需的几个文件
package.json
four-arithmetic.js
four-arithmetic.html
remote-server.html
remote-server.js
test文件夹下的fourarithmeti_spec.js文件(用于单元测试)
二.各个文件的代码
1.package.json文件
//package.json文件是不能有注释的,使用时要将注释全部去掉;
{
"name": "node-red-contrib-example-my-four-arithmetic",
"version": "1.0.0",
"description": "",
"main": "index.js",//如果想要在终端运行,需要主函数是对应的.js文件;;
//(手动)在脚本部分的 package.json 文件中添加一个测试脚本。 要在测试目录中运行所有带有 _spec.js 前缀的文件;
"scripts": {
"test": "mocha \"test/**/*_spec.js \""
},
//测试脚本完毕;
//以下是构建node-red节点所需要的部分;
"node-red": {
"nodes": {
"four-arithmetic": "four-arithmetic.js",
"remote-server": "remote-server.js"
}
},
//构建node-red节点部分添加完毕;
"author": "",
"license": "ISC",
//一下是单元测试时才会出现、用到的部分;
//执行过程:在VS Code中打开package.json文件,ctrl+`打开终端,定位到桌面上的Creat文件(项目文件)所在路径,执行以下命令)
//"npm install node-red-node-test-helper node-red --save-dev"(不带双引号)即可
"devDependencies": {
"node-red": "^2.2.2",
"node-red-node-test-helper": "^0.2.7"
}
//单元测试的依赖添加完毕;
}
2.four-arithmetic.js
module.exports = function (RED) { //这句代码的意思是把以下的所有包装成一个模块;
function FourArithmeticNode(config) { //这是一个定义的节点构造函数;节点由构造函数定义,可用于创建节点的新实例。该函数在运行时注册,因此可以在流中部署相应类型的节点时调用它。
this.firstnumber = parseInt(config.firstnumber); //此函数 parseInt()把字符串变成数字,防止我们输入的数字在程序运行过程中被传输成了字符串;
this.secondnumber = parseInt(config.secondnumber); //此函数 parseInt()把字符串变成数字,防止我们输入的数字在程序运行过程中被传输成了字符串;
//以下是预先定义的运算函数,在这里定义,下面使用;
function FourArithmeticadd(n1, n2) { //预定义的函数里只传递一个形参
result = n1 + n2;
return result;
}
function FourArithmeticminus(n1, n2) {
result = n1 - n2;
return result;
}
function FourArithmeticmultiply(n1, n2) {
result = n1 * n2;
return result;
}
function FourArithmeticRounding(n1, n2) { //除法
result = n1 / n2;
return result;
}
function FourArithmeticremainder(n1, n2) { //取余
result = n1 % n2;
return result;
}
//预定义函数结束;
RED.nodes.createNode(this, config);
//配置节点;然后该节点可以使用此属性来访问运行时的配置节点。
//Retrieve the config node
this.server = RED.nodes.getNode(config.server);
var node = this; //访问上下文(节点),这是最简单的形式;node=this了;所以紧接着下面这句代码也可以写成this.on("input", function (msg) {}
node.on("input", function (msg) { //注册一个input侦听器以接收来自流中上游节点的消息;侦听器把条件语句包含在内,当有一个输入的时候,就开始执行下面的代码;
this.status({ fill: "green", shape: "dot", text: "connected" }); //设置状态,如果有输入,则节点变为此状态;
console.log(node) //打印日志;
//以下为条件判断语句;执行不同的加、减、乘、取整、取余;
if (this.server.example == "+") {
msg.payload = FourArithmeticadd(this.firstnumber, this.secondnumber);
node.send(msg);
}
else if (this.server.example == "-") {
msg.payload = FourArithmeticminus(this.firstnumber, this.secondnumber);
node.send(msg);
}
else if (this.server.example == "*") {
msg.payload = FourArithmeticmultiply(this.firstnumber, this.secondnumber);
node.send(msg);
}
else if (this.server.example == "%") {
msg.payload = FourArithmeticremainder(this.firstnumber, this.secondnumber);
node.send(msg);
}
else {
msg.payload = FourArithmeticRounding(this.firstnumber, this.secondnumber);
node.send(msg);
}
})
this.status({ fill: "red", shape: "ring", text: "disconnected" });//设置正常运行状态;
//结束;
}
RED.nodes.registerType("four-arithmetic", FourArithmeticNode); //这也是一个定义的节点构造函数的一部分;节点由构造函数定义,可用于创建节点的新实例。该函数在运行时注册,因此可以在流中部署相应类型的节点时调用它。
}
3.four-arithmetic.html
<!--以下js语法部分都属于 (主节点定义)-->
<script type="text/javascript">
RED.nodes.registerType('four-arithmetic', { //节点类型;下面为节点定义属性;
icon: "font-awesome/fa-automobile", //定义图标,采用的是Font Awesome icon(字体真棒图标)
category: 'function', //类别属性
color: '#a6bbcf', //定义背景色
defaults: { //定义节点在工作区中的显示名称;之后还有配置节点;
name: { value: "四则运算" },
firstnumber: { validate: "", validate: RED.validators.number() }, //判断输入的firstnumber是否是数字;
secondnumber: { validate: "", validate: RED.validators.number() }, //判断输入的secondnumber是否是数字;
server: { value: "", type: "remote-server" } //节点通过向defaults 数组添加属性来注册它们对配置节点的使用,并将type属性设置为配置节点的类型。
},
inputs: 1, //定义节点输入的个数,只能是0或1;
outputs: 1, //定义节点输出的个数
label: function () { //节点标签,在工作区中使用的标签;
return this.name || "four-arithmetic"; //以下示例显示如何设置标签以获取此属性的值或默认为合理的值。打开网页版后,我们的节点在工作区中显示的是“四则运算”
},
paletteLabel: "我的四则运算", //此处定义的是“调色板标签”
inputLabels: "这是四则运算", //一下两行,节点可以在其输入和输出端口上提供标签,通过将鼠标悬停在端口上可以看到这些标签
outputLabels: ["这里是输出"], //可以由节点的 html 文件静态设置
});
</script>
<!--以下是编辑对话框的编辑模板,以下为HTML定义的代码部分-->
<script type="text/html" data-template-name="four-arithmetic">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<!-- 以下为添加一个条目,属性验证first); -->
<div class="form-row">
<label for="node-input-firstnumber"><i class="fa fa-tag"></i> Firstnumber</label>
<input type="text" id="node-input-firstnumber">
</div>
<!--结束-->
<!-- 以下为添加一个条目,属性验证second); -->
<div class="form-row">
<label for="node-input-secondnumber"><i class="fa fa-tag"></i> Secondnumber</label>
<input type="text" id="node-input-secondnumber">
</div>
<!--结束-->
<!--以下为配置节点新加内容;重要-->
<div class="form-row node-input-server">
<label for="node-input-server"><i class="fa fa-random"></i>点击配置</label>
<input type="text" id="node-input-server" placeholder="">
</div>
<!--结束-->
</script>
<button onclick="myFunction('Harry Potter','Wizard')">点击这里</button>
<script>
function myFunction(name, job) {
alert("Welcome " + name + ", the " + job);
}
</script>
<script>
a = 5;
b = 6;
c = a + b;
console.log(c);
</script>
<!--以下是帮助文本-->
<script type="text/html" data-help-name="lower-case">
<p>这是一个四则运算编辑器,可以输入两个数,选择运算方式,点开始运行</p>
</script>
remote-server.html
<!--以下为Configuration nodes(配置节点)的相关函数 remote-server.html -->
<script type="text/javascript">
RED.nodes.registerType('remote-server', {
category: 'config',
defaults: {
example: { value: "", required: true }
},
label: function () {
return this.example ;
},
oneditprepare: function () {
$("#node-config-input-example").typedInput({
types: [
{
value: "",
options: [
{ value: "+", label: "+" },
{ value: "-", label: "-" },
{ value: "*", label: "*" },
{ value: "/", label: "/" },
{ value: "%", label: "%" }
]
}
]
})
}
});
</script>
<script type="text/html" data-template-name="remote-server">
<div class="form-row">
<label for="node-config-input-example"><i class ="fa fa-bookmark"></i>123</label>
<input type="text" id="node-config-input-example">
</div>
</script>
<!--结束-->
4.remote-server.js
module.exports = function(RED) {
function RemoteServerNode(n) {
RED.nodes.createNode(this,n);
this.example = n.example
}
RED.nodes.registerType("remote-server",RemoteServerNode);
}
5.test文件夹下的fourarithmeti_spec.js文件(用于单元测试)(貌似已经实现了)
var should = require("should");
var helper = require("node-red-node-test-helper");
var fourNode = require("../four-arithmetic.js"); //引入需要测试的文件;
var severNode = require("../remote-server.js"); //引入需要测试的文件;
helper.init(require.resolve('node-red')); //初始化助手,告诉助手运行时在哪里可以找到node-red;
module.should = function fourNode(config) { //初始化节点;
RED.nodes.createNode(this, config);
}
//描述'unit.test'的行为;
describe('four-arithmetic Node', function () {
beforeEach(function (done) { //启动 Node-RED 服务器;
helper.startServer(done);
});
afterEach(function (done) { //停止服务器; 通常在 unload() 完成后调用; 例如,要卸载一个流,然后在每次测试后停止服务器:
helper.unload();
helper.stopServer(done);
});
//加载成功后执行回调函数,获取结果;
it('流', function (done) {
var flow = [{ id: "n1", type: "four-arithmetic", name: "four-arithmetic" },];
helper.load([fourNode], flow, function () { //异步 helper.load() 方法就会调用提供的回调函数(Node-RED 服务器和运行准备就绪时);
var n1 = helper.getNode("n1"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用,此时n1就代表这个节点;
try {
n1.should.have.property('name', 'four-arithmetic'); //断言,断言失败则被显式捕获并传递给 done()调用;
done();
} catch (err) {
done(err);
}
});
});
it('测试流', function (done) {
var flow = [
{ id: "n1", type: "four-arithmetic", name: "四则运算", server:'n3', firstnumber: 6,secondnumber: 3 , wires: [["n2"]] },
{ id: "n2", type: "helper" },
{ id: 'n3', type: 'remote-server',example: '*'}
];
helper.load([fourNode,severNode], flow, function () { 异步 helper.load() 方法就会调用提供的回调函数(Node-RED 服务器和运行准备就绪时);
var n2 = helper.getNode("n2"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用;
var n1 = helper.getNode("n1"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用;
n2.on("input", function (msg) { //接收到输入;
try {
msg.should.have.property('payload',18); //可以检查helper节点输入事件处理程序中的有效负载确实是小写的;
done();
} catch (err) { //断言失败则被显式捕获并传递给 done()调用;
done(err);
}
});
n1.receive({ payload: '....' }); //调用 n1.receive({ payload: "UpperCase" }),将消息发送到被测的lower-case节点 n1;
});
});
});
6.mocha测试
var fourarithmetic = require("../four-arithmetic.js"); //引入需要测试的文件;
var assert = require('assert'); //引入断言库'assert','assert'断言库模块式node的内置模块;
describe('测试four-arithmetic.js', function () { //describe方法,称为测试套件,表示一组相关的测试;先描述一下测试的文件;
describe('测试加法', function () { //再描述一下测试的方法;
it('2+3', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticadd(2, 3), 5);
})
it('8+6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticadd(8, 6), 14);
})
})
describe('测试减法', function () { //再描述一下测试的方法;
it('3-2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticminus(3, 2), 1);
})
it('8-6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticminus(8, 6), 2);
})
})
describe('测试乘法', function () { //再描述一下测试的方法;
it('3*2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticmultiply(3, 2), 6);
})
it('8*6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticmultiply(8, 6), 48);
})
})
describe('测试取余', function () { //再描述一下测试的方法;
it('3%2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticremainder(3, 2), 1);
})
it('8*6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticremainder(8, 6), 2);
})
})
describe('测试取整', function () { //再描述一下测试的方法;
it('3/2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticRounding(3, 2), 1);
})
it('8/6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticRounding(8, 6), 1);
})
it('9/4', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticRounding(9, 4), 3);
})
})
});
三.单元测试
(一).最简单方法
mocha是一个功能丰富的javascript测试框架
添加一个链接,里面有详细介绍:
链接: https://www.bilibili.com/video/BV1GQ4y1N7xj?spm_id_from=333.337.search-card.all.click.
把所要测试的代码中的函数拉出来,分别对自定义的函数进行测试
- 1.创建一个文件夹“测试”如下图所示:
- 2.把我们要测试的自定义函数复制一份,在刚刚创建的测试文件夹中创建一个four-arithmetic.js文件,将自定义函数写入其中;
//以下是预先定义的运算函数,在这里定义,下面使用;
function FourArithmeticadd(n1, n2) { //预定义的函数里只传递一个形参
result = n1 + n2;
return result;
}
function FourArithmeticminus(n1, n2) {
result = n1 - n2;
return result;
}
function FourArithmeticmultiply(n1, n2) {
result = n1 * n2;
return result;
}
function FourArithmeticRounding(n1, n2) {
result = n1 / n2;
return result;
}
function FourArithmeticremainder(n1, n2) {
result = n1 % n2;
return result;
}
module.exports = { //将自定义的函数方法导出
FourArithmeticadd,
FourArithmeticminus,
FourArithmeticmultiply,
FourArithmeticRounding,
FourArithmeticremainder
}
- 3.在创建的测试文件夹中创建一个test子文件夹;在该子文件夹中创建一个fourarithmeti_spec.js文件,测试代码将写入其中
var fourarithmetic = require("../four-arithmetic.js"); //引入需要测试的文件;
var assert = require('assert'); //引入断言库'assert','assert'断言库模块式node的内置模块;
describe('测试four-arithmetic.js', function () { //describe方法,称为测试套件,表示一组相关的测试;先描述一下测试的文件;
describe('测试加法', function () { //再描述一下测试的方法;
it('2+3', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticadd(2, 3), 5);
})
it('8+6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticadd(8, 6), 14);
})
})
describe('测试减法', function () { //再描述一下测试的方法;
it('3-2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticminus(3, 2), 1);
})
it('8-6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticminus(8, 6), 2);
})
})
describe('测试乘法', function () { //再描述一下测试的方法;
it('3*2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticmultiply(3, 2), 6);
})
it('8*6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticmultiply(8, 6), 48);
})
})
describe('测试取余', function () { //再描述一下测试的方法;
it('3%2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticremainder(3, 2), 1);
})
it('8*6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticremainder(8, 6), 2);
})
})
describe('测试取整', function () { //再描述一下测试的方法;
it('3/2', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticRounding(3, 2), 1);
})
it('8/6', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticRounding(8, 6), 1);
})
it('9/4', function () { //mocha提供了it方法,称为测试用例,表示一个单独的测试;可以写某个方法的多个测试用例测试不同情况下的状况;
assert.equal(fourarithmetic.FourArithmeticRounding(9, 4), 3);
})
})
});
-
4.之后我们在终端定位到F:\桌面\测试,执行npm init 生成package.json文件;
-
5.给package.json文件添加依赖项:npm install node-red-node-test-helper node-red --save-dev
注意要先在终端定位到F:\桌面\测试文件夹。
-
6.打开package.json文件,将
"test": " "
里面的内容修改成以下格式;
"scripts": {
"test": "mocha \"test/**/*_spec.js \""
},
- 7.打开需要测试的four-arithmetic.js文件夹,在内部程序的最后面添加一段代码:
module.exports = {
FourArithmeticadd,
FourArithmeticminus,
FourArithmeticmultiply,
FourArithmeticRounding,
FourArithmeticremainder
}
目的是 将自定义的函数方法导出;
- 8.最后在终端定位到F:\桌面\测试文件夹,输入命令
npm test 即可完成测试;
(二).第二种方法
将我们最开始创建好的完整代码文件当成是一个模块,当成是一个整体,不用把函数都拉出来一一进行测试了;以下是具体的方法实现:
1.具体的程序实现:
var should = require("should");
var helper = require("node-red-node-test-helper");
var fourNode = require("../four-arithmetic.js"); //引入需要测试的文件;
var severNode = require("../remote-server.js"); //引入需要测试的文件;
helper.init(require.resolve('node-red')); //初始化助手,告诉助手运行时在哪里可以找到node-red;
module.should = function fourNode(config) { //初始化节点;
RED.nodes.createNode(this, config);
}
//描述'unit.test'的行为;
describe('four-arithmetic Node', function () {
beforeEach(function (done) { //启动 Node-RED 服务器;
helper.startServer(done);
});
afterEach(function (done) { //停止服务器; 通常在 unload() 完成后调用; 例如,要卸载一个流,然后在每次测试后停止服务器:
helper.unload();
helper.stopServer(done);
});
//加载成功后执行回调函数,获取结果;
it('流', function (done) {
var flow = [{ id: "n1", type: "four-arithmetic", name: "four-arithmetic" },];
helper.load([fourNode], flow, function () { //异步 helper.load() 方法就会调用提供的回调函数(Node-RED 服务器和运行准备就绪时);
var n1 = helper.getNode("n1"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用,此时n1就代表这个节点;
try {
n1.should.have.property('name', 'four-arithmetic'); //断言,断言失败则被显式捕获并传递给 done()调用;
done();
} catch (err) {
done(err);
}
});
});
it('测试流', function (done) {
var flow = [
{ id: "n1", type: "four-arithmetic", name: "四则运算", server:'n3', firstnumber: 6,secondnumber: 3 , wires: [["n2"]] },
{ id: "n2", type: "helper" },
{ id: 'n3', type: 'remote-server',example: '*'}
];
helper.load([fourNode,severNode], flow, function () { 异步 helper.load() 方法就会调用提供的回调函数(Node-RED 服务器和运行准备就绪时);
var n2 = helper.getNode("n2"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用;
var n1 = helper.getNode("n1"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用;
n2.on("input", function (msg) { //接收到输入;
try {
msg.should.have.property('payload',18); //可以检查helper节点输入事件处理程序中的有效负载确实是小写的;
done();
} catch (err) { //断言失败则被显式捕获并传递给 done()调用;
done(err);
}
});
n1.receive({ payload: '....' }); //调用 n1.receive({ payload: "UpperCase" }),将消息发送到被测的lower-case节点 n1;
});
});
});
2.程序的简单详解:
以下程序用于创建流
//加载成功后执行回调函数,获取结果;
it('流', function (done) {
var flow = [{ id: "n1", type: "four-arithmetic", name: "four-arithmetic" },];
helper.load([fourNode], flow, function () { //异步 helper.load() 方法就会调用提供的回调函数(Node-RED 服务器和运行准备就绪时);
var n1 = helper.getNode("n1"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用,此时n1就代表这个节点;
try {
n1.should.have.property('name', 'four-arithmetic'); //断言,断言失败则被显式捕获并传递给 done()调用;
done();
} catch (err) {
done(err);
}
});
});
以下程序为了引入配置节点,以及添加firstnumber和secendnumber的具体数值、添加配置节点的运算符;
it('测试流', function (done) {
var flow = [
{ id: "n1", type: "four-arithmetic", name: "四则运算", server:'n3', firstnumber: 6,secondnumber: 3 , wires: [["n2"]] },
{ id: "n2", type: "helper" },
{ id: 'n3', type: 'remote-server',example: '*'}
];
helper.load([fourNode,severNode], flow, function () { 异步 helper.load() 方法就会调用提供的回调函数(Node-RED 服务器和运行准备就绪时);
var n2 = helper.getNode("n2"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用;
var n1 = helper.getNode("n1"); //通过调用 helper.getNode(id) 的方法来获取运行时节点的引用;
n2.on("input", function (msg) { //接收到输入;
try {
msg.should.have.property('payload',18); //可以检查helper节点输入事件处理程序中的有效负载确实是小写的;
done();
} catch (err) { //断言失败则被显式捕获并传递给 done()调用;
done(err);
}
});
n1.receive({ payload: '....' }); //调用 n1.receive({ payload: "UpperCase" }),将消息发送到被测的lower-case节点 n1;
});
});
3.结束和结果
以上就是一个完整的四则运算的单元测试,具体的程序的用法和组合都在脑子里了,还有程序里也有一些解释;
运行的结果: