Last time, in part 1, I was giving some advice about testing - why to test at all, which tests are valuable and which are not, when to write acceptance tests and in what cases aim for the maximum code coverage. It brought about some serious discussion about testing ideas and if you haven't read it yet, you should probably check (it) it out. Giving some general point of view about such broad topic like Test Driven Development / Behavior Driven Development is definetely not enough so I will try to apply these techniques by implementing a concrete feature. I wanted to choose some popular usecase so that most developers will have an opinion how they would approach it. In most applications you will probably need:

User Registration

It is quite common feature and it can be approached in many ways. The most popular is to some authentication gem, like Devise, which is the probably the safest and the fastest way. However, Devise might be an overkill for some cases or maybe you need highly customizable solution. How would you write an implementation fot that usecase then?

Note: the implementation below doesn't aim to be the most secure approach for that feature, it's rather for demonstration purposes. I made some non-standard design decisions for the Rails application, you may want to read one of my previous posts to get more details why this way of designing code might be beneficial.

Specification

We know that we want to implent user registration. Let's say that we want user to confirm his/her account before signing in so we will need to send some confirmation instructions. Also, let's add some admin notifications about new user being registered to make it more interesting.

To make it even better, let's assume that we will create both User and UserProfile during registration: User will have just an email and encrypted_password attributes, UserProfile will have country and age attributes. User will also have to accept some policy to register. If we want to have confirmation, we will also need some attributes for confirmation_token , confirmation date ( confirmed_at ) and let's add confirmation_instructions_sent_at just to know, when the instructions were sent. These are just registration-specific attributes and we won't need them in most cases so let's extract them to UserRegistrationProfile

Note: when writing the implementation and the tests, the following gems were used: rails (4.0.3) , database_cleaner (1.2.0) , simple_form (3.0.1) with country_select (1.3.1) , reform (0.2.4) , bcrypt (3.1.6) , rspec-rails (3.0.0.beta1) , factory_girl_rails (4.3.0) and capybara (2.2.1) .

Start with acceptance tests

When writing new feature we should start from acceptance tests - we will make sure that the feature works from the higher level: from the user perspective and some side effects like sending emails. So the good start will be covering user creation and sending emails to an admin and to the user. Let's write some Capybara tests:

# spec/features/user_registration_spec.rb require 'spec_helper' feature "User Registration" do context "when visiting new user path" do background do visit new_user_path end context "registering with valid data" do given ( :email ) { "myawesome@email.com" } background do fill_form_with_valid_data ( email: email ) end scenario "new user is created" do expect do register end . to change ( User , :count ). by ( 1 ) end context "notifications" do background do register end scenario "confirmation email is sent to the user" do expect ( all_email_addresses ). to include email end scenario "notification is sent to the admin" do expect ( all_email_addresses ). to include "admin@example.com" end scenario "2 emails are sent" do expect ( all_emails . count ). to eq 2 end end end end end def fill_form_with_valid_data ( args = {}) email = args . fetch ( :email , "email@example.com" ) fill_in "email" , with: email fill_in "user_password" , with: "my-super-secret-password" fill_in "user_password_confirmation" , with: "my-super-secret-password" fill_in "age" , with: 22 select "Poland" , from: "country" check "policy" end def register click_button "Register" end

I like using some helper methods, especially in acceptance tests so I wrote fill_form_with_valid_data and register helpers - these are just some details and I don't need to know them when reading tests. There are also some helpers like all_email_addresses and all_emails , which come from the MailerMacros :

# spec/support/mailer_macros.rb module MailerMacros def last_email ActionMailer :: Base . deliveries . last end def last_email_address last_email . to . join end def reset_email ActionMailer :: Base . deliveries = [] end def reset_with_delayed_job_deliveries ActionMailer :: Base . deliveries = [] end def all_emails ActionMailer :: Base . deliveries end def all_emails_sent_count ActionMailer :: Base . deliveries . count end def all_email_addresses all_emails . map ( & :to ). flatten end end

If you like it, just create spec/support/mailer_macros.rb , put the code there and in your spec_helper.rb insert the following lines:

# spec/spec_helper.rb config . include ( MailerMacros ) config . before ( :each ) { reset_email }

Also, the select with country might be not clear - the collection with countries comes from country_select gem.

We have some failing acceptance tests, it will take some time to make them all green. Now we can write some migrations:

rails generate model User email encrypted_password rails generate model UserProfile user_id :integer age :integer country

We also need to add some database constraints, to ensure that users' emails are unique and fields are not null, the migrations would look like this:

class CreateUsers < ActiveRecord :: Migration def change create_table :users do | t | t . string :email , null: false t . string :encrypted_password , null: false t . timestamps end add_index :users , :email , unique: true end end class CreateUserProfiles < ActiveRecord :: Migration def change create_table :user_profiles do | t | t . integer :age t . string :country , null: false t . integer :user_id , null: false t . timestamps end add_index :user_profiles , :user_id end end

Now we have to define some routes:

# config/routes.rb root to: "static_pages#home" resources :users do end

For user registration, we have REST actions: new and create . Let's also add some root page, currently just to get rid of default Rails page:

# app/controllers/static_pages_controller.rb class StaticPagesController < ApplicationController def home end end

