Proper way of interacting with a RESTful web service using Cucumber - ruby-on-rails

I'm using Cucumber to test end to end application behavior in my Rails-based web service. I currently have a Scenario outline that looks like the following (making up a hypothetical scenario here of creating a user with another user):
Scenario Outline: Create a user with another user
Given I want to create a user as a user "<user>"
When I create a user with name "<name>"
And the user's age is "<age>"
Then then the response should be "<response>"
Scenarios: Create user with 3 args
| user | name | age | response |
| bob | joe | 25 | <some_xml_response> |
I'm having a bit of difficulty figuring out how I should write the step definitions for this outline. Basically I'm currently concatenating an XML blob (for name+age) and need to do something similar to how rspec uses :post to post to a controller and see a response. My step definitions currently look like:
Given /^I want to create a user with another user "([^"]*)"$/ do |user|
#reqxml << "<user><creator>#{user}</creator>"
end
When /^I create a user with name "([^"]*)"$/ do |name|
#reqxml << "<name>#{name}</name>
end
And /^the user's age is "([^"]*)"$/ do |age|
#reqxml << "<age>#{age}</age>"
end
Then /^then the response should be "([^"]*)"$/ do |response|
# ?? not sure if I can use rspec :post here?
end
What's the best way to improve this Cucumber outline? I have a lot more I want to test. In rspec this is rather straight forward and maybe the right answer is to stick this in RSpec. But I really want to get better use out of Cucumber and have a better "bigger" picture with end to end user story testing such as the one above.

I use capybara/rspec/cucumber and pickle in concert to minimize the actual amount of step definitions I have to write, and generally it gets the job done.
Pickle makes steps such as "Given a model exists with" automatically available, capybara's web steps take care of the browser automation like "goto route" or and also provides css and xpath functions to verify the output with steps such as "should contain" etc....
This way you can test your whole stack, which is kind of the point of cucumber
My personal opinion is that cucumber is a domain specific language and as such, may not always be able to describe what you are trying to do in a concise fashion. (think complex object relationships). If you have non-technical users that need to read your tests then its pretty good, but it doesn't beat plain rspec otherwise.
Your cucumber steps are a bit askew too. "Given" describes the pre-existing conditions, not you goals. "When" should describe the actions you take to create a user, and "Then" should verify that the actions taken effected your goal.

While I have seen this being done, testing APIs with cucumber brings with it a special kind of pain.
If your API is truly restful, writing "controller" specs for the integration should actually be fine. Or, if you really want "proper" full-stack integration testing: Do yourself a favor and use capybara/rspec instead of Cucumber...

Related

Rails: Cucumber and Application Controller Methods

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

What tests should I write to test user validations for an admin-only feature?

