[Unit Testing for Zombie] 04. Mock and Stub
STUBBING
Notice how the show_author_summary method in tweet is calling the zombie_summarymethod inside zombie. This isn't good! Stub out the zombie_summary method in the test below.
tweet.rb
class Tweet < ActiveRecord::Base belongs_to :zombie validates :status, presence: true, length: {within: 3..140, allow_blank: true} validates :zombie, presence: true def brains? status =~ /(brains|breins)/i end def show_author_summary self.status = self.zombie.zombie_summary end def status_image image = ZwitPic.get_status_image(self.id) # This is returning an array {image_name: image[0], image_url: image[1]} end end
zombie.rb
class Zombie < ActiveRecord::Base has_many :tweets validates :name, presence: true, length: {maximum: 15}, uniqueness: {case_sensitive: false} validates :graveyard, presence: true def to_param name end def zombie_summary "#{name} lives in #{graveyard} and has #{self.tweets.size} tweets" end def geolocate loc = Zoogle.graveyard_locator(self.graveyard) "#{loc[:latitude]}, #{loc[:longitude]}" end def geolocate_with_object loc = Zoogle.graveyard_locator(self.graveyard) "#{loc.latitude}, #{loc.longitude}" end def avatar_url "http://zombitar.com/#{id}.jpg" end end
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "show_author_summary should set status to zombie summary" do @tweet.show_author_summary assert_equal @tweet.zombie.zombie_summary, @tweet.status, 'tweet status does not contain zombie summary' end end
Answer:
First use stubs to call zombie_summary method, it is a fake calling, but set the value of status.
It should before the @tweet.show_author_summary line.
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "show_author_summary should set status to zombie summary" do @tweet.zombie.stubs(:zombie_summary) @tweet.show_author_summary assert_equal @tweet.zombie.zombie_summary , @tweet.status, 'tweet status does not contain zombie summary' end end
MOCKING
Create another test to make sure zombie_summary is actually called in theshow_author_summary method using a mock.
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "show_author_summary should call zombie_summary" do @tweet.zombie.expects(:zombie_summary) @tweet.show_author_summary end end
STUB + ASSERT
Notice how the status_image method in Tweet calls ZwitPic.get_status_image, potentially a remote server call. Create a test which mocks this method and ensures the correct params get sent.
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "status_image calls the ZwitPic get_status_image api" do ZwitPic.expects(:get_status_image).with(@tweet.id) @tweet.status_image end end
Dont forget to call status_image mthod after stubs the get_status_image, because get_status_image is stubbed, but it is inisde status_images method.
STUBS & MOCKING
Notice how the status_image method now has a return value which is expecting image to be an Array. Modify the mock to return an array with two string values.
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "status_image calls the ZwitPic get_status_image api" do ZwitPic.expects(:get_status_image).with(@tweet.id).returns(['Scary Zombie', 'http://zwitpic.com/scary_zombie.png']) @tweet.status_image end end
RETURNING PROPER RESULTS
Notice that the status_image method on Tweet now returns an HTML image tag with an alt attribute set to the image name, and the ZwitPic.get_status_image method is now returning a hash. Write a test which stubs, instead of mocks, get_status_image and tests that @tweet.status_image returns the proper result (which should also be an image tag).
def status_image status_image = ZwitPic.get_status_image(self.id) "<img src='#{status_image[:url]}' alt='#{status_image[:name]}' />".html_safe end
Answer:
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "status_image returns a properly formated HTML image element with alt and src" do #using stubs to test get_status_image is called and set the value #with(): send the param #returns(): mock a return value ZwitPic.stubs(:get_status_image).with(@tweet.id).returns({name: "Gegg", url: "http://monxp.com/monkey.png"}) assert_equal "<img src='http://monxp.com/monkey.png' alt='Gegg' />", @tweet.status_image end end
OBJECT STUB
Notice how the get_status_image is now returning an object. Using an object stub, refactor the previously created test.
def status_image image = ZwitPic.get_status_image(self.id) "<img src='#{image.url}' alt='#{image.name}' />".html_safe end
Answer:
class TweetTest < ActiveSupport::TestCase def setup @tweet = tweets(:hello_world) end test "status_image returns a properly formated HTML image element with alt and src" do image = stub(name: "Yummy brain I ate last night", url: "http://zwitpic.com/2.jpg") ZwitPic.stubs(:get_status_image).with(@tweet.id).returns(image) assert_equal "<img src='http://zwitpic.com/2.jpg' alt='Yummy brain I ate last night' />", @tweet.status_image end end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具