But how to deal with UsersController and form for user registration? We have some fields that are not present in models ( password/password_confirmation and policy ). The popular solution would be using: accepts_nested_attributes_for :profile and some virtual attributes. I don't really like this solution, accepts_nested_attributes_for sometimes can really save a lot of time, especially with complex nested forms with nested_form gem. But virtual attributes are quite ugly and they make models the interfaces for forms. Much better approach is to use form objects. There's a great gem for this kind of problems: Reform - we will use it here.

# app/controllers/users_controller.rb class UsersController < ApplicationController def new @registration_form = registration_form end def create end private def registration_form UserRegistrationForm . new ( user: User . new , profile: UserProfile . new ) end end

That's it for UsersController , we will need some views and the actual form object:

# app/views/users/new.html.haml % h1 User Registration = simple_form_for @registration_form do | f | = f . input :email , label: "email" = f . input :country , label: "country" , as: :country = f . input :age , label: "age" = f . input :password , label: "password" = f . input :password_confirmation , label: "password confirmation" = f . input :policy , label: "I accept the policy" , as: :boolean = f . submit "Register"

And the actual UserRegistrationForm :

# app/forms/user_registration_form.rb class UserRegistrationForm < Reform :: Form include Reform :: Form :: ActiveRecord include Composition property :email , on: :user property :password , on: :nil , empty: true property :password_confirmation , on: :nil , empty: true property :age , on: :profile property :country , on: :profile property :policy , on: :nil , empty: true validates :email , presence: true , email: true , uniqueness: { case_sensitive: false } validates :password , presence: true , confirmation: true validates :age , presence: true validates :country , presence: true validates :policy , acceptance: true , presence: true model :user end

Reform is not (yet) that popular in the Rails community so some things require explanation (check also the docs out). The Reform::Form::ActiveRecord module is for uniqueness validation and the Composition is for... composition - some properties are mapped to user and other to profile. There is also a mystical mapping with on: :nil - these are "virtual" properties like password , password_confirmation and policy - all properties must be mapped to a resource so just to satisfy Reform API I use on: :nil as a convention, also the empty: true option is for virtual attributes that won't be processed. And where does the email validation come from? From our custom validator, let's write some specs but before we should add /forms (and /usecases for business logic) directories to be autoloaded:

# config/application.rb config . autoload_paths += %W( #{ config . root } /app/usecases) config . autoload_paths += %W( #{ config . root } /app/forms)

# spec/usecases/email_validator_spec.rb require 'spec_helper' class DummyModel include ActiveModel :: Validations attr_accessor :email validates :email , email: true end describe EmailValidator do let ( :model ) { DummyModel . new } it "validates email format" do valid_emails = %w[email@example.com name.surname@email.com e-mail@example.com] valid_emails . each do | email | model . email = email expect ( model ). to be_valid end invalid_emails = %w[email @email.com email.example.com email@example email@example.] invalid_emails . each do | email | model . email = email expect ( model ). not_to be_valid end end end

You can probably come up with some more examples to cover email validation but these are sufficient cases. I've introduced DummyModel here to have a generic object that can be validated so the ActiveModel::Validations module is needed and an accessor for an email. Let's implement the actual validation:

# usecases/email_validator.rb class EmailValidator < ActiveModel :: EachValidator def validate_each ( record , attribute , value ) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i record . errors [ attribute ] << ( options [ :message ] || "is not a valid email format" ) end end end

The regexp for email validation comes from Rails guides:). It won't cover all the possibilities but the actual format of the email is an overkill.

I don't fell the need to write tests for other validations and composition for UserRegistrationForm : it's just using very descriptive DSL, the validation are already tested in Rails.

We haven't set up the associations yet in models:

# app/models/user.rb class User < ActiveRecord :: Base has_one :profile , class_name: "UserProfile" , inverse_of: :user validates :email , presence: true , uniqueness: { case_insensitive: false }, email: true validates :encrypted_password , presence: true

# app/models/user_profile.rb class UserProfile < ActiveRecord :: Base belongs_to :user , inverse_of: :profile validates :user , :country , :age , presence: true end

I added also validations in models. These may seem like a duplication because form object already implements them but these are validations always applicable do these models so it is a good idea to have them in models.

Let's concentrate on UsersController and create action. I don't really like testing controllers, especially for CRUD-like stuff, user creation still feels like CRUD but not that typical in Rails, especially when using dedicated form object. So let's test drive registration process: we are going to use UserRegistrationForm for data aggregation and validation - if the data is valid, the user will be created by UserRegistration service object with redirection to root path, otherwise it will render new template.

# spec/controllers/users_controller_spec.rb require 'spec_helper' describe UsersController do describe "#create" do let ( :registration_form ) { instance_double ( UserRegistrationForm ) } let ( :user_params ) { { "email" => "email@example.com" } } let ( :params ) { { user: user_params } } let ( :user_registration ) { instance_double ( UserRegistration ) } before ( :each ) do allow ( UserRegistrationForm ). to receive ( :new ) { registration_form } allow ( registration_form ). to receive ( :assign_attributes ) . with ( user_params ) { registration_form } allow ( user_registration ). to receive ( :register! ) . with ( registration_form ) { true } allow ( UserRegistration ). to receive ( :new ) { user_registration } end context "valid data" do before ( :each ) do expect ( registration_form ). to receive ( :valid? ) { true } post :create , params end it "executes registration" do expect ( user_registration ). to have_received ( :register! ). with ( registration_form ) end it "redirects to root path" do expect ( response ). to redirect_to root_path end end context "invalid data" do before ( :each ) do expect ( registration_form ). to receive ( :valid? ) { false } post :create , params end it "renders registration form" do expect ( response ). to render_template :new end end end end

