测试驱动开发 Ruby 命令行工具实战

TL;DR; 本文介绍了 to_yaml 的开发过程当中如何采用 TDD 方法开发功能,以及用到的免费服务 GitHub / TravisCI / RubyGemsgit

TDD (测试驱动开发)是敏捷开发中的一项核心实践和技术,也是一种设计方法论。 TDD 的原理是在开发功能代码以前,先编写单元测试用例代码,测试代码肯定须要编写什么产品代码。github

to_yaml 是一款命令行工具,JSON 输入转为 YAML 文本输出json

<!--excerpt-->ruby

背景

先从最近使用的 ElasticSearch 提及。 做为通用的日志收集、分析与展现的工具集,ELK 工具栈已经至关普及。 其中在管理 ElasticSearch 集群时,大部分时间都要使用 HTTP 接口跟 JSON 格式数据打交道。app

ES 输出 JSON 数据内容比较多,即便使用 ?pretty 参数,仍然难看清数据的层次关系。 ?pretty 的一个反作用是输出内容过长,浪费了大量的屏幕纵向空间。框架

使用 YAML 格式可以在很大程度上缓解空间的问题。 基于这个想法,作了一个简单的工具出来,发布在了 RubyGems.orgcurl

初步想法

在实际使用 JSON 时,但愿的是能直接将接口输出内容直接转换为 YAML 格式。 如这样的形式:svg

$ curl -s -XGET http://localhost:9200/_mappings | to_yaml

另外,要把这个工具做为软件包发布出来,方便别的地方使用。 ruby 程序的第一选择便是 gem工具

使用 gem 发布还有一个好处:能够利用 ruby 的开发工具,如 bundlerspec 等,来发布质量可控的软件。单元测试

开始动手

建立 gem 框架目录

现阶段最简单的工具是 bundle gem

$ bundle gem to_yaml
Creating gem 'to_yaml'...
Code of conduct enabled in config
MIT License enabled in config
      create  to_yaml/Gemfile
      create  to_yaml/.gitignore
      create  to_yaml/lib/to_yaml.rb
      create  to_yaml/lib/to_yaml/version.rb
      create  to_yaml/to_yaml.gemspec
      create  to_yaml/Rakefile
      create  to_yaml/README.md
      create  to_yaml/bin/console
      create  to_yaml/bin/setup
      create  to_yaml/CODE_OF_CONDUCT.md
      create  to_yaml/LICENSE.txt
      create  to_yaml/.travis.yml
      create  to_yaml/.rspec
      create  to_yaml/spec/spec_helper.rb
      create  to_yaml/spec/to_yaml_spec.rb
Initializing git repo in /private/tmp/to_yaml

更改 to_yaml.gemspec 中的基本信息,全部标记 TODO 的全酌情修改。

github 上建立一个代码库。而后 git add . && git commit -am Init && git push

如今咱们已经有了基本的框架。

代码未动,测试先行

查看 Rakefile 内容以下

require "bundler/gem_tasks"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec)

task :default => :spec

命令行执行 rake specrake,能够执行默认的测试用例。

来让咱们为 to_yaml 工具写一个最简单的用例。

打开 spec/to_yaml_spec.rb 文件,在 describe ToYaml 与对应 end 直接增长一个用例

it 'parse json with cli' do
    json_str = '{"a": [1, 2, 3], "b": {"c": "d"}}'
    yaml_str = '---\na:\n- 1\n- 2\n- 3\nb:\n  c: d\n'

    cli_output = run_cli('./exe/to_yaml', json_str)
    expect(cli_output).to eq(yaml_str)
  end

修改 spec/spec_helper.rb,加入 helper 方法

require 'open3'

def run_cli(exe="./exe/to_yaml", input)
  cmd = %Q{echo \'#{input}\'| bundle exec #{exe}}
  output = Open3.popen3(cmd) { |stdin, stdout, stderr, wait_thr| stdout.read }
end

注意这里面,咱们构造一个 JSON 字符串 json_str,和一个 YAML 字符串 yaml_str。 指望的行为是运行 ./exe/to_yaml 命令处理 json_str 后,输出 cli_outputyaml_str 一致。

如今执行这个测试 rake specrake,毫无悬念失败了。

不过这是一个好的开头,咱们有了明确的目标:让测试成功。

建立命令行

to_yaml.gemspec 中,经过 spec.executables 指定了可执行文件的路径为 ./exe/ 目录。

spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }

to_yaml 命令文件就放在这里。

如今建立 exe/to_yaml 文件,加入内容:

#!/usr/bin/env ruby

require 'json'
require 'yaml'

puts JSON.load(STDIN).to_yaml

再简单不过的一个单行脚本。 按咱们的设想,从 STDIN 读入内容,转化为 YAML 格式。 本着 [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it) 原则,没作任何额外的容错 - -!

如今执行测试

$ rake spec

ToYaml
  has a version number
  parse json with cli

Finished in 0.77046 seconds (files took 0.09961 seconds to load)
2 examples, 0 failures

太棒了,测试用例所有成功。

测试,AGAIN

目前为止的测试还在靠手工驱动执行。 而在实践中,则须要将持续构建整合进开发流程,全部状态都能够追溯,并能及时反馈构建结果

gem 目录下,咱们能够找到一个 .travis.yml 文件。 travis-ci 是开源世界应用最广泛的持续集成工具。

使用 travis-ci 须要先作设置。 到 https://travis-ci.org,使用 github 账号登陆,并从 github 中添加咱们的 to_yaml 项目。

设置完毕,下次咱们 push 代码到 githubtravis-ci 能够自动开始构建。 为了方便从 GitHub 页面看到项目 Build 状态,在 README.md 文件里增长项目的状态图标

[![Build Status](https://travis-ci.org/Lax/to_yaml.svg?branch=master)](https://travis-ci.org/Lax/to_yaml)

刚才已经在本地测试成功,如今将代码 commit 并 push 到 github。 从 travis-ci 页面,咱们看到已经建立了第一次构建 Build #1。 很幸运,此次构建到结果也成功了!

发布

rake release 能够自动建立一个发布 tag,将代码 push 到 github。 并 build 出 to_yaml.gem,经过 gem push 发布到 rubygems.org。

使用

下次使用时,直接 gem install to_yaml,既能够在 PATH 中搜索到 to_yaml 执行程序。

测试一个公开的 JSON 服务:

$ curl -s http://jsonip.com
{"ip":"123.45.67.89","about":"/about","Pro!":"http://getjsonip.com"}

$ curl -s http://jsonip.com | to_yaml
---
ip: 123.45.67.89
about: /about
Pro!: http://getjsonip.com

很清爽,有木有!

总结展望

本文介绍的是一个极简工具的开发过程,但愿你能感觉到测试驱动开发所发挥的做用。

做者在实际项目中也尽量实践 TDD 方法,发布的工具如 aliyun.gem (一个阿里云API的客户端) 也采用了相似流程。 甚至本博客也引入了 travis-ci 作持续构建和测试,确保内容中引用的图片和连接无死链。

若是你对本文描述的内容感兴趣,欢迎发布评论来交流。

相关文章
相关标签/搜索