Jumping on the Rails Pt.5 – Veggie validationsJuly 23, 2009
So if you’ve been following along from the other parts of the series, you’d remember that we have been building a project story builder using cucumber and BDD. So far we have learnt how to use the process to flesh out functionality from the outside in and generate the basic functionality for our application, this part of the series will focus on saving our project and validating its data using the same process.
Although it may seem that we haven’t covered much ground in the last two parts, as we build up on our steps, the less we will have to do later. You’ll see what I mean shortly.
So lets move on to our next scenario, our projects needs to be able to save & have its data validated, we’ll start with the below scenario.
Scenario: When submitting the form the user must be alerted if the title is not present Given an activated user logged in as 'reggie' When I click new project And fill in the new project form without a title And submit the form Then the project will not be save And a validation message will be displayed
So we’ll add this snippet to our
So our first step to implement is filling in the form without a title, so lets get that step out the way.
When /^fill in the new project form without a title$/ do fill_in 'project_description', :with => 'This project will help us manage stuff' fill_in 'project_aim', :with => 'To provide a service' end
Strange, our test passed, why is that? Well if you remember from the last post, we didn’t implement the save functionality & we haven’t got any validation in place. Either way our tests are passing, so lets move on to the next.
When /^submit the form$/ do submit_form 'new_project' end
Again our tests are passing but that ok, it’s what we expect, we have already implemented the submission process. So we can happily move on to the next step.
Then /^a flash the message will be displayed$/ do flash.should contain "A project must have a title" end
Aha, our first failure, we haven’t implemented a validation message yet, so lets open up our project controller spec and create a few specs.
Here’s what we want:
- It should save if it is valid
- It should not save the project if it is not valid
- It should display a validation error if no title is present
Here’s a rough outline of what we need to spec out
require File.dirname(__FILE__) + '/../spec_helper' describe ProjectsController do it "should save if it is valid" it "should not save" it "should display a validation error" end
If you notice there’s alot of repeation here, as we love being DRY, well restructure this a little
- a valid project
- should save if it is valid
- an invalid project
- should not save
- It should display a validation error
Nice, now that reads better & we have a layout for our specs, lets see how this effects our specs layout.
describe ProjectsController do describe "POST create" context "a valid project" it "should save" end context "an invalid project" do it "should not save" it "should display a validation error" end end end
So our project controller spec now looks like the code above, as we can see, our specs are a lot more expressive in this fashion and allow others to quickly understand what a tests is supposed to do.
describe ProjectsController do describe "POST, create" do before(:each) do @project = mock_model(Project, :title=>"A project", :null_object=>true).as_new_record Project.stub!(:new).and_return @project end context "a valid project" do before(:each) do @project.stub!(:save).and_return true post :create end it "should save" do @project.save.should eql true end it "should display a success flash message" do flash.should contain "You have successfully created your new project" end end context "title is not present" do it "should not save the story" end end end
For those that didn’t notice we’ve refactored our controller spec a little, firstly we’ve added a project mock and stubbed out its call to new. We’ve also added a spec to make sure that we can save our project, this passes automatically seeing as we implemented this code earlier via step definitions.
Now lets create our failing spec:
context "an invalid project" do before(:each) do @project.stub!(:title).and_return nil post :create end it "should not save the story" do @project.save.should eql false end end
Here we basically make sure that we don’t get redirected to the projects info page, as it should not be save & we render the form again.
Now we have our condition to save our project, lets setup our validation we still need to get this passing, we need to add validation to our model, so lets open that and add the following:
Doing so makes our test pass, now on to our next step, we’ll come back to our spec in a sec.
Now we still have a few more steps to run through, these should be pretty simple though.
Then /^a validation message will be displayed$/ do response.should have_selector(:li, :content => "Title can't be blank") end
To fix this we won’t bother with any spec’s, we’ll just add the following snippet to our new projects form:
<%= error_messages_for :project %>
All our scenario’s are now all passing and we can happily move on. Ok, now your probably wondering, hell what was all that effort for, we only setup validation for the title but we created crap loads of code.
It’s true, we setup quite a bit in the last post but in this one, we’ll see how much of a benefit that little bit of leg work was in a sec. Lets get a little ambitious and add to scenario to our projects.feature file.
Scenario: When submitting the form the user must be alerted if the description is not present Given an activated user logged in as 'reggie' When I visit the home page And I click new project And fill in the new project form without a description And submit the form Then a validation message will be displayed say 'Description can't be blank' Scenario: When submitting the form the user must be alerted if the aim is not present Given an activated user logged in as 'reggie' When I visit the home page And I click new project And fill in the new project form without a aim And submit the form Then a validation message will be displayed say 'Aim can't be blank'
Running cucumber quickly we notice that we only have 5 steps to complete our scenario’s. Now if you remember we already created a step to cover validation of messages, wouldn’t it be nice if we could use that same step to cover our the other validation messages.
As we know we going to need this step in a second & our tests are running, lets do a little refactor our previous scenario so that it will cover the other validation messages as well. Change the last step to match the snippet below.
Then a validation message will be displayed say 'Title can't be blank'
Now we refactor its associated step to look like the following:
Then /^a validation message will be displayed say '(.*)'$/ do |message| response.should have_selector :li, :content => message end
Here we’re telling cucumber to take the regular expression and pass it as the ‘message’, this nice little feature not only helps refactor our previous step, run cucumber again the other validation message steps will now cover too, how cool is that
Using this method can save us a lot of time when it comes to writing scenario,s as well as helping us cover a lot ground quickly. Having said that take care when writting your steps, it’s not hard to find yourself lost, trying to work out why a step is not working. With that said, explicit names help to alleviate this no end.
Now with that said let’s move along.
When /^fill in the new project form without a description$/ do fill_in 'project_title', :with => 'new project' fill_in 'project_aim', :with => 'To provide a service' end
As we have defined the step for submitting the button all the leg work is already done and we are greated by an error. We’ll we didnt get the validation message, so we’ll add this below snippet to our Project model.
We’re all passing again & we only have one more step to roll out, nice!
When /^fill in the new project form without a aim$/ do fill_in 'project_title', :with => 'new project' fill_in 'project_description', :with => 'This project will help us manage stuff' end
Again we get the same error, so this time we added validation for our projects aim.
We now have a fully functionality CRUD for our projects, we’ll look into making this more functional, adding scenarios to our project.
I’ve decided to put the code for this series on github partly to help myself remember what I’m learning here but also for others see the code in action & contribute to. I’d love input on the series and and suggestions on improvements as a whole.