Well, it is not really clear, that's the problem with testing controllers and they should be as thin as possible. We need to implement the assign_attributes method in form object to fill models' attributes with params and implement the actual UserRegistration usecase. In tests I use instance_double instead of simple double to make sure I'm not stubbing non-existent methods or with wrong number of arguments - that's a great feature introduced in RSpec 3, which comes from rspec-fire gem. Also, I'm stubbing responses so that I can spy on them using have_received method - it's much cleaner and easier to read. Compare these two examples:

before ( :each ) do expect ( registration_form ). to receive ( :valid? ) { true } post :create , params end it "executes registration" do expect ( user_registration ). to have_received ( :register! ). with ( registration_form ) end it "redirects to root path" do expect ( response ). to redirect_to root_path end

and

before ( :each ) do expect ( registration_form ). to receive ( :valid? ) { true } end it "executes registration" do expect ( user_registration ). to receive ( :register! ). with ( registration_form ) post :create , params end it "redirects to root path" do post :create , params expect ( response ). to redirect_to root_path end

I really encourage you to spy on a stubbed method, I will make your tests much more readable and DRY them up.

I made also some non-standard design decisions here: why not to implement the persistence logic in the form object and use it like:

if @registration_form . persist ( user_params ) # populate data, perform validation and persist data if is valid # happy paths else # failure path end

For simple persistence logic I would probably go with that approach but we will also need to send some confirmation instructions, admin notifications etc., I'm not really comfortable with the idea of form object knowing something about sending notifications, persistence alone would be ok, it would be quite convenient to use but this is too complex, I would leave form object for data aggregation and validation. Let's write code for the controller:

# app/controllers/users_controller.rb class UsersController < ApplicationController def create @registration_form = registration_form . assign_attributes ( params [ :user ]) if @registration_form . valid? UserRegistration . new . register! ( @registration_form ) redirect_to root_path , notice: "You have register. Please, check your email for confimartion instructions" else render :new end end private def registration_form UserRegistrationForm . new ( user: User . new , profile: UserProfile . new ) end end

We need to implement assign_attributes method (we have nice failure message thanks to instance_double that informs us about it):

Failure / Error : allow ( registration_form ). to receive ( :assign_attributes ) UserRegistrationForm does not implement: assign_attributes

and UserRegistration. Let's start from test for assign_attributes method. It looks like, besides assigning params, it should return itself:

# spec/forms/user_registration_form_spec.rb require 'spec_helper' describe UserRegistrationForm do let ( :user ) { User . new } let ( :profile ) { UserProfile . new } subject { UserRegistrationForm . new ( user: user , profile: profile ) } describe "#assign_attributes" do it "populates models' attributes with params" do subject . assign_attributes ( "email" => "email@example.com" , "country" => "Poland" ) expect ( subject . user . email ). to eq "email@example.com" expect ( subject . profile . country ). to eq "Poland" end it "assigns profile to user" do subject . assign_attributes ({}) expect ( user . profile ). to eq profile end it "returns self" do expect ( subject . assign_attributes ({})). to eq subject end end end

And the code for implementation:

# app/forms/user_registration_form.rb def assign_attributes ( params ) from_hash ( params ) save_to_models self end

It uses some Reform::Form private methods that I found in source code so this implementation might not be stable but fortunately we have it covered in tests so we will know breaking changes if it happens in next versions. And there's a gotcha here: The keys in hash must be stringified, symbols won't work (applies to 0.2.4 version of Reform).

Let's write some minimal implementation for UserRegistration to satisfy controller's specs:

# app/usecases/user_registration.rb class UserRegistration def register! ( aggregate ) end end

And what the UserRegistration should be responsible for? Let's start with persistence logic: user with it's profile must be created and the encrypted password should be assigned to the user. We will also need registration profile to be created.

# spec/usecases/user_registration_spec.rb require 'spec_helper' describe UserRegistration do let ( :user ) { FactoryGirl . build_stubbed ( :user ) } let ( :profile ) { FactoryGirl . build_stubbed ( :user_profile ) } let ( :form ) { double ( :form , user: user , profile: profile , password: "password" ) } subject { UserRegistration . new ( encryption: encryption ) } let ( :encrypted_password ) { "encrypted_password" } let ( :encryption ) { instance_double ( Encryption , generate_password: encrypted_password ) } context "persistence is success" do before ( :each ) do allow ( user ). to receive ( :save! ) { true } allow ( profile ). to receive ( :save! ) { true } allow ( user ). to receive ( :create_registration_profile! ) { true } end before ( :each ) do subject . register! ( form ) end specify "user gets encrypted password" do expect ( user . encrypted_password ). to eq encrypted_password end it "saves user" do expect ( user ). to have_received ( :save! ) end it "creates profile for user" do expect ( profile ). to have_received ( :save! ) end it "create registration profile for user" do expect ( user ). to have_received ( :create_registration_profile! ) end end context "persistence fails" do it "raises RegistrationFailed error" do allow ( user ). to receive ( :save! ) { raise_error ActiveRecord :: RecordInvalid } expect do UserRegistration . new . register! ( form ) end . to raise_error UserRegistration :: RegistrationFailed end end end

Note: keep in mind that you should write one test and then write minimal implementation to make it pass and then another test. I gave the several tests and the actual UserRegistration in advance, just to make it easier to read and follow.

