Bummer! This is just a preview. You need to be signed in with a Pro account to view the entire video.
Start a free Basic trial
to watch this video
Cucumber is a Behavior-Driven Development (BDD) framework that allows you to write out specifications in plain English, allowing all stakeholders in a project to understand what's going on. You'll need to be comfortable with Ruby on Rails to follow along but there are some general principles you can learn about BDD if you're interested.
Related Links
-
0:00
[? music ?] [treehouse workshops]
-
0:02
Hello and welcome to this Treehouse Workshop.
-
0:05
I'm Andrew, and today we're going to talk about Cucumber,
-
0:07
a BDD, or behavior-driven development, testing framework.
-
0:11
Sometimes in client meetings you come out on the same page with your client.
-
0:16
Hopefully you are every time.
-
0:18
But as the development progresses in your application development
-
0:23
and the client sees the app, sometimes there's a miscommunication
-
0:27
on what needs to be done.
-
0:29
So Dan North came up with this idea of behavior-driven development.
-
0:34
There's 3 main principles in behavior-driven development,
-
0:39
and you can check those out on behaviour-driven.org.
-
0:43
The first principle is business and technology
-
0:46
should refer to the same system in the same way.
-
0:49
Basically, the words that you use as the developer
-
0:52
and as the business owner or the project owner should be the same language.
-
0:58
There shouldn't be anything really technical.
-
1:00
Everyone should understand what's going on in the software.
-
1:04
Principle number 2 is that
-
1:05
any system should have an identified, verifiable value to the business.
-
1:12
This idea is that you shouldn't be developing anything
-
1:15
that doesn't add value to the business.
-
1:18
And once you've identified it, it's got to be verifiable
-
1:22
that this actually does produce value for the business
-
1:25
because you're in the business to stay in business and to make money.
-
1:31
So if you're wasting time on stuff that isn't valuable to your consumer
-
1:38
and therefore you as a business, you should really scrap that idea.
-
1:42
And then the third principle is that the up-front analysis, design and planning
-
1:47
all have diminishing return.
-
1:50
So the idea there is that you can't really predict
-
1:54
everything that's going to happen in the future.
-
1:57
You can't predict all possible futures.
-
2:00
So just really focus on the minimum viable product
-
2:04
and get it out there for real users to test.
-
2:07
In short, BDD relies on the use of a very specific (and small) vocabulary
-
2:14
to minimize miscommunication and ensures that everyone understands,
-
2:18
whether that's the business owners, the developers, testers, analysts and managers,
-
2:23
so that they're not only on the same page but use the same words.
-
2:27
And Cucumber is a framework that enables you to just do that.
-
2:34
If you go to cukes.info, there is the Cucumber home page.
-
2:40
On here it shows you the basic overview of what Cucumber is.
-
2:44
So you define a feature and then scenarios,
-
2:48
and each scenario has sets of steps, and you implement the step_definition
-
2:54
and then you start running these steps as you would in a testing framework.
-
2:59
BDD doesn't supersede any testing framework per se;
-
3:05
it's really the best practices of testing just manifest in plain English.
-
3:12
So today we're going to create an anonymous blog
-
3:16
where users can comment and post their own posts
-
3:20
for a business owner or site operator to monetize
-
3:24
and make money from all the lovely people
-
3:27
coming to read these interesting anonymous articles.
-
3:30
What I'm going to do is jump into my console
-
3:34
and make sure I'm using the right version of Ruby, 1.9.3.
-
3:42
And let's just do gem install rails
-
3:44
just to make sure we've got the right version of Rails installed, all up to date.
-
3:51
Now I'm going to type rails new anoneeblog to create my new application.
-
4:00
And whilst it's doing its thing,
-
4:02
I'm going to jump over to the Cucumber-Rails gems documentation.
-
4:10
Cucumber can also be used in other languages like Java, Dot Net,
-
4:17
and it's been translated into other languages as well, not just English.
-
4:21
But we're going to use a specific gem called Cucumber-Rails.
-
4:25
For installation in Rails 3 projects, we need to include the Cucumber-Rails gem
-
4:31
and the database cleaner.
-
4:33
So I'm just going to copy that.
-
4:36
Actually, I'm going to open up Sublime, the anoneeblog.
-
4:41
So if we jump into our Gemfile and then paste in the test group gems
-
4:50
and jump back to our terminal and cd into our anoneeblog folder
-
4:56
and then bundle install, we've installed Cucumber.
-
5:03
So if we jump back to the documentation
-
5:06
it says to run rails generate cucumber:install,
-
5:11
so that's what we'll do next.
-
5:18
As you can see, it's created a features folder.
-
5:21
This is where we'll be writing our plain text features
-
5:25
and a bunch of other files too.
-
5:27
So let's go back to Sublime and jump into our features folder.
-
5:35
In here I'm going to create a new file called blogging.feature.
-
5:44
At the top of the feature file you need to specify a feature,
-
5:49
and you do that by saying Feature: and then what the feature is, a general description,
-
5:55
so Blogging, and then I'm going to say the value
-
6:02
In order to monetize content
-
6:10
As a site operator
-
6:16
I want people to be able to create a blog post.
-
6:28
These features could be anyone in the business or a user on the system
-
6:34
from their perspective.
-
6:37
So as the site operator, I want people to be able to generate content for me
-
6:42
so I can add word(?) all over the place so I can generate money.
-
6:49
The feature is a non-executed step.
-
6:53
It's just for a little bit of background.
-
6:58
The next thing we're going to define is a scenario which is executed.
-
7:04
Scenario: and then we'll call this Creating a blog post.
-
7:13
Under each scenario there's a step, and they all have these special keywords.
-
7:19
The first special keyword is Given, which is to talk about the assumptions of the test.
-
7:27
So Given I am on the home page.
-
7:31
If you've noticed, we haven't even developed any Ruby on Rails code at all.
-
7:37
We're just writing the tests, the actual scenarios,
-
7:42
that we want to have in the system.
-
7:44
So we're not committing to anything right now other than this is what we want it to do.
-
7:49
And instead of writing Given again, you can use the keyword And,
-
7:53
which can be used in combination with these other keywords
-
7:57
that I'm going to show you later.
-
7:59
Given I am on the home page
-
8:01
And I follow "New Post".
-
8:06
Imagine that on this home page there's a New Post button or a link.
-
8:11
When I click on it, it will take me through to a form that I need to fill in.
-
8:17
So I can do When.
-
8:20
This is the test case, so when I perform a certain action.
-
8:24
When I fill in "Title" with "Hello World!"
-
8:34
And—so I'm using And again, but it's When and then And rather than Given and And—
-
8:42
And I fill in "Body" with "This is my first post!"
-
8:56
And I press "Publish"
-
9:01
Then I should see "Hello World!"
-
9:09
And I should see "This is my first post!"
-
9:18
So this is saying when we press the Publish button,
-
9:23
I should be able to see Hello World! and This is my first post!
-
9:27
So as a business owner or site operator, I understand this language here,
-
9:33
and most people would understand this language who are dealing with websites.
-
9:38
So we're using very similar expressions as we're going through this scenario.
-
9:45
I forgot to mention that I used a Then keyword, which is also a special keyword,
-
9:53
to test these assumptions when I've performed a particular action,
-
9:58
and then I have used the combination And again there.
-
10:02
So what do we need to do?
-
10:05
We go back to our terminal, and when we installed Cucumber
-
10:09
it's created a rake task called cucumber.
-
10:14
So when I type rake cucumber,
-
10:16
it says the database doesn't exist yet, so rake db:migrate
-
10:22
and let's do rake cucumber.
-
10:30
And as you can see, all of these steps are yellow,
-
10:36
and that's because these steps are undefined.
-
10:41
As you noticed, at the bottom here
-
10:44
Cucumber has given us some examples of steps and how to implement them.
-
10:49
So let's copy and paste these
-
10:52
and then jump into our step_definitions and create a new file.
-
10:58
Let's call these general_steps.rb.
-
11:06
I'm just going to post those in there,
-
11:10
and I'm just going to grab this one step here where it says, "I am on the home page,"
-
11:15
and I am going to create a new step_definition file called path_steps.rb
-
11:22
and then paste that in there and save.
-
11:25
As you can see, we've got a block with Given, When, and Then
-
11:33
and a regular expression here
-
11:37
which captures special keywords that are in the step.
-
11:45
So for example, Given I follow something,
-
11:50
instead of calling it arg1 because Cucumber doesn't know what it's going to be,
-
11:53
I'm going to call this (link_name).
-
11:57
And there's a special keyword pending.
-
12:00
Here when I fill in, I'm going to put field and then with value,
-
12:09
and then When I press (button_name)
-
12:15
and Then I should see (content).
-
12:19
So instead of just having those arg1s and arg2s,
-
12:21
I've just made it a little bit clearer.
-
12:24
And here I don't need to do anything
-
12:27
because I'm not looking for a particular phrase.
-
12:31
So if you're not familiar with regular expressions,
-
12:33
I encourage you to go and look into those in greater detail.
-
12:37
But just imagine I'm repeating myself—"I follow" when I click on several links
-
12:42
or "I fill in" several forms.
-
12:44
These are regular expressions, they are regular sentences,
-
12:47
and we can get the program to grab the specific values in these quotes
-
12:56
and then we can test for those things.
-
12:59
When I run that test again, rake cucumber,
-
13:05
you can see that instead of suggesting now, it just says that I need to implement this
-
13:12
'Given I am on the home page'.
-
13:16
Cucumber uses something called Capybara,
-
13:20
which is a way for moving around a web page in code.
-
13:26
If we look at the documentation here, we can see that there are special keywords
-
13:32
called fill_in, click_link, visit, and that's the one that we really want to use right now.
-
13:40
We want to visit the home page.
-
13:42
So if we copy visit and then jump into our code,
-
13:47
go into our paths and then type visit and then "/", which is the home page URL.
-
13:56
So when we run this test now, when I do rake cucumber, it passes.
-
14:04
Next it says when I follow "New Post".
-
14:08
It says this needs to be implemented.
-
14:11
So if we go back to the Capybara website, there's a thing called click_link,
-
14:17
and it takes a string, which is the link_name.
-
14:20
So let's copy click_link, jump into our general_steps, and implement our step.
-
14:27
So click_link link_name.
-
14:30
And then let's run that again and see what happens.
-
14:37
And it's red. It says Unable to find link "New Post".
-
14:42
We haven't written any code yet, so that's what we need to do now.
-
14:45
In our home page we need to create a link with "New Post".
-
14:52
If we take a look in our public folder, delete the index.html
-
14:59
which Rails by default puts in there.
-
15:01
But in Rails 4 that's not going to be there at all, so you don't have to worry about it.
-
15:05
I've deleted that, and let's go to our routes config file.
-
15:14
We'll do root 2 and we'll call it posts
-
15:19
because we want the index of our PostsController to list the posts.
-
15:28
In our layouts file, which is in app, views, layouts, application,
-
15:37
let's create another element here
-
15:42
being all HTML5 and that and do a link_to new_post_path
-
15:52
and "New Post" and save that.
-
15:59
And then let's run our test again.
-
16:03
As you can see, it says uninitialized constant PostsController.
-
16:09
So this is really holding our hand, telling us what we need to do next.
-
16:12
So let's do rails g controller posts.
-
16:19
This is generating our PostsController for us,
-
16:22
so let's rake cucumber again.
-
16:25
And it says it can't find the index action on PostsController.
-
16:28
So let's jump into our PostsController,
-
16:34
hit Save and then rake cucumber again.
-
16:39
So it's missing a template index.
-
16:42
So let's jump into our views, posts, New File, save it, index.html.erb.
-
16:50
You'll be starting to think now and starting to see
-
16:53
how this development process is working out.
-
16:56
It's like I'm not having to think because we've already specified what we need to do.
-
17:02
It's just going through each step and it's telling me what to do every single time.
-
17:07
It's developing by itself, which is awesome.
-
17:10
So let's run this test again.
-
17:14
And when we get past all this stack trace, it says local variable or method undefined.
-
17:24
And this is a route, so when we do rake routes, we've only got the root path.
-
17:32
So let's go to our routes config file.
-
17:38
As you can see, we've only defined the root URL.
-
17:43
So let's do resources :posts.
-
17:49
And then when we do rake routes now,
-
17:51
you can see we've got all the various routes there.
-
17:55
When I do rake cucumber now, it's given us a different error,
-
18:00
saying that the 'new' action hasn't been defined yet.
-
18:03
So let's jump back into our PostsController and we'll do def new
-
18:10
and then we know what's going to happen next
-
18:14
is it says it can't find the template for new_html.erb.
-
18:19
And then let's run rake cucumber again.
-
18:24
Now we're starting to do a couple of steps in a row
-
18:27
because we're anticipating what the test is going to do.
-
18:32
But the idea is that you don't write any code that you don't have to do,
-
18:36
what doesn't bring value to the business.
-
18:38
So there's this step here where I fill in.
-
18:41
Let's jump into our general_steps and let's implement this.
-
18:50
Capybara, if we jump back to their documentation,
-
18:54
you can see there's a fill_in and then the label name with the value.
-
19:00
So let's copy that and then let's jump back to our steps file.
-
19:09
So when we fill in field with the value, it should populate those fields for us.
-
19:19
Let's run it again.
-
19:21
Now it says Unable to find the field "Title".
-
19:24
That's because we haven't done a form yet.
-
19:26
We're going to use something called SimpleForm,
-
19:28
which is a gem for Rails to create simple forms.
-
19:32
If we jump into our Gemfile and do gem 'simple_form'
-
19:39
and then do bundle install, boom, and then go to our new form,
-
19:47
let's just do
New Post
and then simple_form_for(@post) do -
20:01
and then we'll call the variable for the form f and that,
-
20:10
and let's do f.input, which is the special keyword for SimpleForm.
-
20:19
And we'll do :title.
-
20:22
We're not going to do any more than it's asked us to yet.
-
20:27
Let's do rake cucumber.
-
20:33
If we scroll up past all that stack trace, it goes a 'model_name' for NilClass.
-
20:38
So this is nil because any form helper
-
20:43
looks for the model name on an active model class.
-
20:47
So let's jump into our PostsController and let's do post = Post.new.
-
20:57
Click on Save, hit Return, and it says uninitialized constant Post.
-
21:06
That's because we don't have the Post model created.
-
21:11
So let's do rails g for generate model Post where the title is a string
-
21:19
and the body is a text.
-
21:22
If we hit Enter, rake db:migrate and then rake cucumber, let's see what happens.
-
21:34
As you can see, the title is now filled in.
-
21:38
Next, the body. It can't find the body.
-
21:41
And define f.input :body.
-
21:48
Let's run our test again.
-
21:52
Now it's asking us to implement the I press step,
-
21:57
so let's jump into our general_steps and then we need to implement that step here.
-
22:04
We need to click the button name, so let's do button_name and see what happens.
-
22:12
Oh, undefined method 'click'.
-
22:14
Let's jump back to the Capybara documentation.
-
22:18
So button. Is there a button? Click_button. So it's actually click_button.
-
22:23
Let's jump back to our test, replace click with click_button button_name,
-
22:28
and then run again.
-
22:31
It says Unable to find button "Publish".
-
22:34
That's because we haven't done the button Publish.
-
22:36
Let's jump back into our new and then do f.button :submit
-
22:46
and then let's call it "Publish" and then run the test again.
-
22:54
And it says we don't have a 'create' action, so let's implement that now.
-
22:58
Let's go to PostsController and then def create
-
23:04
and then we'll do @post = Post.new(params[:post])
-
23:15
and then @post.save and then let's redirect to the post as well.
-
23:25
Run the test.
-
23:27
Oh, 'redirect' is an invalid method because it is.
-
23:31
redirect_to
-
23:33
Not sudo reboot. Where did that come from?
-
23:35
rake cucumber—there we go.
-
23:38
So we don't have a 'show' action. Let's implement that.
-
23:40
def show
-
23:44
Let's do @post = Post.find(params[:id]).
-
23:53
And then let's go to our posts and create show.html.erb.
-
24:03
And because we know that the next test
-
24:08
will be expecting to find the Post.title and the body, let's just do that now.
-
24:16
simple_format @post.body
-
24:22
rake cucumber
-
24:26
Okay. It says Then I should see "Hello World!"
-
24:29
We haven't implemented that step yet.
-
24:32
So once we've implemented this step, we will have created a very simple
-
24:39
but effective way for testing our forms and testing for content.
-
24:45
Let's go back to the Capybara documentation and let's look for content.
-
24:51
So a page has content?
-
24:56
Let's copy that.
-
24:59
Because this returns a true or false value,
-
25:03
we need to assert whether this is true or false.
-
25:05
So if page has content, it would return true or false.
-
25:10
Let's do assert, which is what you'd use in a test framework.
-
25:15
Actually, it's using MiniTest under the hood here.
-
25:19
Let's do rake cucumber.
-
25:22
As you can see, all these steps are passing now.
-
25:24
We've started defining the language, defining the steps,
-
25:27
so that when we add more content to these features
-
25:32
we can reuse these steps over and over again.
-
25:35
So it can be quite painful initially, but once you get over the hump,
-
25:39
things start to look better.
-
25:42
I've got some more features here set up for blogging.
-
25:47
Open this in Sublime, copy and paste those, and paste them into the blogging_feature.
-
25:54
Here we have a few more steps to look at.
-
25:59
This is the scenario we want to check: Creating an invalid blog.
-
26:05
I want to go to the home page, follow "New Post" and don't enter a title
-
26:10
but I enter in a body and I click on Publish.
-
26:13
I should see "can't be blank".
-
26:15
This is an error message that SimpleForm shows.
-
26:18
So when I fill in the title and then empty the body and click on Publish,
-
26:23
it should say "can't be blank" on the body that time.
-
26:26
So when I fill in the body—that implies the title is still filled in—
-
26:30
and I click on Publish, I should see a published post.
-
26:34
I'm going to use a new annotation up here called work in progress.
-
26:41
When I run rake cucumber:wip, it will only run the scenarios that are work in progress.
-
26:51
So instead of running all those features over and over again,
-
26:54
we're just seeing this.
-
26:56
So what's happening here?
-
26:58
It's a bit obtuse at the moment, so let's introduce a new step
-
27:03
that we can use for debugging.
-
27:05
Just before the failing test happens, let's include a new step to show me the page.
-
27:14
Let's jump back into here and then say And then show me the page.
-
27:23
Maybe not Then because that's a keyword. And show me the page. Let's do that.
-
27:27
So rake cucumber:wip and let's copy that step_definition for us to general_steps
-
27:36
and let's have a look at Capybara.
-
27:40
Let's look at debugging.
-
27:45
Capybara provides save_and_open_page, so let's copy that
-
27:48
and jump back into our Sublime,
-
27:53
save and open the page, and let's run this test again.
-
27:58
As you can see, it says Please install the launchy gem.
-
28:03
Let's do that.
-
28:05
Jump into our Gemfile, and let's put it into our test group
-
28:13
and then bundle install
-
28:17
and then rake cucumber:wip
-
28:24
and it's opened the page.
-
28:25
As you can see, it's creating a post. That's not what we want.
-
28:29
We want it to validate the presence of the title and the body.
-
28:34
So let's jump back into our Sublime editor and go to Post
-
28:40
and let's do validates :title :presence => true.
-
28:50
Let's, while we're at it, because we know it's looking for the body as well, do that.
-
29:02
Save and then rake cucumber:wip.
-
29:07
What's going on here? PresenceValidator. Let's double check.
-
29:12
We can't see blank because it's not doing anything.
-
29:17
We're not actually showing the form.
-
29:20
So if we jump into our PostsController and do an if statement here,
-
29:28
so if @post.save will redirect to the post if it's valid,
-
29:36
else we will render the new form again.
-
29:43
So let's run that rake cucumber:wip again, and I've spelled render wrong, so it will fail.
-
29:50
Let's do it again.
-
29:52
There we have it.
-
29:54
It says can't be blank.
-
29:55
If we look at all these steps, they're passing. Great.
-
29:59
Sometimes when you're working like this, it can get a little bit confusing
-
30:04
because you're not actually clicking through the process yourself;
-
30:07
you're getting this system automated.
-
30:10
We don't want that page to load up every time, so let's get rid of that step.
-
30:15
Go to our feature and get rid of that step.
-
30:21
But now that's been implemented, we can use that again
-
30:24
if we need to debug in the future.
-
30:26
Now let's take a look at this next scenario called Listing Articles.
-
30:32
It says I have the following posts:
-
30:35
As you can see, this is new structure here and it's a table
-
30:39
with the title and bodies of 2 posts.
-
30:43
So given I've got my first post and my second post,
-
30:47
when I'm on the home page the second post should be listed first.
-
30:51
It should be going from the newest first down to the oldest.
-
30:56
When I follow the second post link, I should see the second post's post.
-
31:03
And when I follow the home page, I should be back on the home page.
-
31:07
Let's do rake cucumber:wip again.
-
31:13
And we've got some new things to implement here.
-
31:17
These 2 steps here are related to the post,
-
31:21
so let's just copy that and create a new step_definition file called post_steps
-
31:28
because we're going to keep our code nice and compartmentalized.
-
31:33
So post_steps, save that.
-
31:41
And then there was this last one here about a path.
-
31:43
So let's go to path_steps and post this in here.
-
31:53
Let's assert that the current path is the same as "/".
-
32:00
That's that step implemented.
-
32:03
Let's run cucumber:wip again.
-
32:07
This following posts step has not been implemented, so let's do that next.
-
32:14
When we jump into our post_steps, it says that this table here,
-
32:20
this table of data, is a Cucumber::AST::Table.
-
32:25
Let's check the documentation on that.
-
32:28
Pop that into Google, click on that here,
-
32:33
and you can see the methods that are on this particular object.
-
32:40
There's one thing called hashes here which shows that it returns an array of hashes
-
32:47
and we can use Active Record to pass in these hashes in order to create these posts.
-
32:53
So let's go to our posts definition and cycle over the table for hash in table.hashes
-
33:08
and then we can do Post.create and then (hash).
-
33:15
So that passes in the body and title for each one of those rows.
-
33:19
So there's 1 hash per row with the keys and values.
-
33:25
Let's save that and then do rake cucumber:wip again.
-
33:31
Then we need to implement this.
-
33:34
"My Second Post" should be listed first.
-
33:37
The regular expression is capturing the title here, so I'm going to do title.
-
33:42
But also notice that these 2 steps are very similar.
-
33:45
So I'm going to get rid of this last step
-
33:48
and then I'm going to use another similar regular expression here
-
33:52
to capture the position.
-
34:00
What we can do is we can look using Capybara
-
34:05
to find all the h1 elements on our listing page.
-
34:09
Let's do that now.
-
34:10
We can do all("h1").each h1 do and then we're going to pass in i as an index.
-
34:28
The index that's been passed in by Ruby is a zero-based index,
-
34:34
so we can do if position =="first", which is this captured expression here,
-
34:46
and i == 0, then we can assert_equals h1.text—so that's the text of the h1—
-
35:00
is equal to the title that's captured by the regular expression here.
-
35:06
We can do elseif position = "second" and i == 1, which is zero-based for the second
-
35:21
in the array of h1s, we can assert_equals h1.text is equal to the title as well.
-
35:33
Let's run that step again.
-
35:36
It says Unable to find the link to "My Second Post".
-
35:41
So let's jump into our index.html and let's do for post in @posts
-
35:58
and let's do h1 and let's do link_to post.title, post.
-
36:19
This should list all the posts as h1s.
-
36:24
As you can see, we've got something going on here.
-
36:28
Oh. It says 'each' on NilClass.
-
36:32
Let's jump back to our posts index and do @posts = Post.all and then do it again.
-
36:44
And it says it's Unable to find the "Home" link.
-
36:49
So let's go back to our layouts and then we can link_to "Home", root_path.
-
37:05
That's using the Rails special keyword. Let's run that again.
-
37:11
And then it should be on the home page. And it passes.
-
37:14
Let's take a look at our application as it stands using rails s.
-
37:21
Let's jump into our home page and let's create a new post.
-
37:26
This is my first post. Hello! And Publish.
-
37:35
Let's go back home. This is my first post.
-
37:37
Let's create a second post.
-
37:40
This is my second post.
-
37:44
Hello!
-
37:47
And let's go to the home page.
-
37:50
As you can see, there's something not quite right here.
-
37:55
There must be something wrong with our testing assumptions
-
37:58
because our first post is first and our second post is second.
-
38:03
Sometimes even when we're trying to do the best thing here, it doesn't add up.
-
38:10
So let's deconstruct what's going on here.
-
38:13
I can see that I've missed an equals sign there.
-
38:15
I wonder if that's anything to do with it.
-
38:18
Let's do rake cucumber:wip.
-
38:23
It's still passing, and we know it should fail
-
38:25
because the first post is listed first, not the second.
-
38:31
Let's try and log out some information here
-
38:33
Let's assert_equals that the title is—
-
38:45
We're hoping that the title of the first step should be—what was it?—
-
38:51
"My Second Post".
-
38:53
Let's do "My Second Post".
-
39:00
It says 'assert_equals' is undefined method, 'assert_equals'.
-
39:05
So we can see that this here is not even being triggered by this all.
-
39:13
So maybe this all method is incorrect here.
-
39:18
But I should be able to assert_equals.
-
39:23
Okay, so in MiniTest it's not assert_equals, it's actually assert_equal.
-
39:27
So let's assert_equal that all h1s should be—the length of it should be 2
-
39:39
just as a quick sanity check.
-
39:43
Let's rake cucumber:wip.
-
39:47
It's equaling 2 but for some reason it's not going in here correctly.
-
39:52
So let's assert_equal h1.text is equal to "Blah". It should fail.
-
40:07
Yeah, it expected "My First Post" but was "Blah".
-
40:11
That's okay.
-
40:14
Let's just go to our code in our PostsController
-
40:21
and let's just reverse the order.
-
40:24
So order("created_at—and we want it to descend.
-
40:36
So let's run rake cucumber:wip
-
40:39
because that's the code that we should be expecting, and it's passing.
-
40:44
Do rails s again, take a look at our localhost,
-
40:49
and the "My Second Post" is first; the first post is second.
-
40:54
So that's as it should be.
-
40:57
But we've got something wrong with our test,
-
41:00
and we need to make sure that our test—
-
41:02
It's almost like you need a test for your tests.
-
41:05
Let's take a look at this.
-
41:07
The issue here is that each should have each_with_index,
-
41:14
which is the Ruby method to pass in the thing that you're iterating over
-
41:21
with its index.
-
41:23
If we run this test now, rake cucumber:wip,
-
41:27
we can see that it was expecting the second post first, not the first post.
-
41:33
So if we jump into our PostsController and do order("created_at DESC").all,
-
41:47
do another rake cucumber:wip, and it's not passing.
-
41:53
Let's boot up the Rails server and see if what we're expecting is happening.
-
41:59
And it is. So there's something wrong with the logic somewhere.
-
42:04
Let's just run rake cucumber:wip again just to double check where we're at.
-
42:09
It was expecting the first post but was second post.
-
42:15
Let's double check.
-
42:16
If the position is first and is equal to 0—
-
42:23
Let's check what this all action is doing.
-
42:27
Let's do assert_equal 0, i
-
42:34
and assert_equal h1.text, and it should be "My Second Post".
-
42:47
This should pass on the first iteration.
-
42:52
Let's do rake cucumber:wip.
-
42:55
It's not. It's saying expected 0 but was 1.
-
43:02
So each_with_index. Let's double check the documentation for it.
-
43:09
Let's check if this is a zero-based index or not.
-
43:14
It doesn't say it's zero-based, so let's double check
-
43:19
by doing 1 and 2, rake cucumber:wip, and there we go.
-
43:30
So each_with_index doesn't seem to be a zero-based index. I thought it was.
-
43:37
Those are passing now.
-
43:38
So if we take out the wip from our blogging feature now,
-
43:45
like so, run rake cucumber:wip, we haven't got any with the wip annotation.
-
43:56
So when we run rake cucumber by itself, those 3 things pass.
-
44:00
What's a blog without comments?
-
44:03
Not a blog at all or Daring Fireball, which is a blog, so I don't know why I said that.
-
44:08
Let's create a Comments feature.
-
44:14
We'll copy those comments from this folder here.
-
44:21
We've got a feature for commenting.
-
44:24
It says In order to increase engagement on the site to monetize traffic
-
44:28
As an site operator I want people to be able to comment on blog posts.
-
44:34
And I have introduced a new thing called Background.
-
44:37
Background is something that gets run before every scenario.
-
44:42
This background would create 2 posts,
-
44:46
and it expects that I'm on the home page.
-
44:49
And when I follow the second post link,
-
44:51
I should be able to fill in some form somewhere on there saying
-
44:55
my name is Andrew and that the comment body is awesome.
-
45:01
And when I press Submit Comment,
-
45:03
I should be back on that "My Second Post" page.
-
45:07
with a "Comment by: Andrew" and "My Awesome comment".
-
45:13
And if I follow the home link and I go to "My First Post",
-
45:18
there shouldn't be a comment by me and you shouldn't see my awesome comment.
-
45:24
And then secondly, we've got this other scenario where we do an invalid comment
-
45:29
where if we don't fill in the name but we fill in the body it should say
-
45:34
I "can't be blank" and vice versa and with the assumption
-
45:38
that the form will be completely filled in on the error
-
45:41
and I only need to fill in the missing part.
-
45:44
And once I submit that, I should be able to see "Comment by: Andrew"
-
45:48
and "My Awesome comment".
-
45:50
I've just noticed we need to submit that comment,
-
45:56
and I should be able to see those.
-
45:58
Let's jump back to our console after we've just done wip on this creating comment.
-
46:05
So rake cucumber:wip.
-
46:11
As you can see, there's 2 steps that haven't been defined yet.
-
46:15
So let's copy those and let's jump first to our posts_step
-
46:22
because there's one for a particular post.
-
46:24
I should be on the title of that particular post page
-
46:29
and I should not see.
-
46:32
So this is a general step, so let's keep organized.
-
46:38
Let's call that content.
-
46:43
Let's do a similar thing to has_content, and we'll just put an exclamation mark
-
46:50
to say has not got that content.
-
46:53
In our posts_step we can do something similar to our paths
-
47:02
where we assert_equal current_path is the same as the post_path with the—
-
47:13
I'm going to start up here so it's a little bit more legible.
-
47:18
So post = Post.where.
-
47:24
The title is the title, and we're going to get the first one that matches that.
-
47:29
So we put post in there.
-
47:31
We're asserting that the current path of the page is the same as the post path.
-
47:37
So we've implemented those 2 steps now,
-
47:39
and when we do cucumber:wip we can address the first problem—
-
47:45
that there isn't a field with the name "Name" and I can't fill in the word "Andrew".
-
47:54
So let's jump to our show because it's expecting a form here.
-
47:59
We will do a simple_form_for.
-
48:05
And because it's supposed to be for a comment for a particular post,
-
48:15
we're going to pass in the array of post and a new comment.
-
48:29
Let's run rake cucumber:wip.
-
48:32
As you can see, it's saying 'model_name' is nil.
-
48:36
That's because comment is a NilClass again.
-
48:39
If we jump to our PostsController and go to our show,
-
48:46
what we can do here is just do @comment = Comment.new.
-
48:51
You should be guessing now that the comment class hasn't been created yet,
-
48:58
so there's an uninitialized constant, Comment.
-
49:02
So we can do rails g model Comment,
-
49:07
and because it's a nested belongs_to structure,
-
49:15
we're going to create a post_id of an integer,
-
49:19
we're going to create a name, which is a string,
-
49:22
and we're going to create a body, which is text.
-
49:31
Okay, so rake db:migrate.
-
49:33
Always remember to do that when you create a model.
-
49:36
When we do rake cucumber now, it will clone the development database
-
49:45
into the test database.
-
49:46
I did rake cucumber, so it's showing loads of stuff.
-
49:49
So let's do rake cucumber:wip
-
49:54
so we're not bogged down with all these tests not failing.
-
49:59
If we scroll up to the top, it says can't find 'post_comments_path'.
-
50:07
So let's jump into our routes
-
50:12
and then let's do a nested resource, so resources :comments.
-
50:22
When we do rake routes now, it will show the comments route.
-
50:28
We don't need to do that, so I'm just running rake cucumber:wip,
-
50:32
and it says Unable to find the field "Name".
-
50:35
So once again, jump back into our form for the comment
-
50:41
and do f.input :name.
-
50:45
And because I know there's going to be a body and that will be the next test,
-
50:49
we can start combining these together.
-
50:52
But don't get too carried away because you may end up
-
50:55
spending time on a feature that doesn't need implementing.
-
51:00
And then f.button :submit, and I can't remember the specific wording,
-
51:08
so let me just jump to the commenting feature.
-
51:12
It's called "Submit Comment".
-
51:18
"Submit Comment", "Submit Comment".
-
51:24
When I click on Submit Comment, I should be on the post page
-
51:30
and I should see the comment.
-
51:32
So let's see if we need to do anything.
-
51:36
It says uninitialized constant CommentsController. Okay.
-
51:40
We need to generate it, so rails g controller comments.
-
51:47
As you can see, this has been created now.
-
51:50
Let's run rake cucumber:wip.
-
51:54
The more and more you get used to this, you'll understand,
-
51:57
"Oh, create method." So let's create that.
-
52:00
Comments def create end.
-
52:08
Let's do @comment =
-
52:11
Let's do @post as well.
-
52:13
So @post = Post.find(params[:post_id])
-
52:22
and then we can do @comment = @post.comments.build(params[:comment]).
-
52:37
And because we haven't set up the belongs_to and has_many relationship,
-
52:43
we'll do that now.
-
52:44
So a post has_many :comments.
-
52:50
And a comment belongs_to :post.
-
52:58
Okay.
-
53:00
So if @comment.save redirect_to @post
-
53:09
because we want to be back on the post page.
-
53:12
And else if there's an error, we'll do something in a second.
-
53:15
Let's do rake cucumber:wip.
-
53:23
It says that I should be on the second post page, which I am,
-
53:26
but we're not displaying the comments yet.
-
53:29
So let's go to our show page and we will display the comments here.
-
53:37
Let's do for comment in @post.comments
-
53:46
and then we'll do a div_for(comment) as a nice little wrapper, do, end,
-
54:03
and then we'll have an h2 with the %= Comment by: comment.name,
-
54:18
end the h2 and then do a simple_format, which is a Rails helper,
-
54:24
with comment.body.
-
54:28
Save that and rake cucumber:wip.
-
54:33
As you can see, it goes home, goes to the first post,
-
54:38
and it can't see the "Comment by: Andrew" on that post.
-
54:44
As you can see, we've already been building up these steps,
-
54:48
and once you've written them out, they pass a lot quicker
-
54:52
as you're progressing through building your site.
-
54:56
Let's check the last scenario out
-
55:01
by just doing a wip over it and then rake cucumber:wip.
-
55:08
When I press Submit it says it can't be blank.
-
55:11
So let's jump into our Comment and let's do validates :name,
-
55:22
:presence—let's spell it right the first time this time—
-
55:26
and validates :body, :presence => true as well.
-
55:34
Let's run this again.
-
55:40
It's saying Missing template comments/create.
-
55:44
So let's take a look at our CommentsController.
-
55:49
That's because when it's valid it tries to render the create method.
-
55:54
But since we've already defined post and comment,
-
55:58
we can just render the same form from the posts in the show action.
-
56:07
Let's do rake cucumber:wip. And it passes.
-
56:13
Let's finally remove the wip and then test all of our features and scenarios.
-
56:24
And everything is passing.
-
56:26
The final test is to boot up our Rails server, go to localhost 3000.
-
56:35
Let's create a comment.
-
56:38
Andrew. Really cool post!
-
56:44
And as you can see, it said Comment by: Andrew.
-
56:47
And if I submit, it says it must be filled in.
-
56:50
So it's working as we expect it to.
-
56:54
I hope you've enjoyed watching me build a website
-
56:57
using Cucumber and behavior-driven development.
-
57:00
It just goes to show that after the initial pain of the setup building out the site,
-
57:04
it gets a little bit faster, and you can maybe take a few of those steps all in one go.
-
57:10
So whether you're building a project yourself
-
57:12
for you and your business partner or even for your client,
-
57:16
it really helps to minimize miscommunications
-
57:19
so that you're all on the same page using the same language
-
57:22
and hopefully be building the same product in the end.
-
57:25
My name is Andrew Chalkley, and you've been watching a Treehouse Workshop.
-
57:29
If you've enjoyed this workshop, you'll also enjoy the others that we have to offer
-
57:33
on TeamTreehouse.com.
-
57:35
[? music ?] [treehouse workshops]
You need to sign up for Treehouse in order to download course files.
Sign up