I created an RSpec spec to test if a POST #create action works properly:
describe "POST #create" do
it "creates a career" do
expect {
post "/careers/", career: attributes_for(:career)
}.to change(Career, :count).by 1
end
end
The above code works correctly. The issue happens when I create another test to allow only users with roles of "admin". Do I need to create a new user, log them in, and then run the above test? Do I need to do this for all future tests which have a restriction based on the user's role?
Is there another way to do this type of testing? 1) Just test if the create method works, and 2) only allow Users with "admin" role access the GET #new and POST #create methods?
When your feature is fully developed you'll want to have the following tests:
one happy-path test in which an admin creates a career
one in which a non-admin tries to create a career but is prevented from doing so
possibly another in which a not-logged-in user tries to create a career but is prevented from doing so (whether you want this depends on whether you have to write different code to handle non-logged-in and logged-in non-admin users), and
possibly other tests of different scenarios in which an admin creates a career.
This idea of having one complete, happy-path test is one of the most fundamental patterns in testing, but I'm not aware that it has a name, other than being implied by the term "happy path".
It looks like you're doing TDD. Great! To get from where you are now to the above list of tests, the next test to write is the one where the non-logged-in user is prevented from creating a career. To make both tests pass at the same time you'll need to change the first test to log in an admin. And if you need more tests of successfully creating a career (bullet 4), yes, you'll need to log in an admin in those too.
Side notes:
Unless you already have it, I'd write your happy-path spec not as a controller spec but as a feature spec (an acceptance test), so that you specify the important parts of the UI and integration-test the entire stack. Your failed-authentication specs might work as controller specs, although you might decide you need to acceptance-test the UI when a user doesn't have permissions for at least one of those scenarios.
I really don't like that expect {}.to change syntax. It prevents you from making any other expectations on the result of posting. In your example I would want to expect that the HTTP response status is 200 (response.should be_success). As I said, though, my first spec would be a feature spec, not a controller spec.
So, this is an interesting question. Yes, you should definitely (IMO) test authentication separately from the target method/action. Each of these constitutes a unit of functionality and should be tested as such.
In my current project, I'm favoring POROs (I often keep them in a directory called 'managers' although I know many people prefer to call them 'services') for all sorts of things because it lets me isolate functionality and test it independently. So, I might end up with something like:
# controllers/foo_controller.rb
class FooController < ApplicationController
before_action :authenticate
def create
#results = FooManager.create(params)
redirect_to (#results[:success] ? my_happy_path : my_sad_path)
end
def authenticate
redirect_to unauthorized_path unless AuthenticationManager.authenticate(params, request)
end
end
# managers/foo_manager.rb
class FooManager
class << self
def create(params)
# do a bunch of great stuff and return a hash (perhaps a
# HashWithIndifferentAccess, if you like) which will
# allow for evaluation of #results[:success] back in the
# controller.
end
end
end
# managers/authentication_manager.rb
class AuthenticationManager
class << self
def authenticate(params, request)
# do a bunch of great stuff and return a boolean
end
end
end
With an approach like this, I can very easily test FooManager.create and AuthenticationManager.authenticate (as well as FooController.create and FooController.authenticate routing) all independently. Hooray!
Now, whether your authentication framework or controller method is behaving correctly at a unit level, as Dave points out very well, is a separate issue from whether your entire system is behaving as expected. I'm with him on having high-level integration tests so you're clear about what 'done' looks like and you know when to holler 'ship it!'

rspec expect condition OR condition

How do I write rspec tests defensively, so that in a scenario at least one expectation must be met yet the failure of others is accepted? (without the input changing). AND is easy enough by listing multiple expectations, but how is OR expressed?
As an example, a user has many posts, and user Bob hacks a form so that when he submits his create post form it sends the id of user Dunc. Currently the application ignores the passed Dunc id, and uses Bob's id as Bob is creating the post. So we could test that the newly created Post has Bob's user_id. However, if in future the code is refactored so that it returns an error message instead of assuming Bob's id, that test would wrongly fail. I want to test the intent, not the implementation.
So i need to test that either no post is created, or that if one is created, its for Bob.
This example is so simple it can be solved by testing
expect { run }.not_to change( Post.where(user_id: #other_user.id), :count )
However I'm looking for the general solution, in more complex cases there can be many conditions. How is "OR" achieved in Rspec? (or is it not possible?)
I don't think it is possible.
I do think you are mistaken when you say that you would be testing implementation, instead of intent in your example.
When you write a test, you test whether what comes out matches your expectation.
Creating a user is something completely different than returning error messages.
In my opinion it would be strange to say: when I do this, I expect this, or that, or that, or that to happen.
In my opinion you should write one test, that tests whether a user is created when you send the correct parameters, and another test that deals with what happens when a user tries to send illegal parameters.

Add extra documentation to rspec output

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

Best BDD practices for designing Cucumber scenarios for forms

Lets say you have a form which creates a new user.
How do you write your Cucumber scenario?
1.)
Given I am logged in as admin
When I create a new user
Then I should see "Successfully created user"
2.)
Given I am logged in as admin
When I go to Create new user
And I fill in "Name" with "Name111"
And I fill in "Password" with "Password111"
And I press "Create new user"
Then I should see "Successfully created user"
If you choose 1.) where do you document the requirements for a User (A user should have a name and a password). I see that BDD is about behavior but at some point you and the stakeholder have to specify which properties a user should have, don't you?
I'm very new to BDD so I appreciate any advise...
You should read Imperative vs Declarative Scenarios.
--Aslak. Creator of Cucumber.
The scenarios you've written are fairly low level. Unless you're actually producing secure login functionality to sell, I'd stick to the happy case and unit / manual test the rest. If you don't, you'll create so many scenarios it'll be a maintenance nightmare.
Find out what differentiates the product you're creating from all the similar products, then target that as the value of the scenario. Then it will look like this:
Given Fred is logged in
When Fred <does something>
Then Fred should <get some really differentiating value>
And <something else happens>
Stick to the really high-level capabilities, rather than low-level form-based steps. For instance:
Given there is already a question on BDD and Cucumber
Given Peyote is logged in
When Peyote proposes a question on BDD and Cucumber
Then Peyote should see other questions on BDD and Cucumber.
There's a concept called the "Page Paradigm", in which you create a class with all the low-level steps that the page or screen can perform. You can then call those low-level steps on the page from within the higher-level Cucumber step fixtures.
Your business will be much more engaged with scenarios like this. The main purpose of BDD is not to produce automated tests, but to have conversations around the scenarios so that you can find out where you're going wrong and what other options you could consider before you go to the trouble of implementing the code. Automated tests are a nice by-product.
The conversations, and the learning you get by talking through them, are the things which make BDD different from ATDD (Acceptance Test Driven Development). That's why we use language like Example, Scenario, Given, When, Then, Context, Event, Outcome instead of Test, SetUp, TearDown, Act, Arrange, Assert - so we can talk about these with business, BAs and testers in the same language.
See Dan North's article on Deliberate Discovery and the rest of his blog for more, and good luck with the BDD!
Either one will work. With #1 you would create a step to handel the filling in of the form. I prefer a hybrid of #1 and #2 because I use Scenario Outlines alot for example:
Background:
Given the following users exist:
| email | password |
| test#example.com | testpassword23 |
| test2#example.com | notthistime |
| test3#example.com | welcomeback |
#login #authentication
Scenario Outline: Authentication
Given I am on the new user session page
When I login with "<s_email>" and "<s_password>"
And I press "Login"
Then I should see "<s_message>"
Examples:
| s_email | s_password | s_message |
| test#example.com | testpassword23 | Signed in successfully |
| test2#example.com | itriedreallyhard | Invalid email or password. |
| teOst#example.com | testpassword23 | Invalid email or password. |

Resources