It is quite clear from the tests what should be expected from this class: creation of user, profile, registration profile and assigning encrypted password. Data aggregate ( form ) is just a double with profile and user, we don't care what it actually is, it should just implement the stubbed interface. I also use FactoryGirl and build_stubbed method for initializing models - I find it more convenient than to use instance_double because instance doubles don't cover attributes from database tables.

The factories for User and profiles would look like that:

# spec/factories.rb FactoryGirl . define do factory :user do email "email@example.com" # I'll explain that later, why it is that long encrypted_password "$2a$10$bcMccS3q2egnNICPLYkptOoEyiUpbBI5Q.GAKe0or2QB7ij6yCeOa" end factory :user_profile do age 22 country "Poland" end end

And the actual implementation:

# app/usecases/user_registration.rb class UserRegistration class RegistrationFailed < StandardError ; end attr_reader :encryption private :encryption def initialize ( options = {}) @encryption = options . fetch ( :encryption , Encryption . new ) end def register! ( aggregate ) user = aggregate . user profile = aggregate . profile user . encrypted_password = encrypted_password ( aggregate . password ) ActiveRecord :: Base . transaction do begin user . save! profile . save! user . create_registration_profile! rescue :: ActiveRecord :: StatementInvalid , :: ActiveRecord :: RecordInvalid => e raise_registration_error ( e ) end end end private def raise_registration_error ( errors ) message = "Registration Failed due to the following errors: #{ errors } " raise UserRegistration :: RegistrationFailed , message end def encrypted_password ( password ) encryption . generate_password ( password ) end end

Let's discuss some design decisions: the constructor accepts options hash so that we can inject dependencies like encryption and to provide defaults if it's injected. The persistence logic is wrapped in transaction block so that e.g. user won't be created if profile creation fails. If it fails, RegistrationFailed error is raised with a descriptive message. Also, the encryption is private: we don't need it to be public.

To satisfy tests, the create_registration_profile! must be implemented and generate_password for encryption. Fortunately, we just need to setup associations for UserRegistrationProfile to have create_registration_profile! implemented. But we need to generate the model first:

rails generate model UserRegistrationProfile confirmed_at :datetime confirmation_instructions_sent_at :datetime confirmation_token user_id :integer

let's set up some database constraints in generated migration:

class CreateUserRegistrationProfiles < ActiveRecord :: Migration def change create_table :user_registration_profiles do | t | t . datetime :confirmed_at t . datetime :confirmation_instructions_sent_at t . string :confirmation_token t . integer :user_id , null: false t . timestamps end add_index :user_registration_profiles , :user_id add_index :user_registration_profiles , :confirmation_token , unique: true end end

and then write the associations:

# app/models/user.rb class User < ActiveRecord :: Base has_one :registration_profile , class_name: "UserRegistrationProfile" , inverse_of: :user end

# app/models/user_registration_profile.rb class UserRegistrationProfile < ActiveRecord :: Base belongs_to :user , inverse_of: :registration_profile validates :user , presence: true end

The minimal implementation for Encryption to make the UserRegistration tests happy is the following:

# app/usecases/encryption.rb class Encryption def generate_password ( phrase ) end end

To finish the user creation we have to implement the password generation. Bcrypt and it's create password method is a reasonable choice here. Let's write the tests:

# spec/usecases/encryption_spec.rb require 'spec_helper' describe Encryption do subject { Encryption . new } let ( :password ) { "password" } let ( :encypted_password ) { "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa" } let ( :password_generator ) { class_double ( BCrypt :: Password ). as_stubbed_const } before ( :each ) do allow ( password_generator ). to receive ( :create ). with ( password ) { encypted_password } end it "creates password using Bcrypt as default" do expect ( subject . generate_password ( password )). to eq encypted_password end end

The encrypted_password doesn't have to be that long but looks more genuine that way. The BCrypt::Password is also a class double so that we make sure we don't stub a non-existent method. And the implementation of Encryption class:

# app/usecases/encryption.rb class Encryption attr_reader :password_generator private :password_generator def initialize ( args = {}) @password_generator = args . fetch ( :password_generator , BCrypt :: Password ) end def generate_password ( phrase ) password_generator . create ( phrase ) end end

The pattern for constructor is similar to the one from UserRegistration . The password_generator is also made private - the rule of thumb is that everything should be private unless it needs to be public, just to keep the interfaces clean.

Now we have the basic implementation for user creation with it's profiles. Still, we need confirmation stuff and notification to tje admin. It is beyond the UserRegistration responsibilities, we also don't need always to a notification or confirmation instructions or to confirm user at all, just to have the interface flexible enough. Maybe we will have some additional things that will take place during registration - like third party API notification. To keep the responsibilities separate and UserRegistration easy to use, we can implement all the additional actions as the listeners that are being passed to the constructor of UserRegistration . Let's write specs for it first:

# spec/usecases/user_registration_spec.rb require 'spec_helper' describe UserRegistration do # same code a before context "persistence is success" do # same code a before # and this is new: context "with listeners" do let ( :user_confirmation ) { double ( :user_confirmation , notify: true ) } let ( :admin_notification ) { double ( :admin_notification , notify: true ) } before ( :each ) do UserRegistration . new ( user_confirmation , admin_notification , encryption: encryption ). register! ( form ) end it "notifies user_confirmation listener" do expect ( user_confirmation ). to have_received ( :notify ). with ( user ) end it "notifies admin_notificaiton listener" do expect ( admin_notification ). to have_received ( :notify ). with ( user ) end end end end

