I have an embedded ruby interpreter running inside a c application, and a ruby class that acts an an interface to the c application. So it looks something like this:
class MyApi
include RealCAPI
def api_method
some_call_to_c_api
end
end
Inside my ruby class that interacts with the api I have something like this. I create an instance of the MyApi class and then call methods on that instance.
class Foo
def initialize
api = MyApi.new
...
...
end
def do_something
bar = api.api_method
...
...
...
final_result #is a function of Foo methods but depends on something from the api
end
end
I would like to test Foo class with something like this:
describe Foo do
it "should do something" do
foo = Foo.new
expect(foo.do_something to eq("something")
end
end
The problem is that none of the calls to the api will work outside of the c application.
How do I test this Foo class?
Do I try to somehow test inside the c application?
Do I write a standalone test only "mock up" of the MyApi class, that mimics what happens in the c application?
I realize that if I mock up the api I can't really test it, but at least I will be able to test the classes that use it, right?
I think you have to mock the api, as you said. What you are testing here is the ruby code, so you should probably stick to it and test only the ruby code.
You'll end up with something saying : provided my api does something, then my ruby code works exactly as specified. Which is good, and does not rely on wheter your api is working or not.
Of course, you'll probably have to write some test for your api. But keeping both tests separated seems like a good idea to me !
edit
Not sure if that's your question here, but this could easily be done by using something like
it "should do something" do
MyApi.any_instance.stub(:ping).and_return("pong") #so that when foo calls ping, the api returns "pong"
foo = Foo.new
expect....
end
Related
I have the following:
class Foo
def bar(some_arg)
end
end
It is called as Foo.new.bar(some_arg). How do I test this in rspec? I don't know how to know whether I've created an instance of Foo that has called bar.
receive_message_chain is considered a smell as it makes it easy to violate the Law of Demeter.
expect_any_instance_of is considered a smell in that it is not specific as to which instance of Foo is being called.
As #GavinMiller noted, those practices are generally reserved for legacy code that you do not control.
Here's how to test Foo.new.bar(arg) without either:
class Baz
def do_something
Foo.new.bar('arg')
end
end
describe Baz do
subject(:baz) { described_class.new }
describe '#do_something' do
let(:foo) { instance_double(Foo, bar: true) }
before do
allow(Foo).to receive(:new).and_return(foo)
baz.do_something
end
it 'instantiates a Foo' do
expect(Foo).to have_received(:new).with(no_args)
end
it 'delegates to bar' do
expect(foo).to have_received(:bar).with('arg')
end
end
end
Note: I'm hard coding the arg here for simplicity. But, you could just as easily mock it, too. Showing that here would depend on how the arg is instantiated.
EDIT
It is important to note that these tests are intimately familiar with the underlying implementation. Therefore, if you change the implementation, the tests will fail. How to fix that issue depends on what exactly the Baz#do_something method does.
Let's say Baz#do_something actually just looks up a value from Foo#bar based on the arg and returns it without changing state anywhere. (This is called a Query method.) In that case, our tests should not care about Foo at all, they should only care that the correct value is returned by Baz#do_something.
On the other hand, let's say that Baz#do_something actually does change state somewhere, but does not return a testable value. (This is called a Command method.) In this case, we need to assert that the correct collaborators were called with the correct parameters. But, we can trust that the unit tests for those other objects will actually test their internals, so we can use mocks as placeholders. (The tests I showed above are of this variety.)
There's a fantastic talk on this by Sandi Metz from back in 2013. The specifics of the technologies she mentions have changed. But, the core content of how to test what is 100% relevant today.
Easiest way is to use expect_any_instance_of.
expect_any_instance_of(Foo).to receive(:bar).with(expect_arg).and_return(expected_result)
That said, this method is discouraged since it's complicated, it's a design smell, and it can result in weird behaviour. The suggested usage is for legacy code that you don't have full control over.
Speculating on what your code looks like, I'd expect something like this:
class Baz
def do_stuff
Foo.new.bar(arg)
end
end
it 'tests Baz but have to use expect_any_instance_of' do
expect_any_instance_of(Foo).to receive(:bar).with(expect_arg).and_return(expected_result)
Baz.do_stuff
# ...
end
If this is the situation you find yourself in, you're best off to raise the class instantiation into a default argument like this:
class Baz
def do_stuff(foo_instance = Foo.new)
foo_instance.bar(arg)
end
end
That way you can pass in a mock in place of the default instantiation:
it 'tests Baz properly now' do
mock_foo = stub(Foo)
Baz.do_stuff(mock_foo)
# ...
end
This is known as dependency injection. It's a bit of a forgotten art in Ruby but if you read up about Java testing patterns you'll find it. The rabbit hole goes pretty deep though once you start going that route and tends to be overkill for Ruby.
If you're mocking this methods in another class spec (say BazClass), then the mock method would just return an object with the information you are expecting. For example, if you use Foo#bar in this Baz#some_method spec, you can do this:
# Baz#some_method
def some_method(some_arg)
Foo.new.bar(some_arg)
end
#spec for Baz
it "baz#some_method" do
allow(Foo).to receive_message_chain(:bar).and_return(some_object)
expect(Baz.new.some_method(args)).to eq(something)
end
otherwise if you want the Foo to actually call the method and run it, then you would just call the method regularly
#spec for Baz
it "baz#some_method" do
result = Baz.new.some_method(args)
#foo = Foo.new.bar(args)
expect(result).to eq(#foo)
end
edit:
it "Foo to receive :bar" do
expect(Foo.new).to receive(:bar)
Baz.new.some_method(args)
end
I am working with Sinatra but I am completely new to Ruby and confused about what the below code is actually doing.
class Something < Sinatra::Base
get '/' do
'hello world'
end
end
We don't seem to be defining a method. Are we calling the get method? If so, at what time is it called? I've not seen anything like this in other languages.
If we had 2 classes that extended Sinatra::Base how would Sinatra understand that the get applies to Something rather than the other class.
As opposed to the way, e.g., Java functions, when you define classes in Ruby, Ruby is actually executing code. Kind of like Java's static blocks. So when you do e.g.
class Foo
puts(self)
end
you will open a class (i.e. change the current self to Foo), within its context do a puts (which will print out the Foo class object), and then close the class (returning self to what it was before).
get is a method defined on Sinatra::Base. Thus, your code is actually interpreted as if it were
class Something < Sinatra::Base
self.get('/') do
'hello world'
end
end
Because self (i.e. Foo) inherits from Sinatra::Base, that's a method we're invoking - and we're doing it as the Something class definition is being read.
What that method does, roughly, is keep a table of "things to do when GET request comes in". It remembers that when it sees URL /, it should do the block do "hello world" end; more-or-less like this (example code; the original is a bit more complex):
class Sinatra::Base
WHAT_TO_DO_ON_GET = {}
def self.get(url, &thing_to_do)
WHAT_TO_DO_ON_GET[url] = thing_to_do
end
end
The Sinatra runtime is just a loop that, when a GET request comes in, looks up the URL against THINGS_TO_DO_ON_GET and executes what it finds there.
I am extending an existing library by creating a child class which extends to the library class.
In the child class, I was able to test most of functionality in initialize method, but was not able to mock super call. The child class looks like something like below.
class Child < SomeLibrary
def initialize(arg)
validate_arg(arg)
do_something
super(arg)
end
def validate_arg(arg)
# do the validation
end
def do_something
#setup = true
end
end
How can I write rspec test (with mocha) such that I can mock super call? Note that I am testing functionality of initialize method in the Child class. Do I have to create separate code path which does not call super when it is provided with extra argument?
You can't mock super, and you shouldn't. When you mock something, you are verifying that a particular message is received, and super is not a message -- it's a keyword.
Instead, figure out what behavior of this class will change if the super call is missing, and write an example that exercises and verifies that behavior.
As #myron suggested you probably want to test the behavior happening in super.
But if you really want to do this, you could do:
expect_any_instance_of(A).to receive(:instance_method).and_call_original
Assuming
class B < A
def instance_method
super
end
end
class A
def instance_method
#
end
end
Disclaimer expect_any_instance_of are a mark of weak test (see):
This feature is sometimes useful when working with legacy code, though
in general we discourage its use for a number of reasons:
The rspec-mocks API is designed for individual object instances, but
this feature operates on entire classes of objects. As a result there
are some semantically confusing edge cases. For example, in
expect_any_instance_of(Widget).to receive(:name).twice it isn't clear
whether a specific instance is expected to receive name twice, or if
two receives total are expected. (It's the former.)
Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too
complex.
It is the most complicated feature of rspec-mocks, and has historically received the most bug reports. (None of the core team
actively use it, which doesn't help.)
A good way to test this is to set an expectation of some action taken by the superclass - example :
class Some::Thing < Some
def instance_method
super
end
end
and the super class:
class Some
def instance_method
another_method
end
def self.another_method # not private!
'does a thing'
end
end
now test :
describe '#instance_method' do
it 'appropriately triggers the super class method' do
sawm = Some::Thing.new
expect(sawm).to receive(:another_method)
sawm.instance_method
end
end
All This Determines Is That Super Was Called On the Superclass
This pattern's usefulness is dependent on how you structure your tests/what expectations you have of the child/derivative class' mutation by way of the super method being applied.
Also - pay close attention to class and instance methods, you will need to adjust allows and expects accordingly
YMMV
A bit late to this party, but what you can also do is forego using the super keyword and instead do
class Parent
def m(*args)
end
end
class Child < Parent
alias super_m m
def m(*args)
super_m(*args)
end
end
That way your super method is accessible like any other method and can e.g. be stubbed like any other method. The main downside is that you have to explicitly pass arguments to the call to the super method.
I have piece of code to test that is not wrapped in a method. It just stands alone with itself in a Ruby class.
begin
# Do stuff - bunch of Ruby code
end
This is not a Rails app. It's a standalone Ruby class. I don't want to execute the whole begin end statement in my rspec tests. How do you test something like this? Should it be done using mocks/stubs? I asked a couple of people but they also didn't know the answer.
I've found that this is easier to test if you can encapsulate the behavior in a method or a module, but it really depends on what code you're trying to execute. If the code winds up altering the class in a public fashion, you can write tests around the fact that the class behaves as expected in memory. For instance:
class Foo
attr_accessor :bar
end
describe Foo
it "should have an attr_accessor bar" do
foo = Foo.new
foo.bar = "baz"
foo.bar.should == "baz"
end
end
This becomes more difficult if you're altering the class in a way that is private.
I've had luck in the past by rewriting this type of behavior into a method that can be explicitly called. It makes testing a lot easier, as well as make it a lot easier to understand timing when troubleshooting problems. For instance:
class Foo
def self.run
# do stuff
end
end
Can you provide a little more context of what you're trying to do in your class?
I want to test one program which can capture and send IP packets to some clients, so how to mock request or client in Cucumber? thanks
Normally I would answer the question with the cavet that it's a bad idea but this is such a bad idea I'm only going to answer half of it, how to mock in Cucumber generically.
You see Cucumber is meant to be a total test from the outside in so it's meant to completely run your code without any test doubles. The whole point is you are not unit testing but are testing your whole application.
"We recommend you exercise your whole stack when using Cucumber. [However] you can set up mocks with expectations in your Step Definitions." - Aslak Hellesøy, Creator of Cucumber
Granted you can do this but you are going to need to write your own the TCPServer and TCPSocket classes to avoid using the network and that can actually introduce bugs since your writing specs against your mock Net classes not the actual Net classes. Again, not a good idea.
Enough yapping, here's how to use mocks in Cucumber. (I'm going to assume you have a basic understanding of Cucumber and Ruby so I will skip some steps like how to require your class files in Cucumber.)
Let's say you have the following classes:
class Bar
def expensive_method
"expensive method called"
end
end
class Foo
# Note that if we don't send a bar it will default to the standard Bar class
# This is a standard pattern to allow test injection into your code.
def initialize(bar=Bar.new)
#bar = bar
puts "Foo.bar: #{#bar.inspect}"
end
def do_something
puts "Foo is doing something to bar"
#bar.expensive_method
end
end
You should have the Bar and Foo classes required in your features/support/env.rb file but to enable RSpec mocks you need to add the following line:
require 'cucumber/rspec/doubles'
Now create a feature file like this one:
Feature: Do something
In order to get some value
As a stake holder
I want something done
Scenario: Do something
Given I am using Foo
When I do something
Then I should have an outcome
And add the steps to your step definitions file:
Given /^I am using Foo$/ do
# create a mock bar to avoid the expensive call
bar = double('bar')
bar.stub(:expensive_method).and_return('inexpensive mock method called')
#foo = Foo.new(bar)
end
When /^I do something$/ do
#outcome = #foo.do_something
# Debug display of the outcome
puts ""
puts "*" * 40
puts "\nMocked object call:"
puts #outcome
puts ""
puts "*" * 40
end
Then /^I should have an outcome$/ do
#outcome.should_not == nil
end
Now when you run your feature file you should see:
****************************************
Mocked object call:
inexpensive mock method called
****************************************