RSpec: testing AR finders without hitting the database - ruby

How would I test the following finder with RSpec?
def self.find_by_mbid(mbid)
super(mbid.downcase())
end
The only way I see that is possible would be storing it in the database beforehand, which I'd like to avoid, as this is a unit test. I don't see how to return a mock here, since super is pretty much inaccessible to RSpec.
Is there a better way of doing this? Or are unit tests too low-level for this sort of thing?

You could mock this if you're willing to change the implementation, but I recommend spec'ing finders with the database.

Related

Rspec Double leaking to another example

I am testing a class that makes use of a client that makes external requests and I would like to mock this client, but verify that it gets called, however I am getting a double error.
My test looks like something like this:
describe '#execute' do
let(:attributes) { {foo: 'bar'} }
let(:client_double) { double('client', create: nil) }
let(:use_case) { described.class.new }
before do
allow(Client::Base).to receive(:new).and_return(client_double)
use_case.execute(attributes)
end
it 'creates something' do
expect(Something.find_by(foo: 'bar')).not_to be_nil
end
it 'calls client' do
expect(client).to have_received(:create).with('bar')
end
end
and the first example passes as expected, however rspec keeps breaking in the second example giving me this error:
#<Double "foo"> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
someone knows what I can do to fix it?
Reusing Fixtures with Let Methods
In this case, before is actually before(:each), which is reusing the client_double and attributes you defined with the #let helper method. The let commands make those variables functionally equivalent to instance variables within the scope of the described object, so you aren't really testing freshly-created objects in each example.
Some alternatives include:
Refactor to place all your setup into before(:each) without the let statements.
Make your tests DAMP by doing more setup within each example.
Set up new scope for a new #describe, so your test doubles/values aren't being reused.
Use your :before, :after, or :around blocks to reset state between tests, if needed.
Since you don't show the actual class or real code under test, it's hard to offer specific insights into the right way to test the object you're trying to test. It's not even clear why you feel you need to test the collaborator object within a unit test, so you might want to give some thought to that as well.
It turns out I was using a singleton as a client and haven't realized before, so it was trully class caching it through examples. To fix it all I did was mock the instantiate method instead of the new method and everything worked.
So in the end this worked:
allow(Client::Base).to receive(:instantiate).and_return(client_double)

How can I workaround this capybara/rspec naming collision?

I'm writing a new rspec test case using Capybara (SitePrism actually, which uses Capybara) and I've run into an apparently known issue: https://github.com/jnicklas/capybara/issues/1396. Essentially, due to a change in one or the other, RSpec and Capybara now both have methods named all, and when I try to use SitePrism to find a group of elements or SitePrism sections, Capybara invokes the wrong method and returns something of the type RSpec::Matchers::BuiltIn::All rather than the expected array of Capybara or SitePrism objects.
For some reason, all my old tests, including many with very similar usage of sections and elements constructs, work perfectly fine. I'm having a really hard time finding differences between them that would account for one failing and the other succeeding. I briefly tried rolling back either Capybara or RSpec to just try to make the problem go away for the moment, but it seemed silly, trying to pinpoint when the problem was introduced, when existing test cases that have been running every day never broke.
Can anyone advise me on why one works and the other fails? Here is what these two test cases have in common:
Both spec files require the same spec_helper.rb file.
The spec_helper.rb file has both require rspec and require capybara.
Each spec file uses require_relative to require a page object used in each test.
Each page object file has a sections :table_rows, <SECTION CLASS>, <ROW CSS> declaration. They may have different names, classes, and CSS Selectors, but they're the same basic construct.
In each spec file, methods are invoked on the page objects that reference table_rows.
Referencing table_rows in one of these older test cases works just fine, but I'm getting the name collision error in the new test case. Anyone know why that might be, so I can fix the new test case?
Failing that, does anyone know how I can separate things so that the page object requires capybara but not rspec and the spec file requires rspec but not capybara to prevent the collision? I don't know much about Ruby package management, but it seems like in order to run an rspec test case that uses a page object, it's all going to be mixed together. I'm not sure of a way to avoid requiring them both.
Failing that, does anyone know which versions of either of these two packages I could theoretically use to avoid the issue? I did experiments with rspec 3.2 and capybara 2.4, and neither seemed to work, and I gave up there, because I remembered that no matter how far back in time I go, the test cases I've already written were working fine, and it just seemed silly to try to solve it this way.
Require capybara after Rspec or always call page.all rather than just all
Not the best approach, but you can monkey patch the SitePrism::Page class. Here you replace 'all' by 'page.all':
SitePrism::Page.class_eval do
def find_all(*find_args)
page.all(*find_args)
end
end