We don't actually care what the listeners are, the only requirement is that they must implement the same interface: notify method which takes user argument. And the implementation:

# app/usecases/user_registration.rb class UserRegistration class RegistrationFailed < StandardError ; end attr_reader :encryption , :listeners private :encryption , :listeners def initialize ( * listeners , ** options ) @listeners = listeners @encryption = options . fetch ( :encryption , Encryption . new ) end def register! ( aggregate ) user = aggregate . user profile = aggregate . profile user . encrypted_password = encrypted_password ( aggregate . password ) ActiveRecord :: Base . transaction do begin user . save! profile . save! user . create_registration_profile! rescue :: ActiveRecord :: StatementInvalid , :: ActiveRecord :: RecordInvalid => e raise_registration_error ( e ) end end notify_listeners ( user ) end private def raise_registration_error ( errors ) message = "Registration Failed due to the following errors: #{ errors } " raise UserRegistration :: RegistrationFailed , message end def encrypted_password ( password ) encryption . generate_password ( password ) end def notify_listeners ( user ) listeners . each do | listener | listener . notify ( user ) end end end

These changes are not that noticeable but they are huge. The constructor now takes some listeners (splat) - we can pass one listener, several or none, it will always be an array. Also, the options is now a keyword argument introduced in Ruby 2.0 which makes the changes really smooth. And the new method: notify_listeners which sends notify message to all the listeners with user argument.

To handle the user confirmation stuff we will need, well, UserConfirmation and UserRegistrationAdminNotification to handle the notifcations.

Let's start with UserConfirmation . We need notify method which will take care of: assigning confirmation token, which must be unique, setting date when the confirmation instructions were sent and sending the instructions. We will need some mailer here ( UserConfirmationMailer ), clock ( DateTime ) and something to generate token - SecureRandom will be a good fit here with it's base64 method. Let's translate the specification to the tests:

# spec/factories.rb FactoryGirl . define do # same as before factory :user_registration_profile do # this in new here end end

# spec/usecases/user_confirmation_spec.rb require 'spec_helper' describe UserConfirmation do describe "#notify" do let! ( :user ) { FactoryGirl . build_stubbed ( :user , registration_profile: FactoryGirl . build_stubbed ( :user_registration_profile )) } let ( :mailer_stub ) { double ( :mailer , deliver: true ) } let! ( :mailer ) { class_double ( UserConfirmationMailer , send_confirmation_instructions: mailer_stub ). as_stubbed_const } let ( :confirmation_instructions_sent_date ) { DateTime . new ( 2014 , 2 , 23 , 21 , 0 , 0 )} let ( :clock ) { double ( :clock , now: confirmation_instructions_sent_date ) } subject { UserConfirmation . new ( mailer: mailer , clock: clock ) } before ( :each ) do allow ( user ). to receive ( :save_with_profiles! ) allow ( SecureRandom ). to receive ( :base64 ) { "token" } subject . notify ( user ) end it "assigns confirmation token to user" do expect ( user . confirmation_token ). to eq "token" end it "sends email with confirmation instructions" do expect ( mailer ). to have_received ( :send_confirmation_instructions ). with ( user ) end it "sets date when the confirmation instructions have been sent" do expect ( user . confirmation_instructions_sent_at ). to eq confirmation_instructions_sent_date end it "persists new data" do expect ( user ). to have_received ( :save_with_profiles! ) end end end

Like before, we should start with one test, make it pass and then write the next one. Here is the implementation for it:

# app/usecases/user_confirmation.rb class UserConfirmation attr_reader :mailer , :clock private :mailer , :clock def initialize ( args = {}) @mailer = args . fetch ( :mailer , UserConfirmationMailer ) @clock = args . fetch ( :clock , DateTime ) end def notify ( user ) assign_confirmation_token ( user ) user . confirmation_instructions_sent_at = clock . now mailer . send_confirmation_instructions ( user ). deliver user . save_with_profiles! end private def assign_confirmation_token ( user ) begin user . confirmation_token = SecureRandom . base64 ( 20 ) end while User . find_by_confirmation_token ( user . confirmation_token ). present? end end

The pattern for constructor is similar to the previous ones: provide the way to inject dependencies and some defaults if they are not specified so it is more flexible, less coupled and the testing becomes easier as a bonus. We have while loop to ensure the confirmation token is unique amongst users. The find_by_attribute methods are deprecated since Rails 4.0.0 and the activerecord-deprecated_finders will be removed from dependencies in 4.1.0 so we have to implement our own finder method. Here are also some important design decisions - we assign both confirmation_instructions_sent_at and confirmation_token to the user, not the registration profile. How is that? The important question is: do we need to expose that the user has registration profile? What if we change our mind and decide to put this data in "normal" profile, not registration profile? Or we didn't make a decision to create a registration profile at all in a first place and these attributes belonged to the user since the beginning and we later decided to move them to a separated table? From the UserConfirmation perspective, it is just an implementation detail. The save_with_profiles! is provided to make user's data persistence more convenient. We need to implement mailer as well but let's start with user's related stuff.

