The RSpec Book笔记《四》Describing Code with RSpec用RSpec描述代码

简介:

在前面的章节,我们用cucumber从应用的层面,从外部描述codebreaker游戏的行为。我们定义了步骤,但是留下一个异常,我们希望game返回消息,但是返回的却是一个空数组。

在本章,我们将使用RSpec来描述更细粒度的行为,game对象实例的预期行为。

创建spec/codebreaker目录
 

 
  1. mkdir -p spec/codebreaker 



创建game_spec.rb文件
 

 
  1. vi spec/codebreaker/game_spec.rb 



输入下面的内容
 

 
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" 
  7.       it "prompts for the first guess" 
  8.     end 
  9.   end 
  10. end 



创建spec_helper.rb文件
 

 
  1. vi spec/spec_helper.rb 



输入下面的内容
 

 
  1. require 'codebreaker' 



执行命令

 
  1. rspec spec/codebreaker/game_spec.rb --format doc 



修改spec/codebreaker/game_spec.rb的内容
 

 
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" do 
  7.         output=double('output'
  8.         game=Game.new(output) 
  9.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  10.  
  11.         game.start 
  12.       end 
  13.       it "prompts for the first guess" 
  14.     end 
  15.   end 
  16. end 


执行命令
 

 
  1. rspec spec/codebreaker/game_spec.rb --color 



我们看到了红色的错误。有的时候是逻辑错误,有的时候是其他错误。不管什么错误,我们要把他们变成绿色的。

编辑文件
 

 
  1. vi spec/codebreaker/game_spec.rb 



更新内容
 

 
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" do 
  7.         output=double('output'
  8.         game=Game.new(output) 
  9.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  10.  
  11.         game.start 
  12.       end 
  13.       it "prompts for the first guess" 
  14.     end 
  15.   end 
  16. end 



执行命令
 

 
  1. rspec spec/codebreaker/game_spec.rb --color 



这下变成绿色了。

执行命令
 

 
  1. cucumber features/codebreaker_start_game.feature 



Then I should see "Welcome to Codebreaker!"也变成了绿色。

接下来我们继续编辑spec/codebreaker/game_spec.rb文件。

 

 
  1. vi sepc/codebreaker/game_spec.rb 



替换第二个it中的内容。
 

 
  1. it "prompts for the first guess" do 
  2.        output=double("output"
  3.        game=Game.new(output) 
  4.        output.should_receive(:puts).with("Enter guess:"
  5.  
  6.        game.start 
  7.      end 

然后再次执行

 

 
  1. rspec spec/codebreaker/game_spec.rb  

还是有错误

 

 
  1. .F 
  2.  
  3. Failures: 
  4.  
  5.   1) Codebreaker::Game#start prompts for the first guess 
  6.      Failure/Error: game.start 
  7.        Double "output" received :puts with unexpected arguments 
  8.          expected: ("Enter guess:") 
  9.               got: ("Welcome to Codebreaker!") 
  10.      # ./lib/codebreaker/game.rb:8:in `start' 
  11.      # ./spec/codebreaker/game_spec.rb:18:in `block (3 levels) in <module:Codebreaker>
  12.  
  13. Finished in 0.00178 seconds 
  14. 2 examples, 1 failure 

再次修改lib/codebreaker/game.rb

 

 
  1. vi lib/codebreaker/game.rb 

添加

 

 
  1. module Codebreaker 
  2.   class Game 
  3.     def initialize(output) 
  4.       @outputoutput=output 
  5.     end 
  6.  
  7.     def start 
  8.       @output.puts "Welcome to Codebreaker!" 
  9.       @output.puts "Enter guess:" 
  10.     end 
  11.   end 
  12. end 

执行

 

 
  1. rspec spec/codebreaker/game_spec.rb 

这下麻烦了,问题更大了,不仅第二个测试没有通过,就连刚才通过的第一个也出现了问题。

两次测试都没有接收到应该接收到信息,导致测试都失败了。

解决这个问题,有一个方法,就是as_null_object。

我们来修改一下game_spec.rb

 

 
  1. vi spec/codebreaker/game_spec.rb 

内容如下

 

 
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" do 
  7.         output=double('output').as_null_object 
  8.         game=Game.new(output) 
  9.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  10.  
  11.         game.start 
  12.       end 
  13.       it "prompts for the first guess" do 
  14.         output=double("output").as_null_object 
  15.         game=Game.new(output) 
  16.         output.should_receive(:puts).with("Enter guess:"
  17.  
  18.         game.start 
  19.       end 
  20.     end 
  21.   end 
  22. end 
  23. ~      

执行命令

 

 
  1. rspec spec/codebreaker/game_spec.rb --color 

这回好了,又出现了我们的绿色,测试都通过了。

是重构的时候了。

Martin Flower在重构一书中写到。

重构,就是在不改变代码外在行为的情况下,改进代码的内部结构。

如何来确保外在行为没有被改变呢?在每次改变之后,我们都会进行检查。如果测试通过,那就说我们成功了。如果没有通过,就要找出问题,甚至回滚到前一个阶段,来保证外在行为还保持在绿色状态。

重构有一个作用,就是消除重复,提高重用。

在game_spec.rb中有两行代码

 

 
  1. output=double("output").as_null_object  
  2. game=Game.new(output)  

在两次测试都出现了。我们来修改一下game_spec.rb,引入before(:each)。

 

 
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.  
  7.       before(:eachdo 
  8.         @output=double('output').as_null_object 
  9.         @game=Game.new(@output) 
  10.       end 
  11.     
  12.       it "sends a welcome message" do 
  13.         @output.should_receive(:puts).with('Welcome to Codebreaker!'
  14.  
  15.         @game.start 
  16.       end 
  17.       it "prompts for the first guess" do 
  18.         @output.should_receive(:puts).with("Enter guess:"
  19.  
  20.         @game.start 
  21.       end 
  22.     end 
  23.   end 
  24. end 

before(:each)中的代码在每个测试运行之前都会执行。

如果只是创建变量,给变量赋值。在RSpec中我们可以使用let(:method){}来替代。

 

 
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.  
  7.        let(:output) { double("output").as_null_object } 
  8.        let(:game) { Game.new(output) } 
  9.  
  10.       it "sends a welcome message" do 
  11.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  12.  
  13.         game.start 
  14.       end 
  15.       it "prompts for the first guess" do 
  16.         output.should_receive(:puts).with("Enter guess:"
  17.  
  18.         game.start 
  19.       end 
  20.     end 
  21.   end 
  22. end 
  23. ~     

重构完成了。跑一下测试,也是绿色的,没有问题,重构成功了。

让我们来做一个可执行的game。

 

 
  1. mkdir bin 
  2. vi bin/codebreaker

在文件中输入

 

 
  1. #!/usr/bin/evn ruby 
  2. $LOAD_PATH.unshift File.expand_path('../../lib',__FILE__) 
  3. require 'codebreaker' 
  4.  
  5. game=Codebreaker::Game.new(STDOUT) 
  6. game.start 

让我们执行以下

 

 
  1. ruby bin/codebreaker 

看到了我们想要的内容。

 

 
  1. Welcome to Codebreaker! 
  2. Enter guess: 

总结

本章我们从一个cucumber的逻辑错误开始。从外部的cucumber周期,转向内部的rspec周期。

本章我们使用RSpec来完成了一个red/green/refactor的周期。

这就是BDD的周期。从外到里的驱动开发,从使用cucumber描述的业务scenario,到使用rspec描述的内部对象。

 




本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/1060876,如需转载请自行联系原作者

目录
相关文章
|
Java 项目管理 Maven
这你必须知道,如何上传自己的jar包到maven公共远程中央仓库
这你必须知道,如何上传自己的jar包到maven公共远程中央仓库
1281 0
|
Java API Spring
API管理工具Swagger介绍及Springfox原理分析
swagger是一个API框架,号称世界上最流行的API工具。它提供了API管理的全套解决方案,比如API在线编辑器,API UI展示界面,代码生成器等诸多功能。
11560 115
|
4月前
|
人工智能 缓存 Kubernetes
KubeCon China 2025 速递:Fluid - 数据无所不在,计算无处不及
Fluid 在 Kubernetes 中实现了弹性数据集管理,提高 AI/ML 工作负载的数据接入效率,并入选 CNCF 2024 技术雷达报告,评为“Adopt”类别。
|
8月前
|
机器学习/深度学习 人工智能 自然语言处理
《深度揭秘:DeepSeek如何解锁自然语言处理密码》
DeepSeek是基于Transformer架构的自然语言处理(NLP)佼佼者,通过自注意力机制高效捕捉长距离依赖关系,优化语义理解和生成。预训练阶段,DeepSeek利用海量文本数据学习语法、语义等知识,确保多义词的准确理解与翻译。监督微调和强化学习从人类反馈进一步提升模型性能,使其在智能客服、写作辅助、信息检索等领域广泛应用,为AI语言应用开辟新道路。
251 2
|
机器学习/深度学习 人工智能 自然语言处理
AppFlow:玩转科大讯飞星火大模型
讯飞大模型是科大讯飞开发的先进人工智能系统,利用深度学习和自然语言处理技术,提供高效准确的语言理解与生成能力,在语音识别、语义理解、机器翻译及智能对话等领域表现出色。AppFlow全面支持该模型,并提供公共配置模版,用户只需填写简单参数即可在钉钉群中配置讯飞模型机器人。
302 2
|
SQL Oracle 关系型数据库
【Oracle】oracle sqluldr2工具使用方法
oracle数据导出工具sqluldr2可以将数据以csv、txt等格式导出,适用于大批量数据的导出,导出速度非常快。导出后可以使用oracle loader工具将数据导入。
1224 0
markdown插入图片、音频视频
markdown插入图片、音频视频
615 0
markdown插入图片、音频视频
|
存储 缓存 NoSQL
Redis 攻击方法总结(一)
Redis 攻击方法总结
702 0
Redis 攻击方法总结(一)
|
存储 Java
Java基础:static的理解(含义、用法及静态修饰的优先顺序)
Java基础:static的理解(含义、用法及静态修饰的优先顺序)
346 0
Java基础:static的理解(含义、用法及静态修饰的优先顺序)