I'm trying to understand how stubbing works with Rails
and MiniTest
. I've followed the simple example from MiniTest documentation. I'm stuck with a very simple example:
require 'minitest/mock'
require "test_helper"
class TotoTest < ActiveSupport::TestCase
class Clazz
def foo
"foo"
end
end
test "Stubbing" do
puts Clazz.new.foo # "foo" is well printed
Clazz.stub :foo, "bar" do # ERROR HERE
assert_equal "bar", Clazz.new.foo
end
end
end
When stubbing, I get an error telling the method foo
. The full execution log:
Testing started at 13:55 ...
[...]
Started
foo
Minitest::UnexpectedError: NameError: undefined method `foo' for class `TotoTest::Clazz'
test/models/toto_test.rb:14:in `block in <class:TotoTest>'
test/models/toto_test.rb:14:in `block in <class:TotoTest>'
Finished in 0.52883s
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
Process finished with exit code 0
I can't begin to understand why i'm told the foo
method does not exist when the execution runs well the line before.
What am I missing? Why does this not work?
I've even tried an alternative, using a mock:
require 'minitest/mock'
require "test_helper"
class TotoTest < ActiveSupport::TestCase
class Clazz
def foo
"foo"
end
end
test "Stubbing" do
mock = Minitest::Mock.new
def mock.foo
"bar"
end
puts Clazz.new.foo
Clazz.stub :foo, mock do
assert_equal "bar", Clazz.new.foo
end
end
end
The result is the same. Where am I wrong?
EDIT : Use case
To be more precise, I'd like to stub the YouTube API. The calls to YouTube API are implemented in a module. The module is included in a controller. In a system test, I'd like to replace the real call to that API by a stub, to be independent from YouTube API.
You're stubbing a class method instead of an instance method:
Clazz.stub :foo, "bar"
You call stub
on an instance of Class
class referenced by a constant Clazz
.
You should call #stub
on Clazz
instance:
clazz = Clazz.new
clazz.stub :foo, mock do
assert_equal "bar", clazz.foo
end
Edit: Regarding the use case. I think that a controller is a wrong place to include methods handling an external API. I would suggest to wrap it in a separate object, then you can stub this object, e.g.:
yt_mock = ... # mocking yt methods you want to use
YouTube.stub :new, yt_mock do
# controler test
end
You can also create YouTube as a class that accepts adapters and delegates calls to them - one adapter would use a real YT api, another one just predefined answers.