# spec/models/user.rb require 'spec_helper' describe User do subject { User . new ( email: "email@example.com" , encrypted_password: "password" ) } describe ".find_by_confirmation_token" do let! ( :user ) { FactoryGirl . create ( :user ) } before ( :each ) do FactoryGirl . create ( :user_registration_profile , confirmation_token: "token" , user_id: user . id ) end it "finds user with specified confirmation token" do expect ( User . find_by_confirmation_token ( "token" )). to eq user end end describe "#confirmation_token=" do it "assigns confirmation token to user" do subject . confirmation_token = "token" expect ( subject . confirmation_token ). to eq "token" end end describe "#confirmation_instructions_sent_at=" do it "assigns confirmation instructions sent date to user" do date = DateTime . now subject . confirmation_instructions_sent_at = date expect ( subject . confirmation_instructions_sent_at ). to eq date end end end

The find_by_confirmation_token finder method is pretty easy but it involves another table with registration profile so I decided to write test for it. The tests also suggest that we need readers for these attributes, not only the writers. Let's use delegate macro from ActiveSupport for it:

# app/models/user.rb class User < ActiveRecord :: Base # the same code as before # new code delegate :confirmation_token , :confirmation_instructions_sent_at , :confirmed_at , to: :registration_profile , allow_nil: true def self . find_by_confirmation_token ( token ) joins ( :registration_profile ) . where ( "user_registration_profiles.confirmation_token = ?" , token ) . first end def confirmation_token = ( token ) ensure_registration_profile_exists registration_profile . confirmation_token = token end def confirmation_instructions_sent_at = ( date ) ensure_registration_profile_exists registration_profile . confirmation_instructions_sent_at = date end def save_with_profiles! User . transaction do save! profile . save! if profile registration_profile . save! if registration_profile end end private def ensure_registration_profile_exists build_registration_profile if registration_profile . blank? end

Before making any assignment, we have to make sure that the registration profile exists. The same applies to the persistence, which is again wrapped in a transaction. And let's implement the mailer for sending confirmation instructions:

rails generate mailer UserConfirmationMailer send_confirmation_instructions

Some basic tests to prove that the mailer actually works:

# spec/mailers/user_confirmation_mailer_spec.rb require "spec_helper" describe UserConfirmationMailer do describe "#send_confirmation_instructions" do let! ( :user ) { FactoryGirl . build_stubbed ( :user , email: "email@example.com" , registration_profile: FactoryGirl . build_stubbed ( :user_registration_profile , confirmation_token: "token" )) } let ( :mail ) { UserConfirmationMailer . send_confirmation_instructions ( user ) } it "has proper subject" do expect ( mail . subject ). to eq ( "Confirmation Instructions" ) end it "sends email to the user" do expect ( mail . to ). to eq ([ user . email ]) end it "has link to confirm account" do url = "/confirmations/ #{ user . confirmation_token } " expect ( mail . body . encoded ). to match ( url ) end end end

The setup with FactoryGirl may seem to be complex but I like doing this kind of setup manually, not to rely on predefined attributes for the factory so that I know where the data comes from. We also assume that there will be some controller action for confirmations so we will need to define routes to make the tests pass:

#app/mailers/user_confirmation_mailer.rb class UserConfirmationMailer < ActionMailer :: Base default from: "contact@email.com" def send_confirmation_instructions ( user ) @user = user mail ( to: user . email , subject: "Confirmation Instructions" ) end end

# app/views/user_confirmation_mailer/send_confirmation_instructions.html.erb <p> To complete the registration process, click the link below: </p> < %= link_to " Confirm ", user_confirmation_path ( token: @ user . confirmation_token ) % >

And the route with a controller action:

# config/routes.rb get '/confirmations/:token' , to: "confirmations#confirm" , as: :user_confirmation

# app/controllers/confirmations_controller.rb class ConfirmationsController < ApplicationController def confirm end end

And add the listener in UsersController :

# app/controllers/users_controller.rb def create @registration_form = registration_form . assign_attributes ( params [ :user ]) if @registration_form . valid? UserRegistration . new ( UserConfirmation . new ). register! ( @registration_form ) redirect_to root_path , notice: "You have register. Please, check your email for confimartion instructions" else render :new end end private def registration_form UserRegistrationForm . new ( user: User . new , profile: UserProfile . new ) end

To make all the tests happy, we need some to send a notification to the admin. It looks like UserRegistrationAdminNotification will be just an adapter layer for NewUserAdminNotificationMailer to provide the listener interface. The tests and the implementation are quite simple:

# spec/usecases/user_registration_admin_notification_spec.rb require 'spec_helper' describe UserRegistrationAdminNotification do describe "#notify" do let! ( :user ) { FactoryGirl . build_stubbed ( :user ) } let ( :mailer_stub ) { double ( :mailer , deliver: true ) } let! ( :mailer ) { class_double ( NewUserAdminNotificationMailer , notify: mailer_stub ). as_stubbed_const } subject { UserRegistrationAdminNotification . new ( mailer: mailer ) } before ( :each ) do subject . notify ( user ) end it "sends email to admin about new user being registered" do expect ( mailer ). to have_received ( :notify ). with ( user ) end end end

The implementation:

# app/usecases/user_registration_admin_notification.rb class UserRegistrationAdminNotification attr_reader :mailer private :mailer def initialize ( args = {}) @mailer = args . fetch ( :mailer , NewUserAdminNotificationMailer ) end def notify ( user ) mailer . notify ( user ). deliver end end

We also need to generate the mailer with notify method (yes, the same as for the listener but it is good enough here):

rails generate mailer NewUserAdminNotificationMailer notify

and the simple implementation to make the tests green:

