The scenario is as follows. My Order model has an after_create that contacts a remote payment gateway to retrieve a payment URL. In my Cucumber tests I don't want to perform this action, but return an arbitrary URL. My current cucumber tests looks like this:
Given there is a product "Product X"
When I enter my credentials
And I click "Order Now"
Then I should be redirected to "arbitrary url"
The problem is where/how do I make sure that my order model sets the url correctly and does not contact the remote payment gateway?
The wiki also has some tips on stubbing.
In features/support/env.rb I monkey-patched my Order model to set the arbitrary URL. This could possible be done with Mocha or something else as well, but there is not point in this case.
In my steps I can check the response for the correct redirect like this:
Then /^I should be redirected to the payment gateway$/ do
response.status.should eql("302 Found")
response.location.should eql(Order.last.payment_url)
end
Hope this helps for others as well. I'd still like to know if there's a better/cleaner way of achieving this goal.
If I understand what you are trying to do correctly, have a look at FakeWeb.
Related
I am doing integration testing using Cucumber. In my ApplicationController, I have a method called current_user that provides the current user object. I use this object to add items to a redis database:
$redis.sadd("cart#current_user.id}", [1,5,2])
In my Cucumber steps I test this functionality:
Then /^the redis database should have "(.+)" item ids/ do |count|
expect($redis.smembers("cart#{current_user.id}").count).to eq count.to_i
end
However, it is my understanding that Cucumber does not have access to controller methods, even if they are under ApplicationController, and therefore I cannot user the current_user method the way I would in my controllers.
What I am doing now is since I am testing features, there is only one user in the database so the current_user.id will always be 1, but if I start adding more users this may not work nicely.
Is there a workaround for this?
Your not really using Cucumber as intended here. What you are doing is testing how your application currently works, but really Cukes is best used to specify what your application does and why its important.
Applying more appropriate usage to your current problem leads to the following questions
What is the reason for storing the ids in Redis?
What benefit does the customer get by having these id's stored?
Taking a wild guess you might be saving a basket so that if the user logs out, their basket would still be populated when they come back. Then your scenario would be something like
Scenario: Remember products in basket
Given I am registered
And I am logged in
When I put some products in my basket
And I log out
And I log in again
Then my basket should still have some products in it
Notice how the scenario is all about WHAT you are doing and WHY its important but reveals nothing about HOW this is going to be done. This is a really good way to critique scenarios. Scenarios that contain HOW stuff are going to be harder to write and much harder to maintain. Anyhow enough of that :)
Now you can use standard cucumber stuff like assigned the user to a variable in one step e.g. #i = create_registered_user and then using that user in the other steps e.g. login as: #i
Note that we don't look at the database, only at what the user sees, and we don't reveal anything about HOW this functionality works in the scenario.
If you want to write tests (rather than scenarios) that do reveal how functionality works and do look at databases for results then I'd suggest that rspec would be better suited for this.
do you have a step to login? if so, you can change it a little so you can control which user logs in:
Given "john_doe" logs in to the app
Then you can search by username and do the login in your step. You can do the same on this step:
Then /^the redis database should have "(.+)" item ids/ do |count|
something like
Then /^the redis database should have "(.+)" item ids for user "(.*)"/ do |count, user_name|
user = User.find_by(username: user_name)
expect($redis.smembers("cart#{user.id}").count).to eq count.to_i
end
So, while I like Cucumber for its readability for integration testing and its ability to give us documentation we can share with the client easily, I also find it cumbersome from the development and testing speed standpoints.
I got to thinking today that if I could just print out messages to the RSpec documentation format that documented the "steps", then I could easily replicate the business features of Gherkin but in the simplicity of RSpec. But I can't figure out a way to do this.
What I want is to take something like this:
describe "Login and Authorization Tests" do
before (:each) do
docs "Given I have a user"
#user = FactoryGirl.create(:user)
end
it "A user can belong to one or more user roles" do
docs "And the user has no roles assigned"
#user.roles.length.should eq 0
docs "When I add two roles"
#user.roles << FactoryGirl.create(:role)
#user.roles << FactoryGirl.create(:role)
#user.reload
docs "Then the user should have two roles assigned"
#user.roles.length.should eq 2
end
end
and get this in the documentation
User
Login and Authorization Tests
A user can belong to one or more user roles
Given I have a user
And the user has no roles assigned"
When I add two roles
Then the user should have two roles assigned
Note that the message from "before" shows up in the docs too, and would show up with that line in every test below it.
I'm thinking of forking to see if I can add something like this in, but before I did that, does anyone know if something like this is possible already?
I also contacted the RSpec dev team and someone there posted this add-on called rspec-longrun that could be repurposed for this. I haven't had a chance to try it yet, but it looks very promising. And as a bonus, it includes timing information.
rspec-longrun: https://github.com/mdub/rspec-longrun
Thread on rspec-dev: https://github.com/rspec/rspec-dev/issues/34
You can try Steak, but the difference is not that big.
Or you can try Cucumber with RSpec matchers. In last case, you can fork RSpec and add a new formatter
I want to write tests for destroying objects on ruby on rails with cucumber and capybara and rack_test.
I think that I should write a scenario like:
When I try to delete X object
Then I should be on the index page for ...
And I should see "Object deleted successfully"
I don't want to test it by clicking on a link on the index page or the show/edit page because I think I should be testing something different than purely the delete action.
Also, clicking on a link means that the interface is ok, but what happens if someone send a request to delete an object he can't and the interface doesn't show the link?
Given this, I need to send a delete request in the first step and then inspect the response to that request.
I've find troubles trying to do that because when I implement the first step as delete url then when I try page.should have_contain(text) I get the error No response yet. Request a page first. (Rack::Test::Error)
What do you think about it?
check out how-do-you-post-to-a-url-in-capybara (answering another question) but there a several examples of how to use visit in a capybara test like
visit "item/:id/delete"
I'm trying to create a cucumber scenario that checks to see if elements are loaded for an 'edit posting' page. My trouble, however, is that I don't know how to create a path that will direct it to the page.
The general path is something like: /posting/id/edit
i.e. /posting/11/edit
Here is my posting.feature scenario
# Editing existing post
Scenario: Saving the edits to an existing post
Given I am logged in
Given there is a posting
Given I am on the edit posting page
When I fill in "posting_title" with "blah"
And I fill in "posting_location" with "blegh"
When I press "Update posting"
Then I should see "Posting was successfully updated."
I dabbled around with some Factory Girl stuff, but I don't have the knowledge to use it appropriately (if it offers a solution), and wasn't able to find a relevant example.
I've also seen a lot of suggestions with regards to 'Pickle', but if possible I'd like to avoid that route to keep things simple seeing as I have very limited experience. Thanks!
Is there a link on your website that would take someone to the edit page? Then you could do something like:
Given I am on the homepage
And I follow "Posts"
And I follow "Edit"
This assumes that there is a link on your homepage whose text is Posts, and then another one in the resulting page called Edit. This is the best way to accomplish this, because there should be a direct route to whatever page you are testing. Those steps are also provided in web_steps.rb
You could also make a custom step like you did there with Given I am on the edit posting page and the code would be something like:
Given /^I am on the edit posting page$/ do
visit("/posting/11/edit")
end
Which you of course could also generalize like I am on the edit posting page for posting 11. But in general, cucumber tests are acceptance tests, which means not bypassing things like this. You should have a link to the edit page that can be clicked.
I came up with a solution, but I am not sure of its validity in terms of how clean it is. I ended up using Factory Girl (installed the gem).
I kept my scenario the same.
Under features/step_definitions I created posting_steps.rb
Given /^there is a posting$/ do
Factory(:posting)
end
Under features/support I created a file factories.rb with the following inside:
Factory.define :posting do |f|
f.association :user
f.title 'blah'
f.location 'Some place'
end
In my paths.rb I used
when /the edit posting page/
edit_posting_path(Posting.first)
How it works (or at least how I think it works) is that as
Given there is a posting
is executed, the posting_step.rb is invoked (Factory(:posting) is basically Factory.create(:posting)), which in turn uses the factory definition I created in factories.rb. This leads to an instance of a posting being created.
Then in my paths.rb
when /the edit posting page/
edit_posting_path(Posting.first)
gets passed the id from the instance, to ultimately get a path that could resemble /posting/1/edit , and the test continues on its way!
If there are any corrections to be made, please let me know as I am just learning the ropes.
Hopefully this will help other newbies out there!
I use RoR 3 and i guess something changed in controller's tests.
There is no
def test_should_create_post
but
test "should create user" do
...
end
Is there any decription how is that mapping etc? Because i dont get it.
And second thing. How to program (what assertion) use to test login?
so the test "something here" style is rails way of helping us out. It is fundamentally the same as def test_as_you_want but they helped us out by taking away those nasty '_(underscores)' and wrapping the actual test wording in a string. This change came back, phew... maybe 2.3.x. that fact has to be checked but at least a year and a half ago.
Your second thing is a little more harder to answer man. What plugin are you using, or are you one of those guys who are writing their own auth system?
Either way, check out how the 'famous' auth plugins do it. from Restful Auth to Devise, basically you want test that you can:
Signup for the User account
all of your confirmation emails are sent etc..
Most of these 'cheat' or take the easy way out by passing a helper called signed_in users(:one) for instance. Assuming you are cool and using fixtures.
Basically here is what a helper method looks like if your Auth plugin/gem doesn't have one, like Clearance which didn't have it when i was first writing my tests... not sure if it has it now but it sheds light on how it should look. Notice I've commented out Restful Auth and how he/they did it:
#login user
def login_user(user = users(:one))
#Restful Auth Example
# #request.session[:user_id] = user ? users(user).id : nil
# Clearance
#controller.class_eval { attr_accessor :current_user }
#controller.current_user = user
return user
end
Actually i think i stole this from their shoulda login helper... that's probably what i did. Either way it shows you how to fake login a user.
Now when you are testing, just pass this login_user method to your test when you need a user logged in and start testing the rest of the method without worrying about them actually signing in. That is what the plugin is supposed to do and the 1000 people following it on github would scream if it didn't at least LOG that guy in.
cheers