How to perform action when assertion is failed in minitest - ruby

I am doing automation of api using minitest in ruby. If any assertion is getting failed, then I want to perform some sort of action. Is there anything I can do when assert_equal() is getting failed?

assert_equal compares two things. You can compare these things by yourself.
Like:
if a != b
action
end
p.s. assert_equal do little more than just comparing(compare float/time/class/regex), you can look at his source.

Related

Mocking a Browser for RSpec, Without Test Doubles Leaking

I find mocking things with RSpec to be entirely problematic and I often don't know how much code to include, in terms of it being diagnostic. So I'll start with the situation I have and the code that I've isolated as causing the problem.
I have tests where I need to mock a browser. I have a mock driver I set up like this:
require "watir"
def mock_driver
browser = double("watir")
allow(browser).to receive(:is_a?).with(Watir::Browser).and_return(true)
allow(browser).to receive(:driver).and_return(true)
browser
end
The only problems I have in my test suite are these two tests:
context "an empiric driver is requested" do
it "a watir browser is provided" do
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
Empiric.set_browser mock_driver
end
it "the requested watir browser can be shut down" do
#allow(Empiric.browser).to receive(:quit)
Empiric.quit_browser
#allow(mock_browser).to receive(:new).and_return(Empiric.browser)
#Empiric.set_browser mock_driver
end
end
(The commented out bits in the second test are on purpose to illustrate what's going on.)
With that one line in place in the second test, I get the following error on that test:
<Double "watir"> 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.
If I entirely comment out the first test above, that error doesn't happen so I know I've isolated the two tests that are interacting with each other.
Okay, now notice the final line of my second test that is commented out. That seems to be what the error is indicating to me. It's saying I need to create a new double in the other. Okay, so I'll change my last test:
it "the requested watir browser can be shut down" do
#allow(Empiric.browser).to receive(:quit)
Empiric.quit_browser
#allow(mock_browser).to receive(:new).and_return(Empiric.browser)
Empiric.set_browser mock_driver
end
So here I've uncommented the last line so I'm establishing the mock_driver in that test and not allowing the code to leak.
That, however, returns exactly the same error on exactly the same test.
I'm not sure if it would help to see the methods that are being called in that test, but here they are. First is set_browser:
def set_browser(app = :chrome, *args)
#browser = Watir::Browser.new(app, *args)
Empiric.browser = #browser
end
And here is quit_browser:
def quit_browser
#browser.quit
end
The fact that RSpec thought one test was "leaking" into the other made me think that perhaps my #browser instance was the problem, essentially being what's persisting between the two tests. But I don't see how to get around that. I thought that maybe if I quit the browser in the first test, that would help. So I changed the first test to this:
it "a watir browser is provided" do
Empiric.quit_browser
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
Empiric.start_browser mock_driver
end
That, however, led to the above error being shown on both tests now.
My more likely accurate guess is that I simply don't know how to provide a mock in this context.
I think you have to use allow with the mock and not Watir::Browser.
For example, what happens if you allow the mock browser to receive whatever calls the browser would and have the it return the mock browser?
Right now you're allowing the "Watir::Browser" to receive those messages and that's returning an "Empiric.browser". Looking at your code, I understand why you put that in there but I think that might be what's screwing you up here.
Mocks in RSpec are horrible things that rarely if ever work correctly in situations like this. I would entirely recommend not using the mock_driver that you have set up. Rather, for each of your tests just do something similar to what you are doing in the mock_driver. My guess is you're including the mock driver as part of a shared context and that, too, is another thing that is very fragile in RSpec. Not recommended.
Instead you might want to use contexts to break up your tests. Then for each context block have a before block. I'm not sure if you should use before:all or before:each given that you're simulating a browser. But that way you can set up the browser in the before and tear it down in an after.
But I would recommend getting it working in each test individually first. Even if it's a lot of code duplication. Then once all tests are passing, refactor to put the browser stuff in those before/after blocks.
But, again, don't use mocks. Don't use shared contexts. It never ends well and honestly it makes your tests harder to reason about.
Given some advice from Micah, I wanted to provide an answer with a solution. I ended up doing this:
context "an empiric driver is requested" do
it "a watir browser is provided" do
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
allow(Empiric.browser).to receive(:driver).and_return(true)
expect { Empiric.start_browser :some_browser }.not_to raise_error
end
it "the requested watir browser can be shut down" do
allow(Empiric.browser).to receive(:quit)
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
allow(Empiric.browser).to receive(:driver).and_return(true)
expect { Empiric.quit_browser }.not_to raise_error
end
end
All of that was needed as it is or I would get some error or other. I removed my mock driver and, per Micah's suggestion, simply tried to incorporate what seemed to work. The above "contraption" is what I ended up with as the sweet spot.
This works in the sense of giving coverage of the methods in question. What was interesting was that I had to add this to my RSpec configuration:
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.allow_message_expectations_on_nil = true
end
end
I needed to do this because RSpec was reporting that I was calling allowing something that was nil to receive a value.
This brought up some interesting things, if you think about it. I have a test that is clearly passing. And it adds to my code coverage. But is it actually testing the quit action on a browser? Well, not really since it was testing a quit action on something that it thought was nil.
But -- it does work. And it must be calling the lines of code in question because the code coverage, as reported my SimpleCov, indicates that the statements in question have been checked.

Get Status from Scenario with Cucumber 2.0.0

It looks like its no longer possible use
scenario.status
in Cucumber 2.0.0 to determine the status of a scenario (passed, failed, undefined, skipped). It looks like it is possible to see if a scenario either passes or fails, but I'm also looking to see when steps are undefined or skipped.
Previously, in my code I would write the results to a DB in the After hook of the scenario, like so:
After do |scenario|
#controller.post_results(scenario)
end
Inside of post results, I would call scenario.status to get the status.
Is this no longer possible to do with Cucumber 2.0.0? If it is, what is the new method?
You need to use Hooks.rb to get the status of scenario.
You can use
if scenario.failed?
todo...
end
or
scenario.status
inside the hooks.rb.
Find more details here: https://github.com/cucumber/cucumber/wiki/Hooks

Ruby load module in test

I am running a padrino application and have started with the included mailers. I want to test that a mail is sent and had previously had no trouble accessing the Mail::TestMailer object to look at the mails delivered during the test.
That is the background about what I am doing but not precisely the question. I want to know how can a module become available to the runtime environment.
I have this test in two versions
first
def test_mailer
Mail::TestMailer.deliveries.clear
get '/owners/test'
e = Mail::TestMailer.deliveries.pop
puts e.to.to_s
end
second
def test_mailer
get '/owners/test'
Mail::TestMailer.deliveries.clear
e = Mail::TestMailer.deliveries.pop
puts e.to.to_s
end
In the second version this test fails with the error message NoMethodError: undefined method to' for nil:NilClass This makes sense to me. I clear the messages then ask for the last one which should be nil. However when I run the test on the first version the error is NameError: uninitialized constant OwnersControllerTest::Mail
So somehow the get method is causing the Mail object/module to be made available. I don't understand how it can do this. I don't know if this is a rack-test or padrino thing so am unsure what extra information to copy in here.
Add require 'mail' to your test helper.
The issue is explained here: https://github.com/padrino/padrino-framework/issues/1797

RSpec 3 acceptance testing approach without "its" blocks

I have a doubt about how to test a simple CSV importer without using the its(:...) clause.
In RSpec 2.x, my approach was to set the imported object as the subject of my spec, and then test each attribute in a its(...) block. It was an acceptance-like test, but it served me well, and I didn't want to unit test the library I used to do my CSV parsing, as it was really a trivial implementation, so I was ok with an end-to-end test.
Now, with RSpec 3, I can make this spec pass with transpec, but I read the explanation about why the its block has been removed and I think RSpec 3 is suggesting a different approach, right? So how would you test that?
I don't think a lot of ugly blocks like this
describe '#email' do
subject { super().email }
it { is_expected.to eq("john_doe#email.com") }
end
are any better than
its(:email) { should == "john.doe#email.com" }
as they do exactly the same thing.
I've read that you need to test "behaviour", but how about acceptance tests? What's the suggested way to go here?
Thanks!
From what I understand, Myron suggests using rspec-given for a one-liner rich test suite. Using this package, your tests will look something like this:
Given(:email) { subject.email }
context "sign up" do
When { subject.sign_up(email: "john.doe#email.com") }
Then { email == "john.doe#email.com" }
end
While the its functionality has been removed from rspec-core, it has been put into an includable gem, rspec-its.
https://github.com/rspec/rspec-its
I would just include this gem and keep writing tests the way you have been - I find them the most readable.
ps. Unrelated but I would also always use eq instead of == in specs :)

Rails -- use RSpec "get" and "response" methods in Rake task

I know RSpec has useful methods "get" and "response.should" to run integration tests - I want to know how I can use these (or other methods to achieve the same result) in a Rake task:
desc "Check all content items with type 0 and do something"
task :my_task => :environment do
ContentItem.where("content_type = ?", 0).each do |obj|
get "/my_path/"+obj.value
if (response has a certain html tag)
perform some action on obj
end
end
end
I know that I can't just run rspec methods like that, but this is effectively what I need to do, and I need to be able to process information returned when /my_path/obj.value is opened. Does anyone have any suggestions?
why do you need to go through the url to do this action with your ContentItem ? Why not just use the local obj and do stuff?
Basically it looks like you're mixing up view-code with model code here... the model's object should not depend on values in the html... if there is some information being figured out in the view... put it into a method on the ContentItem model and call that code either from the view... or from this rake task.
Edit:
Ok.... well if you really need to GET a URL - look into Ruby's Net::Http gem - that will literally fetch URLs. Rails doesn't do that as standard... even for local URLs.
You might then be able to use a parser such as hpricot or nokogiri to parse the results to find the tag you need.

Resources