Build Robust & Production Quality Applications - Lesson 2: Shoulda Matchers

With rspec, there comes built-in matchers, which can be found: shoulda matchers

1. Object Identity:

You should use "Object Equivalence" instead because you will be testing if the two objects are the same when in reality they may equal the same but be different objects

  • actual.should == expected
  • actual.should eq(expected)
  • actual.should eql(expected)
  • actual.should equal(expected)

2. Comparison

actual should.be > expected

3. Types of Classes

test whether the actual is an instance of a class

4. Truthiness and Existentialism

tests whether the actual is truthy(not nil or false)

5. Expecting errors

6. Expecting Throws

Expecting methods to throw a symbol * Predicate Matchers - passes if method returns a truthy result

7. Collection Membership

testing whether an array is a subset of the expected

8. Ranges (only for Ruby 1.9)

Also matchers worth mentioning:

  • Have(n)items matcher
  • Operator matchers
  • Respond_to matchers - does this object respond to a certain method
  • Satisfy matcher - very flexible, you can pass it a block to see if it will evaluate to true
  • Specify akindof matcher
  • Yield matchers - allow you to work with blocks
  • Endwith, startwith
  • Cover Matchers

Build Robust & Production Quality Applications - Lesson 1: Front Page

Front Page

Front Pages or Root/home pages are typically static images. Those are rendered using the pages#controller. In this case however, we do not need the pages#front action defined in the controller bc it doesnt do anything. For now, we can just have the empty controller bc due rails conventions, Rails will just render the view of the same name if the action is not present in the controller.

#pages_controller.rb
class PagesController < ApplicationController
  def front
    redirect_to home_path if current_user
  end
end

Build Robust & Production Quality Applications - Lesson 1: Custom Form Builders

Let's say we have a Todo's app, where we allow our users to create a new todo. We also want to show errors when validation errors occur:

#new.html.haml
%section.new_todo
  %h3 Add a new todo
  = form_for @todo do |f|
    = f.label :name, "Name"
    = f.text_field :name
    = f.label :description, rows: 6
    %br
    = f.submit "Add This Todo"

The issue with this code above is that it is not pulling in the errors on the Todos object.

We could:

#new.html.haml
%section.new_todo
  %h3 Add a new todo
  = form_for @todo do |f|
  if@todos.errors?
    = f.label :name, "Name"
    = f.text_field :name

...but this is cumbersome to do for each form.

Lets create a helper called MyFormBuilder that will customize the label:

class MyFormBuilder < ActionView::Helpers::FormBuilder
  def label(method, text = nil, options = {}, &block)
    errors = object.errors[method.to_sym]
    if errors
      text += " <span class =\"error">#{errors.first}</span>" #pull the first error from the errors object (an array)
    end
    super(method, text.html_safe, options, &block) #delegates control back to the custom form builder
    end
  end

Update new.html.haml, make sure you add "MyFormBuilder" to your form_for block:

%section.new_todo
  %h3 Add a new todo
  = form_for @todo, builder: MyFormBuilder do |f|
    = f.label :name, "Name"
    = f.text_field :name
    = f.label :description, rows: 6
    %br
    = f.submit "Add This Todo"

Alternate Option: my_ form_ for helper

This is still somewhat repetitive. Let's create a my_ form_ for helper. We can manually merge in the myformfor rather passing in the MyFormBuilder:

app/helpers/my_form_builder.rb
module ApplicationHelper
  def my_form_for(record, options = {}, &proc) #takes 3 arguements
    form_for(record, options.merge!({builder: MyFormBuidler}), &proc)
  end
end

Update the new.html.haml

%section.new_todo
  %h3 Add a new todo
  = my_form_for @todo do |f|
    = f.label :name, "Name"
    = f.text_field :name
    = f.label :description, rows: 6
    %br
    = f.submit "Add This Todo"

Custom Form Builders in The Wild!

1. Formtastic

  • You can use semantic form_ for

2. Simple_form (creators of devise)

  • allows integration with Twitter Bootstrap & Zurb Foundation 3
  • is not best for those with alot of customization
  • has default mapping - which is not the best for complex forms

3. BootstapForm

  • minimal dsl
  • similar flow/syntax to Rails

Build Robust & Production Quality Applications - Lesson 1: Red, Green, Refactor

Generalization or Trianglization: Writing general code

Red-Green Refactor

  • Writing Tests to Fail and then refactor your code to pass.

  • Each time - only write enough code to get an different error message.

TDD is a discipline for writing code, writing developer (unit) tests, and doing design work in an integrated approach. A useful mnemonic for remembering the basic TDD cycle is "red > green > refactor." Here is a short summary of each of the steps.

RED

Start by thinking of a behavior you want the software to have which could be implemented by a few lines of code (fewer than five is a good target). Then write a test (also only a few lines of code) that will fail unless that behavior is present.

GREEN

Write just enough code to get the test to pass. Focus on: Getting something to work. Simplest possible implementation, rather than the most elegant solution (even hard-coded values are okay).

REFACTOR

Now that the code does what you want, use refactoring to make the design right. Making the code readable and eliminating duplication.

The cycle then repeats with each new behavior you want to have. The entire cycle should take between 5-10 minutes.

A couple of last points to emphasize:

Tests are checked in with the rest of the code (they are essentially living documentation of the code) Tests are run with every build

Good Article by Kent Beck: Test Driven Development

Build Robust & Production Quality Applications - Lesson 1: From Test Later to Test Driven

There are several ways to integrate testing into your workflow:

1. Code First, Test Later

Benefit: Guard against regression

2. Code a Little, Test a Little

Benefit: Feedback loop is shorter

3. Test First Development

Where you write the test first, run it and make sure it fails then after which you write the code to make it pass. It challenges to think about your expected result upfront and you're able to lock in your behavior while working towards that

4. Test Driven Development (TDD)

Test, not only to test with an expected result in mind but you write test to interactively build up (drive out) your implementation code. Benefit: people normally gravitate towards TDD, when they have an end goal that is complex in nature so they start with the simple and build up on complexity gradually.

5. Test Driven Design (TDDe)

Similar to Test Driven Development, except TDD is more logic and locally focused. while TDDe is more expanding to the system level, more focused on the architecture (the interaction or collaboration b/w components in your system - describing the interactions b/w various objects)