# app/mailers/new_user_admin_notification_mailer.rb class NewUserAdminNotificationMailer < ActionMailer :: Base default from: "contact@email.com" def notify ( user ) @user = user mail ( to: "admin@example.com" , subject: "New User Registration" ) end end

Now the tests for the mailer and we are almost finished with the registration:

# spec/mailers/new_user_admin_notification_mailer_spec.rb require "spec_helper" describe NewUserAdminNotificationMailer do describe "#notify" do let! ( :user ) { FactoryGirl . build_stubbed ( :user , email: "email@example.com" ) } let ( :mail ) { NewUserAdminNotificationMailer . notify ( user ) } it "sends email to the admin" do expect ( mail . to ). to eq ([ "admin@example.com" ]) end it "has link to confimartion in the body" do expect ( mail . body . encoded ). to match ( user . email ) end end end

The views for the mailer:

# app/views/new_user_admin_notification_mailer/notify.html.erb < p > New user with email: < %= @user.email %> has registered</p>

And the listener for UserRegistrationAdminNotification in UserRegistrationAdminNotification.new :

# app/controllers/users_controller.rb def create @registration_form = registration_form . assign_attributes ( params [ :user ]) if @registration_form . valid? UserRegistration . new ( UserConfirmation . new , UserRegistrationAdminNotification . new ). register! ( @registration_form ) redirect_to root_path , notice: "You have register. Please, check your email for confimartion instructions" else render :new end end private def registration_form UserRegistrationForm . new ( user: User . new , profile: UserProfile . new ) end

Hell yeah, all tests are happy now, we have completed the user registration feature. Let's add account confirmation feature and simple sign in. We don't need acceptance test or integration test in controller for that feature, it's pretty simple and unit test for controller would be enough. We probably need to find user by confirmation token, confirm the account and redirect to some page. Also, we should return 404 error if there's no match for confirmation token. In a real world application it would probably need some expiration date for token and other features but keep in a mind it's just for demonstration purposes, not writing the complete devise-like solution.

# spec/controllers/confirmations_controller_spec.rb require 'spec_helper' describe ConfirmationsController do describe "#confirm" do let! ( :user ) { FactoryGirl . build_stubbed ( :user , registration_profile: FactoryGirl . build_stubbed ( :user_registration_profile , confirmation_token: "token" )) } let ( :factory ) { class_double ( User ). as_stubbed_const } before ( :each ) do allow ( factory ). to receive ( :find_by_confirmation_token! ) . with ( user . confirmation_token ) { user } end it "it confirms user and redirects to root path" do expect ( user ). to receive ( :confirm! ) { true } get :confirm , token: user . confirmation_token expect ( response ). to redirect_to root_path end end end

We find the user with bang method so, by convention, it raises ActiveRecord::RecordNotFound if the resource is not found - we won't write test for the failure path. Then the confirm! method is used which needs to be implemented and redirect to root path. The implementation for the controller is the following:

# app/controllers/confirmations_controller.rb class ConfirmationsController < ApplicationController def confirm user = User . find_by_confirmation_token! ( params [ :token ]) user . confirm! redirect_to root_path , notice: "You have confirmed you account. Now you can login." end end

Now we need to implement confirm! and find_by_confirmation_token! methods:

# spec/models/user_spec.rb require 'spec_helper' describe User do subject { User . new ( email: "email@example.com" , encrypted_password: "password" ) } # some old code describe "#confirm!" do let ( :date ) { DateTime . new ( 2014 , 02 , 23 , 22 , 6 , 0 ) } before ( :each ) do allow ( DateTime ). to receive ( :now ) { date } subject . confirm! end it "assigns confirmation date with current date" do expect ( subject . confirmed_at ). to eq date end it "persists user and the profile" do expect ( subject . registration_profile . persisted? ). to eq true expect ( subject . persisted? ). to eq true end end end

It would be quite convenient to use confirm! method for new user and make it persisted. I don't feel a need to write test for find_by_confirmation_token! as it is really simple and will use find_by_confirmation_token .

# app/models/user.rb class User < ActiveRecord :: Base def self . find_by_confirmation_token! ( token ) user = find_by_confirmation_token ( token ) if user . blank? raise ActiveRecord :: RecordNotFound else user end end def confirm! ensure_profile_exists registration_profile . confirmed_at = DateTime . now save_with_profiles! end private def ensure_registration_profile_exists build_registration_profile if registration_profile . blank? end end

