CSC 321.01, Class 08: Behavior-driven design (BDD) and Test-driven Devlopment
Overview
- Preliminaries
- Notes and news
- Upcoming work
- Extra credit
- Questions
- Behavior-driven design (BDD)
- Cucumber basics
- Why test?
- Test-driven development
- An example
- Compare and contrast TDD and BDD
News / Etc.
- Please let me know when you notice problems on the Web site. I think I’ve fixed most of them.
- I’m shuffling the class around a bit today.
- Thursday is a lab day on Cucumber.
Upcoming Work
- No reading or reading journal for Thursday! Use the time to work on understanding Cucumber.
- Programming assignment: Intro rails (due TONIGHT at 10:30 p.m.)
- Programming assignment: Cucumber (due Tuesday the 26 at 10:30 p.m.)
- Those working on Python/Django projects may choose to design their own equivalent assignment.
Good things to do
Note: I do not do extra credit in two-credit classes.
Academic
- CS Table today
- CS Extras Thursday - Study abroad
Misc.
- Happy Kareoke Fun Time activities
- Musical, Friday 9pm, Gardner.
- Workshop: Burn your fear. Saturday, 2pm. The Wall. Email [improv] to sign up.
- Workshop: Creating through improv. Sunday, 2pm. The Wall. Email [improv] to sign up.
- Musical, Sunday, 7pm, Gardner
Questions
Behavior-driven design (BDD)
What are the core ideas?
- Work with user stories.
- Add behavior that corresponds to the story.
- Concentrates more on what the user sees than the underlying code.
- Often: Automated/programmatic
What else do you note?
- Wow. It’s verbose. It includes the user stories.
Why?
- Flexible, high-level approach to ensuring that our program does what it’s supposed to do (from the client perspective)
- Accessible to our client - User stories are how we communicate with the client. We should therefore have a programmatic way to verify that we have met the client needs.
- More specific descriptions are better
- “Easier to code”
- Closer to what the client wants
- Helps us communicate
Example?
- As a typical Grinnell student, to stay awake in the morning, I need to
be able to buy coffee from the machine.
- When I go to the coffee machine and put in a quarter and press the “Joe, Please” button, a cup appears at the bottom filled with six ounces of coffee.
- When I go to the coffee machine and put in a quarter and put my mug under the spout and press the “12 oz” button, 12 oz of coffee are added to my mug.
Cucumber basics
BDD is an important enough concept that people have built tools to help with it. In Rails (and elsewhere) one of the popular (or formerly popular) BDD tools is called Cucumber.
What is the core idea?
- BDD stuff can (and should) be automated.
What things are involved in a Cucumber test?
- Feature file: User story + Setup for scenarios + List of scenarios
- Step definitions - Turn the scenarios into calls to your Ruby code. Something like, regular expression + List of instructions
- Libraries
- Capybara - A library that provides backing for step definitions for Web stuff.
- The code you are testing.
- Bridge code
Why is there so much crap involved?
- To separate what is custom from what is general.
- Because there is a lot general implemented for you already.
- Problem: To do/test X, you may have to look at a, b, c, and d.
Scenarios
Feature: Invites
Scenario: Accepting the invitation
Given I sign in as "Mary Jane"
And I visit the invitation page
And I click the "Accept invitation" button
Then I should be redirected to the group page
And I should see a notice telling me I have access
@javascript @mobile
Feature: Visit the landing page of the pod
In order to find out more about the pod
As a user
I want to see the landing page
Scenario: Visit the home page
When I am on the root page
Then I should see "LOG IN"
When I toggle the mobile view
And I go to the root page
Then I should see "Welcome, friend"
When I am on the root page
And I am not logged in
Then I should see "Welcome, friend"
When I go to the mobile path
Then I should see "LOG IN"
Step Definitions
#citation: https://github.com/diaspora/diaspora/blob/master/features/step_definitions/posts_steps.rb
Then /^the post should be collapsed$/ do
first_post_collapsed?
end
Then /^the post should be expanded$/ do
first_post_expanded?
end
Why test?
Why is testing important? (Three minutes with your partners.)
- “Verify” that our code works
- For ourselves
- For our clients
- Edge cases
- Make us comfortable changing code
- Video game! - Fixing tests gives you a concrete set of progress.
- Documents expectations
- Helps us think about our goals
- Edge cases
- Your company or pointy haired boss may require it
- Some testing strategies require you to think broadly about your code, not just what individual procedures do but how the procedures intereact and their effect on the final user interface.
- Tests help you write your code - keep you focused.
- May make others more confident in using your code; may make you more confident in sharing your code.
Test-driven development
What are some core concepts?
- Conceive of coding through testing
- Write tests before you code.
- Some models are very extreme -
- Sam generally believes in
- Write lots of tests, including edge cases
- Write code
- Go back and add tests
- Continuous testing - Prefer that tests are run automatically
- Each time you modify
- Each time you commit (as a precondition to committing)
- “It it’s not tested, it doesn’t exist.”
- Test coverage
An example
Sometimes we write tests retrospectively. (But we can also think of them prospectively.)
Here’s an example from the book (or a variant thereof) that I like.
class TimeSetter
def initialize(d)
@d = d
end
def iso8601_ordinal
d = @d
y = 1980
while (d > 365) do
if ((y % 400 == 0) || (y % 4 == 0) && (y % 100 != 0))
if (d > 366)
d -= 366
y += 1
end
else
d -= 365
y += 1
end
end
return y.to_s + "-" + d.to_s
end
end
What does this code do?
- Provides a way to represent/store days as integers and use them in other ways.
- The
iso8601_ordinalprocedure provides a string of the form “YYYY-DD”, where DD is the day of the year using 1 based indexing. - Assumes no dates before January 1, 1980 are of value.
Why is the getter called iso8601_ordinal?
- It’s a formal name for YYYY-DDD.
- Standards exist; there’s a reason to use them.
- XKCD suggests YYYY-MM-DD
What tests would you write?
Input -> Output
- 0 -> “1980-0” - NO. Don’t test things that do not make sense.
- 1 -> “1980-1”.
- 365 -> “1980-365”.
- 366 -> “1980-366” if 1980 is a leap year
- 367 -> “1981-1” - edge case, right after a leap year
- Check for Y2K bugs! (+ 366 365 365 365 …) + 1 => “2000-1”
- An “average” date -> 2006, 1997
Do you see any obvious bugs?
- Whoops. It doesn’t work on the last day of a leap year. Maybe they should hve written clearer code.
while (d > days_in_year(y)) do
d -= 366
y += 1
end
TDD and BDD
What are some similarities?
What are some differences?