10 Feb 2015
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
07 Feb 2015
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
05 Feb 2015
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
01 Feb 2015
Generalization or Trianglization:
Writing general code
Red-Green Refactor
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
28 Jan 2015
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)