I'm taking over an application. It has no testing.
I'm looking for the bare minimum integration testing I can start with to have at least something to yell at me if I break something.
I was thinking:
Load small sql dump
Given a list of URL
Request URL and ensure a successful response
Searching for something like this has been fruitless.
Any pointers to something like this?
Or, how would you implement something like this quick 'n dirty to get beginning coverage?
I used basic rspec integration testing:
# login factories, etc
context "Login" do
it "works" do
visit '/'
page.should have_content "Login: "
fill_in 'login', :with => #user.login
click_button 'Login'
page.should have_content #user.name
end
end
By creating the integration test, it forced me to make the necessary factories, so I could get an idea of the coupling for each page. Bonus: it made it easier to split the models up later when I added unit testing.
Related
I had a question about within dryness in Capybara here. Tom answered perfectly and in his answer he mentioned:
Feature tests should be for testing larger behaviours in the system.
Is there a difference between a feature spec and a view spec in Ruby on Rails? If possible explain it with some example please.
Thank you.
Yes, feature and view specs are quite different. The first is a full integration test and the second tests a view in isolation.
A feature spec uses a headless browser to test the entire system from the outside just like a user uses it. It exercises code, database, views and Javascript too, if you use the right headless browser and turn on Javascript.
Unlike other types of rspec-rails spec, feature specs are defined with the feature and scenario methods.
Feature specs, and only feature specs, use all of Capybara's functionality, including visit, methods like fill_in and click_button, and matchers like have_text.
There are plenty of examples in the rspec-rails documentation for feature specs. Here's a quick one:
feature "Questions" do
scenario "User posts a question" do
visit "/questions/ask"
fill_in "title" with "Is there any difference between a feature spec and a view spec?"
fill_in "question" with "I had a question ..."
click_button "Post Your Question"
expect(page).to have_text "Is there any difference between a feature spec and a view spec?"
expect(page).to have_text "I had a question"
end
end
A view spec just renders a view in isolation, with template variables provided by the test rather than by controllers.
Like other types of rspec-rails spec, view specs are defined with the describe and it methods. One assigns template variables with assign, renders the view with render and gets the results with rendered.
The only Capybara functionality used in view specs is the matchers, like have_text.
There are plenty of examples in the rspec-rails documentation of view specs. Here's a quick one:
describe "questions/show" do
it "displays the question" do
assign :title, "Is there any difference between a feature spec and a view spec?"
assign :question, "I had a question"
render
expect(rendered).to match /Is there any difference between a feature spec and a view spec\?/
expect(rendered).to match /I had a question/
end
end
I'm trying to set up some feature specs before I get into refactoring some of my company's old code. It's kind of an unconventional setup, but I was able to figure out enough about test doubles to bypass the authentication enough to get started. One problem I'm still having is that some of the instance variables set in these methods I'm bypassing are expected by the view, so I get undefined method for nil:NilClass errors. I would like to get the specs running before I make any changes to the program code. In this case, I could easily just move the particular instance variable to another method. But I'm sure more situations like this will come up. Here's the example I'm currently working on:
def security_level
#right_now = Time.now
#
# other code that wont work without
# connecting to a remote authentication
# server
#
end
Then in my spec:
feature 'Navigation' do
before(:each) do
allow_any_instance_of(ApplicationController).to receive(:security_level).and_return(nil)
end
scenario 'is possible' do
visit root_path
expect(page.has_content?('Quick Stats'))
end
end
Here's the error, coming from #right_now.year in the view
Failure/Error: visit root_path
NoMethodError:
undefined method `year' for nil:NilClass
# ./common/views/layouts/bootstrap/layout.haml:63
EDIT: Is there a way to set instance variables on the controller from within a feature spec?
There's no easy way to accomplish what you want.
The feature spec is handled mostly by Capybara, not RSpec. Capybara runs the majority of the browser / rails server behavior in an external process. This make it inaccessible from RSpec's point-of-view. Thus you cannot use stubs / doubles in this manner.
Feature specs are largely meant to be end-to-end acceptance tests. The idea is to exercise your system as those who would use your system do. Generally, in these types of specs you perform various "workflows". This means, having the spec, log a user in, navigate to particular pages, filling forms, clicking buttons and links. You then generally make your expectations on what you see in the view.
This means your spec would look more like:
feature 'Navigation' do
let(:regular_user) { User.create!(name: 'A Regular User') }
def sign_in(a_user)
visit sign_in_url
# fill out form
click_button 'Sign In'
end
before(:each) do
sign_in(regular_user)
end
scenario 'is possible' do
visit root_path
expect(page.has_content?('Quick Stats'))
end
end
https://github.com/per-garden/fakeldap may provide enough ldap functionality for your feature tests.
I'm having terrible trouble getting Poltergeist and RSpec to play together nicely.
I've written the following test:
it "allows the trainer to view a runner" do
visit '/'
all(:xpath,'//a[#id="get-started"]').first.click
fill_in :name, with: "New Admin"
fill_in :email, with: "admin#test.org"
fill_in :password, with: "letmein"
fill_in :password_confirmation, with: "letmein"
all(:xpath,'//input[#id="get-started-submit"]').first.click
#runner_1 = FactoryGirl.create(:runner, name: "Axel", email: "axel#test.org")
visit '/runners/axel'
debugger
Effectively, what the above is doing is registering 'New Admin' with the password, 'letmein', then trying to view the runner profile page for 'Axel'.
Where the debugger interrupts, I can see that #runner_1 (Axel) has been created:
Runner.friendly.find('axel')
>> #<Runner id: 2, email: "axel.manzano#hotmail.fr",........>
However, when trying to visit '/runners/axel', Poltergeist reports:
ActiveRecord::RecordNotFound
It's not an issue with routes, or anything like that.
Having explored this bug a little further, in fact, it seems anything created in the test file doesn't actually get set up in the environment that Poltergeist is accessing.
I can't seem to understand why. Any help greatly appreciated.
Chances are, you are using "transactional fixtures" in rspec. This means that each test run in a database transaction, which is rolled back at the end of test, so that each test has a clean database.
Other threads/programs can not see what is going on in the transaction. Poltergeist runs the server in the separate thread, which means that it can not see anything that is written to the database in rspec (although it can be accessed directly from the rspec code).
There is a description of this phenomenon on the capybara homepage. The solution is to disable the transactional feature in rspec-rails and use something like DatabaseCleaner to reset the database after a test.
This will work, but unfortunately truncating or deleting the database contents is somewhat slower than they would with the transactional approach - this is why the tranasactions are the default in the first place.
My Signup database has an index on email with a unique requirement. This is great, but the problem is that I'm trying to run integration tests, and every time I go rake rspec spec/features/...rb, unless I did rake db:test:purge, rake db:test:prepare first, it runs into the unique problem and refuses to run. How do I streamline this?
From the code below, you can see that every time I'm running the test, I'm creating a set of seed data with my before(:all), but since the seed data is always the same, this is driving the uniqueness error.
I'm happy to just put this seed data elsewhere or create it some other way, as long as my test suite is still able to run using this seed data.
describe "how requests should flow" do
before(:all) do
#signup_dd = Signup.create(email:"example#example.com")
end
it "should have 2 inventories and 2 signups to start" do
Signup.count.should == 1
end
describe "request creation" do
before do
Signup.find_by_id(#signup_dd)
visit '/requests/new'
save_and_open_page
fill_in '#borrow__1', :with => 1
click_button
end
it "should affect new Requests and Borrows" do
...
end
end
end
There are two ways to fix this:
Remove the (:all) from the before block. RSpec will execute the before block for each test. It will then undo itself after each test. This is really what you want as it ensures changes made by each test do not bleed into other tests. This is usually the recommended approach.
Keep the (:all), but then add a (:after) block to undo your changes. With the :all argument, the before block is only executed once instead of every time. However, it doesn't automatically undo itself like :each, so the :after block becomes necessary. It is up to you, however, to figure out what needs to go in there. In your example, for instance, it might be:
after(:all) do
Signup.delete_all # Or whatever undoes what the before block did
end
See this blog post regarding the use of the two techniques.
when you use before(:all), you need use after(:all) to clean up the data you created in before(:all)
I am testing a website that has both a vertical login form on the right, and a horizontal form on the bottom. Both have identically named "email" and "password" fields. So I am employing capybara's within to scope for the fields I'm interested in. The interesting bits of the web form look like this:
I have two separate projects, in which I am experimenting with sauce labs for automation. The first project is the capybara-only example, modified to test the page shown above. The second project is a cucumber implementation of the exact same tests. These are very simple, one-time hard-coded tests, just to get a proof of concept of the two techniques.
Here is the interesting bit of the capybara-only example:
within(:css, ".right-container.login-form") do
fill_in 'email', :with => "greg.gauthier+#{generate_email_suffix}#website.com"
fill_in 'password', :with => 'l33tP#$$w0rd'
click_button 'Submit'
end
Here is the interesting bit of the cucumber step_definition:
When(/^The user enters his information$/) do
within(:css, ".right-container.login-form") do #the page has duplicate forms
fill_in 'email', :with => "greg.gauthier+#{generate_email_suffix}#website.com"
fill_in 'password', :with => 'l33tP#$$w0rd'
click_button 'Submit'
end
end
When I run the capybara-only version, everything works great. The form gets filled in, and the email confirmation gets sent.
However, when I run the cucumber version, I get this error:
Unable to find css ".right-container.login-form" (Capybara::ElementNotFound)
How can this be? It's the exact same page, the exact same capybara method (within, using the :css selector), and the exact same test code. What am I not getting (aside from the fact that I'm probably cuking it wrong)?
Oh, here's what the require list looks like in the sauce_helper:
Capybara-only version:
require "sauce"
require "sauce/capybara"
require 'capybara/rails'
require 'capybara/rspec'
Cucumber version:
require "sauce"
require "sauce/capybara"
require "sauce/cucumber"
Do I maybe need to include the extra capybara gems in the cucumber version?
Ok, I'm embarrassed to admit this, but the reason for this question was ignorance of webdriver behavior, at least as it works with Capybara/Cucumber.
The test in question is the second scenario in a set of scenarios. Apparently, selenium resets the browser to blank, between each scenario. So, test one works perfectly, but test two, three, and so forth fail on ElementNotFound because, of course... the page in question was not even loaded.
I added a Before hook to a hooks.rb file in my support path, and now it's working.
Apologies for cluttering the question stream...
Try adding require 'capybara' prior to require 'sauce' in your "Cumcumber Version"
From sauce GitHub
## In your test or spec helper
require "capybara"
require "sauce/capybara"
Reasoning within calls find. Sauce checks to see if a find method is defined an if so then it creates it's own alias called base_find if not it uses it's own find method which is not the same as capybara's. I think they may have something to do with your issue since it is using sauce's find method.
Not positive this is the answer but you can always give it a shot.