It's another method in User model, shouldn't the models be thin? Well, it's not business logic involving some complex actions, these are just domain methods for user to handle it's own state (or the profile's state which is rather an implementation detail in this case), it looks like a model's responsibility so it's a right place to add this kind of logic, much better than using e.g. update method on registration profile outside the models.

We completed another feature: user can confirm his/her account. There's only one feature left: sign in. Let's test drive it starting from acceptance test again: when the user exists and is confirmed, we let the user sign in, if exists but is not confirmed yet we render proper info and if the email/password combination is invalid we also want to display proper info. Capybara test for these specs may look like this:

# spec/features/sign_in_spec.rb require 'spec_helper' feature "Sign In" do context "user is confirmed" do given! ( :user ) { FactoryGirl . create ( :user , registration_profile: FactoryGirl . build ( :user_registration_profile , confirmed_at: DateTime . now )) } describe "with valid data" do background do visit sign_in_path fill_in_sign_in_form_with_valid_data ( user ) sign_in end scenario "user signs in and and shows success message" do expect ( page ). to have_content "You have successfully signed in" end scenario "after sign in user sees it's email" do expect ( page ). to have_content user . email end end describe "with invalid data" do scenario "signin is prohibited and user sees error message with wrong email/password combination" do visit sign_in_path fill_in_sign_in_form_with_invalid_data ( user ) sign_in expect ( page ). to have_content "Wrong email/password combination" end end end context "user is not confirmed" do given! ( :user ) { FactoryGirl . create ( :user , registration_profile: FactoryGirl . build ( :user_registration_profile )) } background do visit sign_in_path fill_in_sign_in_form_with_valid_data ( user ) sign_in end scenario "signin is prohibited and user sees info about unconfirmed account" do expect ( page ). to have_content "You must confirm your account" end end end def fill_in_sign_in_form_with_valid_data ( user ) fill_in "email" , with: user . email fill_in "password" , with: "password" end def fill_in_sign_in_form_with_invalid_data ( user ) fill_in "email" , with: user . email fill_in "password" , with: "wrong_password" end def sign_in click_button "Sign in" end

The structure is similar to the one from registration process: there are some helper methods for filling forms and signing in. For the happy path, we also want to verify that the user is actually signed in so we will display it's email. This test will work because in User factory in spec/factories.rb the encrypted_password value is an encrypted form of "password" phrase. Let's start from defining routes and creating controller for the user signin:

# config/routes.rb resources :sessions , except: [ :new ] get '/sign_in' , to: "sessions#new" , as: :sign_in

# app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create end end

and the view layer:

# app/views/sessions/new.html.haml = display_messages % h1 Sign In = simple_form_for :sign_in , url: :sessions , method: :post do | f | = f . input :email , label: "email" = f . input :password , label: "password" = f . submit "Sign in"

Where does the display_messages helper comes from? It's a simple helper for displaying flash messages which can be implemented as follows:

# app/helpers/application_helper.rb module ApplicationHelper def display_messages case when flash [ :notice ] display_flash_message ( flash [ :notice ], "alert-success" ) when flash [ :error ] display_flash_message ( flash [ :error ], "alert-error" ) when flash [ :alert ] display_flash_message ( flash [ :alert ], "alert-error" ) end end def display_flash_message ( message , class_name ) content_tag ( :div , class: "alert centerize-text #{ class_name } " ) do message end end end

Let's also update the root page:

# app/views/static_pages/home.html.haml = display_messages - if current_user % h1 Welcome = current_user . email

Let's stick to the convention and name the helper method with current user the current_user . The signin process is already covered by acceptance test so we won't benefit much from writing controller's test. To keep track of current user, we will store it's id in a session. The implementation might be following:

# app/controllers/sessions_controller.rb class SessionsController < ApplicationController def create user = User . find_by ( email: sign_in_params [ :email ]) return wrong_combination_of_email_or_password if user . blank? return user_not_confirmed if ! user . confirmed? if Authentication . authenticate ( user . encrypted_password , sign_in_params [ :password ]) sign_in ( user ) redirect_to root_path , notice: "You have successfully signed in" else wrong_combination_of_email_or_password end end private def sign_in_params params . require ( :sign_in ). permit ( :email , :password ) end def sign_in ( user ) session [ :user_id ] = user . id end def wrong_combination_of_email_or_password flash . now [ :error ] = "Wrong email/password combination" render :new end def user_not_confirmed flash . now [ :error ] = "You must confirm your account" render :new end end

And for the current_user :

# app/controllers/application_controller.rb class ApplicationController < ActionController :: Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception helper_method :current_user private def current_user @current_user ||= User . find ( session [ :user_id ]) if session [ :user_id ]. present? end end

The last step is to implement Authentication module with authenticate method, which compares encrypted password with plain password. Let's start with a test:

# spec/usecases/authentication_spec.rb require 'spec_helper' describe Authentication do describe ".authenticate" do let ( :encrypted_password ) { "$2a$10$bcMccS3q2egnNICPLYkptOoEyiUpbBI5Q.GAKe0or2QB7ij6yCeOa" } it "returns true if encrypted password is specified password" do expect ( Authentication . authenticate ( encrypted_password , "password" )). to eq true end it "returns false if encrypted password in not specified password" do expect ( Authentication . authenticate ( encrypted_password , "wrong_password" )). to eq false end end end

I don't need to create an instance of Authentication , there is no need to make it a class. And to make all the tests green we just need to implement comparison of passwords using Bcrypt:

# app/usecases/authentication.rb module Authentication def self . authenticate ( encrypted_password , password ) BCrypt :: Password . new ( encrypted_password ) == password end end

Wrapping up

That's all! All the tests now pass. And they run pretty fast (on Ruby 2.1.0), about 1.6 s. That was quite long: the user registration, confirmation and sign in features have been test drived and some not obvious design decisions were made. That gives some basic ideas how I apply Test Driven Development / Behavior Driven Development techniques in everyday Rails programming. The aim of these tests wasn't to have 100% coverage (e.g. I didn't test ActiveModel validations, using Reform DSL to make UserRegistrationForm composition, delegations in User model) but they give me sufficient level of confidence to assume that the application works correctly and they helped with some design choices, which is a great advantage of unit tests. When TDDing, keep in mind what Kent Beck says about his way of writing tests:

I get paid for code that works, not for tests so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards but that could just be hubris). If I don't typically make a kind of mistake (like setting the wrong variables in a constructor), I don't test for it.