相关技术
Git 基础:Git 安装及常用命令介绍
TDD:深度解读 - TDD(测试驱动开发)—— SeabornLee
本项目 Git 地址:https://github.com/cwxyz007/FrequencyNumber
- Git
- Nodejs
- Jasmine
- TDD
本文阅读指南
由于本文代码较多,所以阅读起来,比较枯燥,所以在次列出本文顺序列表,方便读者按需阅读。
-
配置环境
-
题目
-
题目分析
-
编写测试用例
-
TDD 过程
- 创建项目
- 编写测试用例
- 测试过程
- 完整代码
- 完整 Git log
-
总结
配置环境
Ubuntu 环境下的安装过程
$ apt install git #安装 Git
$ apt install nodejs-legacy #安装 Nodejs
$ apt install npm #安装 npm
$ npm install -g jasmine #安装 jasmine
题目
Frequency Number需求:我想要一个nodejs小程序,它可以帮我处理一段字符串信息,这段字符串信息是由英文单词组成,每两个单词之间有空格,处理结果也为一段字符串,这个字符串应该每行只显示一个单词和它的数量,并且按出现频率倒序排列。
example:
input:
“it was the age of wisdom it was the age of foolishness it is”
output:
it 3
was 2
the 2
age 2
of 2
wisdom 1
foolishness 1
is 1
题目分析
题目要求输入一个字符串,然后统计其中的每个单词的个数,然后按倒序输出。省去中间思考的解释过程,那么最后的思考结果如下图:

测试用例
先提前写好测试用例,如果在编写代码的时候想到更好的测试用例时,再添加。
name | input | output | comments |
---|---|---|---|
test 1 | "it" | it 1 | 最简单的例子 |
test 2 | "it is" | it 1\r\nis 1 | 分割单词 |
test 3 | "it is it" | it 2\r\nis 1 | 统计 |
test 4 | "it is it he" | it 2\r\nis 1\r\nhe 1 | 处理多个空格 |
test 5 | "it is it he is is | is 3\r\nit 2\r\nhe 1 | 排序 |
test 6 | "it was the age of wisdom it was the age of foolishness it is" | it 3\r\nwas 2\r\nthe 2\r\nage 2\r\nof 2\r\nwisdom 1\r\nfoolishness 1\r\nis 1 | 测试题目以及格式化输出 |
TDD 过程
创建项目
初始化 jamine
~/WebstormProjects/JasminePractice$ jasmine init
创建好文件后,初始化以及提交一次 Git

测试每一个用例
- test 1
第一个测试用例是最简单的,以最小代码实现功能为基础,那么直接输出字符串,加上数字 1 ,就可以了。
CountWords:
function CountWords() {
}
CountWords.prototype.main = function (text) {
return text + ' ' + 1;
};
module.exports = CountWords;
MySpec:
describe("Frequency Number", function () {
var CountWords = require('../../lib/CountWords');
var countWords;
beforeEach(function () {
countWords = new CountWords();
});
it("should return 'it 1' when given 'it'",function () {
expect(countWords.main('it')).toEqual('it 1');
});
});
Git 日志:

- test 2
第二个测试用例的目的是为了分割单词,那么最小代码就可以直接分割,然后连接单词和个数就行了。
CountWords:
CountWords.prototype.main = function (text) {
var wordsArray = text.split(' ');
if(wordsArray.length === 1)
return wordsArray[0] + ' ' + 1;
else
return wordsArray[0] + ' ' + 1 + '\r\n' + wordsArray[1] + ' ' + 1;
};
MySpec:
it("should return 'it 1\r\nis 1' when given 'it is'",function () {
expect(countWords.main('it is')).toEqual('it 1\r\nis 1');
});
Git 日志:

- test 3
这个测试的目的是为了统计每个单词,那么只要把统计功能写了就好了。
CountWords:
function CountWords() {
}
CountWords.prototype.main = function (text) {
var wordsArray = text.split(' ');
var result = this.countEachWord(wordsArray);
if(result.length === 1)
return result[0][0] + ' ' + result[0][1];
else if(result.length === 2)
return result[0][0] + ' ' + result[0][1] + '\r\n' + result[1][0] + ' ' + result[1][1];
};
CountWords.prototype.countEachWord = function(wordsArray){
var countArray = [];
for(var i = 0; i < wordsArray.length; i++){
var wordPos = this.findWordPosition(wordsArray[i],countArray);
if(wordPos !== false){
countArray[wordPos][1]++;
}
else{
countArray.push([wordsArray[i],1]);
}
}
return countArray;
};
CountWords.prototype.findWordPosition = function (word ,arr){
for(var i = 0; i < arr.length; i++){
if(arr[i][0] === word)
{
return i;
}
}
return false;
};
module.exports = CountWords;
MySpec:
it("should return 'it 2\r\nis 1' when given 'it is it'",function () {
expect(countWords.main('it is it')).toEqual('it 2\r\nis 1');
});
Git 日志:

- test 4
这个测试是为了处理多个空格的情况,方法有很多,我选择用正则表达式的方式。因为代码少。(哈哈~~,好懒怎么办!)
只需要把 split(' ')
修改成 split(/\s+/)
就行了,注意没有引号!
CountWords:
CountWords.prototype.main = function (text) {
var wordsArray = text.split(/\s+/);
var countResult = this.countEachWord(wordsArray);
if(countResult.length === 1)
return countResult[0][0] + ' ' + countResult[0][1];
else if(countResult.length === 2)
return countResult[0][0] + ' ' + countResult[0][1] + '\r\n' + countResult[1][0] + ' ' + countResult[1][1];
};
MySpec:
it("should return 'it 2\r\nis 1' when given 'it is it'",function () {
expect(countWords.main('it is it')).toEqual('it 2\r\nis 1');
});
Git 日志:

- test 5
因为我使用的是二维数组,所以直接使用数组的 sort
方法,然后自定义排序条件,个数多的排在前面。
CountWords:
CountWords.prototype.main = function (text) {
var wordsArray = text.split(/\s+/);
var countResult = this.countEachWord(wordsArray);
countResult.sort(function compareEachOne(a,b){
return a[1] > b[1] ? -1 : (a[1] < b[1] ? 1 : 0);
});
var result = '';
for (var i = 0; i < countResult.length; i++)
{
if(i === 0)
result += countResult[i][0] + ' ' + countResult[i][1];
else
result += '\r\n' + countResult[i][0] + ' ' + countResult[i][1];
}
return result;
};
MySpec:
it("should return 'is 3\r\nit 2\r\nhe 1' when given 'it is it he is is'",function () {
expect(countWords.main('it is it he is is')).toEqual('is 3\r\nit 2\r\nhe 1');
});
Git 日志:

- test 6
到此,题目已经解决了,只是最后需要注意一下输出格式。
CountWords:
CountWords.prototype.main = function (text) {
var wordsArray = text.split(/\s+/);
var countResult = this.countEachWord(wordsArray);
countResult.sort(function compareEachOne(a,b){
return a[1] > b[1] ? -1 : (a[1] < b[1] ? 1 : 0);
});
return this.format(countResult);
};
CountWords.prototype.format = function (arr) {
var resultStr = '';
for(var i in arr){
if(i === '0') //attention : i is equal'0',not 0
resultStr += arr[i][0] + ' ' + arr[i][1];
else
resultStr += '\r\n' + arr[i][0] + ' ' + arr[i][1];
}
return resultStr;
};
MySpec:
it("should return 'it 3\r\nwas 2\r\nthe 2\r\nage 2\r\nof 2\r\nwisdom 1\r\nfoolishness 1\r\nis 1' when given 'it was the age of wisdom it was the age of foolishness it is'",function () {
expect(countWords.main('it was the age of wisdom it was the age of foolishness it is')).toEqual('it 3\r\nwas 2\r\nthe 2\r\nage 2\r\nof 2\r\nwisdom 1\r\nfoolishness 1\r\nis 1');
});
Git 日志:

完整代码
CountWords:
function CountWords() {
}
CountWords.prototype.main = function (text) {
var wordsArray = text.split(/\s+/);
var countResult = this.countEachWord(wordsArray);
countResult.sort(function compareEachOne(a,b){
return a[1] > b[1] ? -1 : (a[1] < b[1] ? 1 : 0);
});
return this.format(countResult);
};
CountWords.prototype.format = function (arr) {
var resultStr = '';
for(var i in arr){
if(i === '0') //attention : i is equal'0',not 0
resultStr += arr[i][0] + ' ' + arr[i][1];
else
resultStr += '\r\n' + arr[i][0] + ' ' + arr[i][1];
}
return resultStr;
};
CountWords.prototype.countEachWord = function(wordsArray){
var countArray = [];
for(var i = 0; i < wordsArray.length; i++){
var wordPos = this.findWordPosition(wordsArray[i],countArray);
if(wordPos !== false){
countArray[wordPos][1]++;
}
else{
countArray.push([wordsArray[i],1]);
}
}
return countArray;
};
CountWords.prototype.findWordPosition = function (word ,arr){
for(var i = 0; i < arr.length; i++){
if(arr[i][0] === word)
{
return i;
}
}
return false;
};
module.exports = CountWords;
MySpec:
describe("Frequency Number", function () {
var CountWords = require('../../lib/CountWords');
var countWords;
beforeEach(function () {
countWords = new CountWords();
});
it("should return 'it 1' when given 'it'",function () {
expect(countWords.main('it')).toEqual('it 1');
});
it("should return 'it 1\r\nis 1' when given 'it is'",function () {
expect(countWords.main('it is')).toEqual('it 1\r\nis 1');
});
it("should return 'it 2\r\nis 1' when given 'it is it'",function () {
expect(countWords.main('it is it')).toEqual('it 2\r\nis 1');
});
it("should return 'it 2\r\nis 1' when given 'it is it'",function () {
expect(countWords.main('it is it')).toEqual('it 2\r\nis 1');
});
it("should return 'is 3\r\nit 2\r\nhe 1' when given 'it is it he is is'",function () {
expect(countWords.main('it is it he is is')).toEqual('is 3\r\nit 2\r\nhe 1');
});
it("should return 'it 3\r\nwas 2\r\nthe 2\r\nage 2\r\nof 2\r\nwisdom 1\r\nfoolishness 1\r\nis 1' when given 'it was the age of wisdom it was the age of foolishness it is'",function () {
expect(countWords.main('it was the age of wisdom it was the age of foolishness it is')).toEqual('it 3\r\nwas 2\r\nthe 2\r\nage 2\r\nof 2\r\nwisdom 1\r\nfoolishness 1\r\nis 1');
});
});
完整 Git log

总结
在写代码的过程中,遇到了很多问题,其中最多的是对 JS 的不熟悉,花费了大量的时间去查看相关文档。本来想法已经有了,思路也很清晰,但就是不知道怎么去实现。例如使用正则表达式的时候不清楚怎么用。最开始为是这样写的 split('\s+')
,囧,这当然是错的。因为使用正则表达式的时候不需要加引号,而且在 JS 中需要添加反斜杠,正确的写法应该是这样 split(/\s+/)
。
其中 Jasmine 和 Nodejs 还接触的很浅,感悟还没有出来!囧!
通过这次练习,体验了一次 TDD 的优势,也慢慢的有点理解 TDD 的思想了。
这次最大的收获其实是终于体验到了在 Linux 环境下编程。之前也安装过 Linux + Win 双系统,但是那个时候觉得 Linux 太丑(某种意义)了,没有坚持玩下去。现在再来看看 Linux ,感觉没有之前那种什么都不知道的感觉了。至少,现在我在 Linux 下写了为的第一个小程序 —— ‘Frequency Number’
其实 Tux 这只企鹅还是很美的,只是之前的我没发现。
网友评论
文章结构清晰,按照tdd方式来做。
╮(╯-╰)╭,感觉棒棒哒