Seems I want to do the opposite of what most people ask for when testing rails...
How to test FOR ActiveModel::MassAssignmentSecurity::Error. I get this already, just want to know how to make it make my test case green and not red.
current test:
describe Tag do
let(:tag) { FactoryGirl.create(:tag) }
subject { tag }
describe "when tag name is sent directly" do
before { tag = Tag.new(name: "New") } # <- what I want to raise the error
it { should_not be_valid } # <- doesn't make it to this line
end
end
How should I structure this as a proper test? I have tried:
lambda do
tag = Tag.new(name: "NEW")
end.should raise_error
But that doesn't work for me either, I get
Exception encountered: #<NoMethodError: undefined method `raise_error' for #<Class:0x00000102525c08>>
Gah, had to put it inside an it {}:
it do
lambda { tag = Tag.new(name: "NEW") }.should raise_error
end
This solved the problem for me
Related
I have the following code which rescues an exception, updates some status attributes on a model, and then re-raises the error to allow it to bubble up to the calling class.
foo.rb
class Foo
...
...
def bar
External::Service.new().bar # this raises ArgumentError (for example)
rescue ArgumentError => e
message = 'Foo: Wrong id passed`
report.update!(status: 'error')
raise e
end
end
In my specs I want to test that it updates the status, and also re-raises the error. Re-raising the error is fine to test, however I can't figure out a way to test updating message attribute as rspec just says an error was raised.
foo_spec.rb
RSpec.describe Foo do
describe '#bar' do
subject(:call_bar) { described_class.new().bar }
let(:report) { create(:report, status: 'generating') }
before do
allow(External::Service).to receive(:new)
.and_raise ArgumentError
end
it 're-raises the error' do
expect { call_bar }.to raise_error(ArgumentError) # works fine
end
it 'updates status' do
expect { call_bar }.to change(report, :status) # does not work as rspec just sees error
end
end
end
I've tried something along the lines of this as well but the issue is that the rescue in the spec is called instead of the one in the code being tested when the error is first raised. The spec will pass but no attributes will be updated so it's a false positive.
it 'updates status' do
call_bar
rescue
expect(report.reload.status).to eq 'error'
end
The following was the solution that worked best for me in the end, however I don't like having two expectations in the one it block so if there are any other solutions please let me know.
it 're-raises the error', :aggregate_failures do
expect { call_bar }.to raise_error(ArgumentError)
expect(report.reload.status).to eq('error')
end
Both expectations pass now, and the status is being properly updated (double checked by adding a binding.pry after the second expectation and checking the value).
The { call_bar }.to raise_error(ArgumentError) allows rails to call the method without actually raising the error, and the changes made persist while still inside the it block, so adding the second expectation here works.
I was checking mock documentation and i had read somewhere that I can use "..." as a parameter, but when I try to use it I receive an error.
enter image description here
This is the code that's returning this error:
require_relative '../../lib/random_joke'
describe JokeMessage do
let(:bot) { double }
let(:api) { double }
let(:message) { double }
it 'fires send_message' do
expect(bot).to receive(:api).and_return(api)
expect(api).to receive(:send_message).with(...)
described_class.new(bot: bot, message: message).send_response
end
end
Following the documentation "..." should work. I don't get what I'm doing wrong here.
I have a Selenium test that is supposed to verify that I'm unable to see a button link unless there is a certain amount of information present on a page such as a pagination link for example. Here is my Selenium assert statement:
def test_bottom_links
load_page('orgs/apache_software')
$driver.find_element(:xpath => "//a[#id='btn_orgs_see_all_projects']").element? == false
end
In my mind this makes sense to me but I receive this error when I run my test:
Selenium::WebDriver::Error::NoSuchElementError: Unable to locate element: {"method":"xpath","selector":"//a[#id='btn_orgs_see_all_projects']"}
The above error is what I want as a passing statement. I don't want Selenium to find this element because it should not be there.
I've also tried this and I get the same error:
$driver.find_element(:xpath => "//a[#id='btn_orgs_see_all_projects']").displayed? == false
I was wondering what the correct syntax should be to make this test pass. I've referred to these linksassertNotSomething and List of Selenium Methods. However, these don't have examples of how they are used so I was wondering how someone would write a test like the above. Thanks for any help offered.
Here's a simple boolean check method that should work.
boolean checkForElementPresence(By locator)
{
try {
driver.findElement(locator);
return true;
} catch (NoSuchElementException e) {
return false;
}
}
You can switch to $driver.find_elements that will return an array without raising an exception. Then you can check for the size of the array to make sure it is empty indicating that the element was not found.
I am stubbing a method like this:
User.stub_chain(:something).and_return(nil)
When I'm testing, I want this code to raise an error:
raise NameError if User.something.blank?
The problem is that User.something.blank? is not true, even though it should be stubbed with a nil value. User.something is actually
#[RSpec::Mocks::Mock:0x795359c #name=nil]
How do I fix this?
There's nothing wrong with your stub code or the code you're trying to test, as evidenced by the following passing test:
require 'spec_helper'
class User ; end
describe "" do
it "" do
User.stub_chain(:something).and_return(nil)
expect { raise NameError if User.something.blank? }.to raise_error(NameError)
end
end
You must have some problem elsewhere in your code.
I'd like to execute some action when Spock test fails. Specifically, take a screenshot. Is it possible? How to do it?
Create a listener class
class ExampleListener extends AbstractRunListener {
def void error(ErrorInfo error) {
println "Actual on error logic"
}
}
then add it to each specification using implementation of IGlobalExtension that is executed for each Spec
class GlobalSpecExtension implements IGlobalExtension {
#Override
void visitSpec(SpecInfo specInfo) {
specInfo.addListener(new ExampleListener())
}
}
and finally create file named org.spockframework.runtime.extension.IGlobalExtension in your META-INF/services directory (typically it will be under src/test/resources if you are using Maven) with the full name of your IGlobalExtension implementation e.g.
com.example.tests.GlobalSpecExtension
The best way to achieve this is to write a (global or annotation-driven) Spock extension that implements and registers an AbstractRunListener. For an example, see OptimizeRunOrderExtension. For how to register a global extension, see the IGlobalExtension descriptor.
There isn't much documentation on extensions because the APIs are still subject to change. If you want to play it safe (and can live with some restrictions), you can implement a JUnit Rule instead.
One problem that you may encounter in both cases is that they don't provide access to the current spec instance. If you need this, you may have to use both an AbstractRunListener (to be notified of the failure) and an IMethodInterceptor (to get hold of the spec instance), both registered by the same extension. (Shouldn't be this hard, but that's what's currently there.)
I've managed to do it this way:
class ExampleTest extends GebSpec{
static boolean success = false
def setup(){
success = false
}
def cleanup(){
assert success == true, someAction()
}
def someAction(){
}
def "TestCase"(){
expect:
/*What you expect here*/
(success = true) != null
}
}
Before each test case "success" is set to false by the setup() method.
At the end of each test case you add the "(success = true) != null" statement. Therefore "success" will only be true if the test case has passed.
After each test case the cleanup() method will verify if "success" is true. If it isn't the method someAction() will be called.
I can't upvote or comment on user3074543's answer, but it's simpler than creating an extension. I want easy. So I shortened user*'s a little (I don't mean the 1-line methods). You can make the logic simpler by recording failure instead of success, and reduce typing with a done() helper.
class Test extends spock.lang.Specification {
def fail
def setup(){ fail = true }
def done(){ !(fail = false) }
def cleanup(){ fail && doStuffWhenFail() }
def 'test things'(){
expect:
stuff
done()
}
}