Ruby on rails 模仿和存根。我不知道';我得不到基本知识

Ruby on rails 模仿和存根。我不知道';我得不到基本知识,ruby-on-rails,rspec,mocking,stubbing,Ruby On Rails,Rspec,Mocking,Stubbing,我正在从FactoryGirl中解放出来(至少在lib文件夹中)。所以,我开始写一些奇怪的东西,比如“mock”和“stub”。有人能帮助新手吗 我有这个模块 module LogWorker extend self def check_todo_on_log(log, done) if done == "1" log.todo.completed = true log.todo.save! elsif done.nil? log.

我正在从FactoryGirl中解放出来(至少在lib文件夹中)。所以,我开始写一些奇怪的东西,比如“mock”和“stub”。有人能帮助新手吗

我有这个模块

module LogWorker
   extend self
   def check_todo_on_log(log, done)
    if done == "1"
      log.todo.completed = true
      log.todo.save!
    elsif done.nil?
      log.todo.completed = false
      log.todo.save!
    end
  end 
end
log
todo
是具有
todo:has\u许多日志关联的rails模型。但在使用存根和模拟时,这真的不重要,对吗

我尝试过很多东西,但是当我把模拟传递给方法时,什么都没有发生

describe LogWorker do
  it 'should check_todo_on_log'do
    todo = mock("todo")
    log = mock("log")
    log.stub!(:todo).and_return(todo)
    todo.stub!(:completed).and_return(false)
    LogWorker.check_todo_on_log(log,1)
    log.todo.completed.should eq true
  end
end

Failures:

  1) LogWorker should check_todo_on_log
     Failure/Error: log.todo.completed.should eq true

       expected: true
            got: false

       (compared using ==

我真的希望看到一些测试LogWorker的规范。使用存根和/或模拟检查日志方法。

首先,您的
检查日志方法非常糟糕。永远不要使用字符串作为选项,尤其是当字符串为“1”时。同样,如果你通过“2”,什么也不会发生。我假设它只是一个分部方法,而您的代码并不是这样的:P

看看你的代码,你有三个主要问题。首先,调用
LogWorker。检查日志(log,1)
上的待办事项。这不会做任何事情,因为您的方法只在第二个参数是字符串
“1”
或nil时才执行操作。其次,存根
todo.completed
,因此它总是返回false:
todo.stub!(:已完成)。并且返回(false)
。然后测试它是否为真。显然,这将失败。最后,不要嘲笑
保存方法。我不知道代码是如何为您实际运行的(它对我不起作用)

下面是我如何编写您的规范(请注意,他们正在测试奇怪的行为,因为
check\u todo\on\u log
方法也很奇怪)

首先,有一种更简单的方法可以将模拟方法添加到模拟对象中。您可以将键和值传递给
mock
方法,它们将自动创建

接下来,我将模拟放入
let
块中。这使得每次测试都可以轻松地重新创建它们。最后,我为函数的每个可能行为添加了一个测试

# you won't need these two lines, they just let the code be run by itself
# without a rails app behind it. This is one of the powers of mocks, 
# the Todo and Log classes aren't even defined anywhere, yet I can 
# still test the `LogWorker` class!
require 'rspec'
require 'rspec/mocks/standalone'

module LogWorker
   extend self
   def check_todo_on_log(log, done)
    if done == "1"
      log.todo.completed = true
      log.todo.save!
    elsif done.nil?
      log.todo.completed = false
      log.todo.save!
    end
  end
end

describe LogWorker do
  let(:todo) { mock("Todo", save!: true) }
  let(:log) { mock("Log", todo: todo) }
  describe :check_todo_on_log do
    it 'checks todo when done is "1"'do
      todo.should_receive(:completed=).with(true)
      LogWorker.check_todo_on_log(log,"1")
    end
    it 'unchecks todo when done is nil'do
      todo.should_receive(:completed=).with(false)
      LogWorker.check_todo_on_log(log,nil)
    end

    it "doesn't do anything when done is not '1' or nil" do
      todo.should_not_receive(:completed=)
      LogWorker.check_todo_on_log(log,3)
    end
  end
end

注意我是如何使用基于行为的测试的吗?我不是测试mock上的属性是否有值,而是检查是否对其调用了适当的方法。这是正确使用mock的关键。

多么好的答案!我从中学到了很多。字符串作为选项传递,因为它是作为复选框中的参数传入的。你认为如果我做了,我应该做
?只是看看它是否在那里。谢谢我建议在调用
check\u todo\u on\u log
的控制器代码中,不要直接传入值,而是对其进行检查。例如
checked=params[:checked]=“1”
(或任何您拥有的),然后
检查日志(log,checked)
。然后只需使用
log.todo.completed=done
,无需使用if语句。