Calling a Method - Watir-Webdriver

I am new to automation and I have started using Watir Webdriver to automate a website. However, there are certain pieces of code which can be reused for multiple test cases. How can I group the reusable pieces of code into a method which I can call in every test case ? Could you please provide references or examples ?
You are interested in methods.
Here is an example:
def automation_snippet
#browser.text_field(:id => 'field_id').set 'foo'
end
Later you will probably be interested in classes, page objects, and modules.
This is basic ruby stuff. You will need to make sure that whatever method you want to use is in scope, too.
So, keywords to start with are methods and scope. If you are using Cucumber, then you can define methods in any step definition file and they will be available in all your other Cucumber tests.

How to mock properly an openssl object in rspec?

I have a method:
require 'openssl'
def extract_creds(data)
pfx = OpenSSL::PKCS12.new(data)
{ :certificate => pfx.certificate.to_pem, :key => pfx.key.to_pem }
rescue
# handle error
end
and i want to write a rspec example for it. How should i properly mock pfx object?
Why this may be the wrong question
You should generally use a mock when you want to:
Avoid testing system behavior outside the unit under test.
Avoid expensive operations, such inter-system tests.
Ensure that an object is called, but you don't care about the results.
In this case, it's not clear why you want to test this behavior, or what you expect the results to be, or why you think you might not get it. For the most part, testing a well-tested external library is not the right thing to do.
What I think the right question is
So, depending on what you really want to test, you may want to check:
Whether the method raises any exceptions.
Whether your method is actually called from some object that should call it.
In both cases, though, there seems to be no real benefit to mocking the library vs. including a real certificate as fixture data. Fixtures are better than mocks for actually exercising code.

Should I open up a class's instance variables just for test validation?

I'm new to BDD, and I'm finding alot of instances where I'm adding instance variables to attr_accessor only so that my tests have an easy way to validate whether they are in the state that they should be in. But this feels a little dirty because no other class needs this information, and so I'm only making it public for the tests. Is this standard or a sign of bad design?
For example I have a collection class which stores an object, and then commits it to a batch array. At the end of the import, the batch array is used to make a batch insert into the database. But there is no need to check on the state of the batch at any point. But in testing I'm wanting to make sure that the batch is in the state that I think it is, and so am opening up that variable for inspection. Is the fact that it's not being checked within the code the real problem?
Is using instance_variable_get an option for you?
>> class Foo
.. def initialize
.. #foo = 'bar'
.. end
.. end #=> nil
>> Foo.new.instance_variable_get(:#foo) #=> "bar"
DON'T Do That!
This is going too deep. See the comments from #Andy and #Michael in the thread on the other 'accepted' answer
Don't write unit tests that look inside the code unit. If it's not something you can see from the external interfaces to the object then it doesn't need to be unit tested. You are going past how the code behaves into how it implements that behavior, and you don't want tests that go down to that level as they don't provide any real value in terms of proving that the code does what it should.
Just consider how many tests you might have to update if someone re-factors that code and in order to make it more readable changes the internal names of things.. or maybe finds a better way of doing whatever and all the instance variables get altered.. The code unit could still be working perfectly fine, but the unit tests would fail..
Alternatively if a change was made that adjusts how the internal variables report out to the interface, the code could effectively be broken, but the tests that are looking at the internal values of the instance variables would not report any failure.

Resources