Use Android API in NativeScript and Angular2 to Manipulate Images

nativeangular

One of the great things with NativeScript is the possibility to use standard web styling and templating tools (namely HTML and CSS) to build your cross-platform mobile application. Unfortunately, some features are not (yet) available, like for example the CSS background: linear-gradient property or filter. Fortunately though, NativeScript provides an almost full access to both iOS and Android native API, allowing to access powerful features, such as advanced image processing.
In this article, I will show you how to perform 3 image processing manipulation, using the Android API with TypeScript.

Before heading to the cases, go ahead and create a new NativeScript project using Angular2

tns create ImageProcessingApp --ng

Then, add the android platform support to the newly created application

tns platform add android

We will also need to add the package to be able to access the Android API withing a TypeScript source file. Run the following command within the ImageProcessingApp directory:

npm -i tns-platform-declarations

Then, add the following line to the reference.d.ts file

/// <reference path="./node_modules/tns-platform-declarations/tns-core-modules/android17.d.ts" />

Add a Linear Gradient Effect

This section will show you how to add a gradient background to a NativeScript View. To do this, we’re going to use the Android class GradientDrawable. Open up app.component.ts and add an AbsoluteLayout to the template, which will serve as the placeholder for the gradient background.

import {Component} from "@angular/core";

@Component({
    selector: "my-app",
    template: "<AbsoluteLayout #background></AbsoluteLayout>",
})
export class AppComponent {
}

Notice the id we gave to the Layout. We’re going to need it in order to access it and start manipulate it via the Android API. Let’s start doing this right now:

import { Component } from "@angular/core";
import { ViewChild } from "@angular/core/src/metadata";
import { ElementRef } from "@angular/core/src/linker/element_ref";

@Component({
    selector: "my-app",
    template: "<AbsoluteLayout #background></AbsoluteLayout>"
})
export class AppComponent {
  @ViewChild("background") background: ElementRef;
}

We’ve used the ViewChild decorator around the background property, giving the id corresponding to the view we want to capture (#background). This is of type ElementRef.
Now, let’s create a new TypeScript file, hosting the function to apply a gradient background through the Android native API. We’ll place it in app/utils folder

// app/utils/background-processing.ts

import { View } from "ui/core/view";
import { Color } from "color";

export function setBackgroundGradient(args: { view: View, colorCodes: string[] }) {
  var backgroundDrawable = new android.graphics.drawable.GradientDrawable();
  var colors = [];
  args.colorCodes.forEach(function(c) {
    let color = new Color(c);
    colors.push(color.android);
  });
  backgroundDrawable.setColors(colors);
  backgroundDrawable.setGradientType(0); // Linear Gradient
  var orientation = android.graphics.drawable
  .GradientDrawable.Orientation.LEFT_RIGHT;
  backgroundDrawable.setOrientation(orientation);
  args.view.android.setBackgroundDrawable(backgroundDrawable);
}

We’e using a GradientDrawable object from the Android API. We take an array of color codes in input, transform them into an Android Color object and pass them to the GradientDrawable. The rest is pretty straightforward. And since we’re using TypeScript, we can get autocompletion suggestions straight to the objects from the library, which is really useful.

Now, we need to call this function within the app.component, with the given background View in input, and the colors we want for the gradient. Open up app.component.ts and update it like this:

import { Component } from "@angular/core";
import { View } from "ui/core/view";
import { ViewChild } from "@angular/core/src/metadata";
import { ElementRef } from "@angular/core/src/linker/element_ref";
import { OnInit } from "@angular/core/src/metadata/lifecycle_hooks";
import { setLinearGradient } from "./utils/background-processing"

@Component({
    selector: "my-app",
    template: "<AbsoluteLayout #background></AbsoluteLayout>",
})
export class AppComponent implements OnInit {
  @ViewChild("background") background: ElementRef;

  ngOnInit(){
    let backgroundView = <View>this.background.nativeElement;
    setLinearGradient({
      view: backgroundView,
      colorCodes: ['#FA7EFE', '#9F14FC']
    });
  }
}

We use the OnInit hook access the background View (we can’t do it within the constructor, because it would be too early as the view would not be created already). We then call the setLinearGradient function with the nativeElement of the View in input, as well as an array composed of the #FA7EFE and #9F14FC colors. The result:

gradient

Add a grayscale filter

For this effect, we’re going to use an existing image which we’ll put to the app/images folder. I’ll use the following image, so go ahead and download it:

NativeScript_Angular_logo

Open up utils/background-processing.ts und add the following method:

export function transformToGrayScale(view: View){
  var colorMatrix = new android.graphics.ColorMatrix();
  colorMatrix.setSaturation(0);
  var colorFilter = new android.graphics.ColorMatrixColorFilter(colorMatrix);
  view.android.setColorFilter(colorFilter);
}

Then, open up app.component.ts and write the following content:

import { Component } from "@angular/core";
import { View } from "ui/core/view";
import { ViewChild } from "@angular/core/src/metadata";
import { ElementRef } from "@angular/core/src/linker/element_ref";
import { OnInit } from "@angular/core/src/metadata/lifecycle_hooks";
import { transformToGrayScale } from "./utils/background-processing"

@Component({
    selector: "my-app",
    template: `
    <Image #image
    src="~/images/NativeScript_Angular_logo.png" 
    stretch="none" 
    verticalAlignment="middle"
    horizontalAlignment="center"></Image>
    `,
})
export class AppComponent implements OnInit {
  @ViewChild("image") image: ElementRef;

  ngOnInit(){
    let imageView = <View>this.image.nativeElement;
    transformToGrayScale(imageView);
  }
}

The result:

grayscale

Add a Blur Effect Using RenderScript

For this effect, we’re going to need a direct access to the picture we want to manipulate, and pass it to a RenderScript object. The picture I’m going to use can be downloaded here. Make sure to download the “Large” format (1356 x 2048). Then, implement the applyBlurredBackground() function, in background-processing.ts like this:

export function applyBlurredBackground(args:{ image: ImageSource, view: View }){
  const RADIUS = 25;
  var inputBitmap = android.graphics.Bitmap.createBitmap(args.image.android);
  var outputBitmap = android.graphics.Bitmap.createBitmap(inputBitmap);
  var rs = android.renderscript.RenderScript.create(app.android.context);
  var intrinsic = android.renderscript.ScriptIntrinsicBlur.create(rs, android.renderscript.Element.U8_4(rs));
  var tmpIn = android.renderscript.Allocation.createFromBitmap(rs, inputBitmap);
  var tmpOut = android.renderscript.Allocation.createFromBitmap(rs, outputBitmap);
  intrinsic.setRadius(RADIUS);
  intrinsic.setInput(tmpIn);
  intrinsic.forEach(tmpOut);
  tmpOut.copyTo(outputBitmap);
  var backgroundDrawable = new android.graphics.drawable.BitmapDrawable(app.android.context.getResources(), outputBitmap);
  args.view.android.setBackgroundDrawable(backgroundDrawable);
}

This one is slightly more complicated than the two others. The code related to the proccessing with RenderScript is actually an adapted version of the one I found on this article. You can adjust the radius constant to fit your needs, 25 is the max value (goes from 1 to 25). We need to pass two arguments: the actual image against wihch we want to apply the blur effect, and the view we want to use as a container.
Finally, we can use this function like this in app.component.ts:

import { Component } from "@angular/core";
import { View } from "ui/core/view";
import { ViewChild } from "@angular/core/src/metadata";
import { ElementRef } from "@angular/core/src/linker/element_ref";
import { OnInit } from "@angular/core/src/metadata/lifecycle_hooks";
import { applyBlurredBackground } from "./utils/background-processing"
import * as imageSource from "image-source";

@Component({
    selector: "my-app",
    template: `
    <AbsoluteLayout #background></AbsoluteLayout>
    `,
})
export class AppComponent implements OnInit {
  @ViewChild("background") background: ElementRef;

  ngOnInit(){
    let backgroundView = <View>this.background.nativeElement;
    let image = imageSource.fromFile("~/images/background.jpg");
    applyBlurredBackground({image: image, view: backgroundView });
  }
}

Just like we did for the Gradient effect in the first section, we define an AbsoluteLayout as a template, and give it the #background id. This allows us to retrieve it as ElementRef, through the @ViewChild decorator. We use the image-source library to retrieve the background from the images folder, and then, we pass them together to the applyBlurredBackground() function, which provides the following result:

blur

Create a Simple Calculator With NativeScript and Angular2

NativeScript_Angular_logo
NativeScript is a really exiting technology, which enables the development of native mobile applications using JavaScript, as well as CSS and HTML. Everything has been thought to make the transition from web to mobile development almost seamless, and this creates the possibility to build parts of your application that are usable for the web, and both Android and iOS mobile platforms. The best part: NativeScript also uses Angular2 and all the greatness that come with it (including TypeScript). This is what we’ll be using for this tutorial, which will show you how to build a really basic calculator for mobile. Let’s get started:

Setting up NativeScript

The easiest way to perform this step is to go and visit the official setup page from NativeScript. I’m personnaly using the Genymotion emulator and I really recommend it. The tutorial will use exclusively Android but since there is nothing really platform specific, this should work for iOS as well.

Starting the ns-calculator Project

To create the application using the NativeScript cli, run:

tns create ns-calculator --ng

The –ng flag indicates that we’ll be using Angular2. We then need to add the platform we want to support. As already said, I’ll be using Android, which is done through the following command:

tns platform add android

If you’d like to add iOS as well:

tns platform add ios

If this worked well, you should now have the following structure in place:
tree

the app folder is where we’ll spend all our time. For the first step, let’s open up app/app.component.ts and do some clean up in order to start from a healthy state:

import {Component} from "@angular/core";

@Component({
    selector: "my-app",
    templateUrl: "app.component.xml"
})
export class AppComponent {
}

The only thing I did is to delete the content from the AppComponent class, as well as replacing app.component.html with a .xml version. Both will actually do the job, and it’s just a matter of preference.

Next, let’s create the initial layout. Open up the app/app.component.xml, and put the following content:

<GridLayout columns="*, *, *, *" rows="auto, auto, *, *, *, *, *" >
  <Label class="result" text="RESULT" colSpan="4"></Label>
  <Label text="OPERATON" row="1" colSpan="4"></Label>
  <Button row="2" col="0" text="7"></Button>
  <Button row="2" col="1" text="8"></Button>
  <Button row="2" col="2" text="9"></Button>
  <Button row="2" col="4" text="+"></Button>
  <Button row="3" col="0" text="4"></Button>
  <Button row="3" col="1" text="5"></Button>
  <Button row="3" col="2" text="6"></Button>
  <Button row="3" col="3" text="6"></Button>
  <Button row="3" col="4" text="-"></Button>
  <Button row="4" col="0" text="1"></Button>
  <Button row="4" col="1" text="2"></Button>
  <Button row="4" col="2" text="3"></Button>
  <Button row="4" col="3" text="X"></Button>
  <Button row="5" col="0" text="0"></Button>
  <Button row="5" col="1" text="."></Button>
  <Button row="5" col="2" text="("></Button>
  <Button row="5" col="3" text=")"></Button>
  <Button row="6" col="0" text="UNDO"></Button>
  <Button row="6" col="1" text="CLEAR"></Button>
  <Button row="6" col="2" colSpan="2" text="="></Button>
</GridLayout>

We’ve created a GridLayout with 4 columns and 7 rows. We indicate that all the columns should use all the space available to them (through the ‘*’ symbol). Since We set this up for all of them, they should share the space equally between them. For the rows, we did the same apart from the two first ones. We just want them to occupy only the space they need (which is their default height).

We’re going to add a little bit of CSS to style this up. Edit app/app.css with the following content:

button, label{
  horizontal-align: center;    
}

label {
  font-size: 36;
}

label.result {
  font-size: 42;
  horizontal-align: left;
}

button {
  font-size: 36;
  width: 100%;
}

The result should look like this:

calcu

This is nice, but this doesn’t do much for the moment, so let’s work on this. Edit app.component.ts with the following content:

import {Component} from "@angular/core";

@Component({
    selector: "my-app",
    templateUrl: "app.component.xml"
})
export class AppComponent {
  operation: string = '';

  append(element: string){
    this.operation += element;
  }
}

We declare an operation variable of type string. The only operation for now is append(), which takes a string in input and add it to the operation variable. We need to do two things in order to put this in use: bind the operation variable to one of the labels in our layout, and call append for every button, with the corresponding elements as parameter. Edit app.component.xml with the following content:

<GridLayout columns="*, *, *, *" rows="auto, auto, *, *, *, *, *" >
  <Label class="result" text="RESULT" colSpan="4"></Label>
  <Label text="{{ operation }}" row="1" colSpan="4"></Label>
  <Button (tap)="append('7')" row="2" col="0" text="7"></Button>
  <Button (tap)="append('8')" row="2" col="1" text="8"></Button>
  <Button (tap)="append('9')" row="2" col="2" text="9"></Button>
  <Button (tap)="append('+')" row="2" col="4" text="+"></Button>
  <Button (tap)="append('4')" row="3" col="0" text="4"></Button>
  <Button (tap)="append('5')" row="3" col="1" text="5"></Button>
  <Button (tap)="append('6')" row="3" col="2" text="6"></Button>
  <Button (tap)="append('6')" row="3" col="3" text="6"></Button>
  <Button (tap)="append('-')" row="3" col="4" text="-"></Button>
  <Button (tap)="append('1')" row="4" col="0" text="1"></Button>
  <Button (tap)="append('2')" row="4" col="1" text="2"></Button>
  <Button (tap)="append('3')" row="4" col="2" text="3"></Button>
  <Button (tap)="append('*')" row="4" col="3" text="X"></Button>
  <Button (tap)="append('0')" row="5" col="0" text="0"></Button>
  <Button (tap)="append('.')" row="5" col="1" text="."></Button>
  <Button (tap)="append('(')" row="5" col="2" text="("></Button>
  <Button (tap)="append(')')" row="5" col="3" text=")"></Button>
  <Button row="6" col="0" text="UNDO"></Button>
  <Button row="6" col="1" text="CLEAR"></Button>
  <Button row="6" col="2" colSpan="2" text="="></Button>
</GridLayout>

We’ve now bound the operation variable to the second label through string interpolation. For every Button, we’ve bound the append method to the ‘tap’ event (equivalent to ‘click’ with Angular2 for the web), and we pass the corresponding element as parameter.
You should now be able to tap the buttons and see the operation building up on the second row of the screen:

The three last buttons require more specific behaviors. Let’s implement the undo() function. What we basically want, is to delete the last element from the whole operation. Add the following function to AppComponent:

  undo(){
    if (this.operation != ''){
      this.operation = this.operation.slice(0, -1)
    }
  }

We use the JavaScript slice method on operation in order to keep everything except the last character. Now, update the app.component.xml in order to bind this function to the tap event of the corresponding button:

  <Button (tap)="undo()" row="6" col="0" text="UNDO"></Button>

You should now be able to delete the last entered element:


Next, comes the clear() function. This one is pretty straightforward, we just want to empty the operation variable:

  clear(){
    this.operation = '';
  }

Alright, only one step left, which is the most important: the evaluation of the operation to get the result. Let’s introduce a new variable: result, of type string as well, which will host the result of the operation. Then, we’ll use the JavaScript eval() function on the operation variable, and stock the result in the result one:

import {Component} from "@angular/core";

@Component({
    selector: "my-app",
    templateUrl: "app.component.xml"
})
export class AppComponent {
  operation: string = '';
  result: string= '';

  append(element: string){
    this.operation += element;
  }

  undo(){
    if (this.operation != ''){
      this.operation = this.operation.slice(0, -1)
    }
  }

  clear(){
    this.operation = '';
  }
  
  evaluate(){
    this.result = eval(this.operation);
  }
}

Now, within app.component.xml, we need to do two things. First, bind the result to the first label. Then, attach the evaluate method to the ‘=’ button:

<GridLayout columns="*, *, *, *" rows="auto, auto, *, *, *, *, *" >
  <Label class="result" text="{{ result }}" colSpan="4"></Label>
  <Label text="{{ operation }}" row="1" colSpan="4"></Label>
  <Button (tap)="append('7')" row="2" col="0" text="7"></Button>
  <Button (tap)="append('8')" row="2" col="1" text="8"></Button>
  <Button (tap)="append('9')" row="2" col="2" text="9"></Button>
  <Button (tap)="append('+')" row="2" col="4" text="+"></Button>
  <Button (tap)="append('4')" row="3" col="0" text="4"></Button>
  <Button (tap)="append('5')" row="3" col="1" text="5"></Button>
  <Button (tap)="append('6')" row="3" col="2" text="6"></Button>
  <Button (tap)="append('6')" row="3" col="3" text="6"></Button>
  <Button (tap)="append('-')" row="3" col="4" text="-"></Button>
  <Button (tap)="append('1')" row="4" col="0" text="1"></Button>
  <Button (tap)="append('2')" row="4" col="1" text="2"></Button>
  <Button (tap)="append('3')" row="4" col="2" text="3"></Button>
  <Button (tap)="append('*')" row="4" col="3" text="X"></Button>
  <Button (tap)="append('0')" row="5" col="0" text="0"></Button>
  <Button (tap)="append('.')" row="5" col="1" text="."></Button>
  <Button (tap)="append('(')" row="5" col="2" text="("></Button>
  <Button (tap)="append(')')" row="5" col="3" text=")"></Button>
  <Button (tap)="undo()" row="6" col="0" text="UNDO"></Button>
  <Button (tap)="clear()" row="6" col="1" text="CLEAR"></Button>
  <Button (tap)="evaluate()" row="6" col="2" colSpan="2" text="="></Button>
</GridLayout>

And we should have the behavior we expects:

Now, if you try to evaluate an invalid operation, you’ll notice a crash of the application, as the eval() function throws an exception if the input is not valid JavaScript. Let’s catch the exception and alert the user saying that he tries to do an invalid operation. We’ll use NativeScript dialogs and specifically, the alert function. First, let’s import the dialogs module:

import dialogs = require("ui/dialogs");

Then, we update the evaluate method to catch the exception and display an error through dialogs.alert():

  evaluate(){
    try {
      this.result = eval(this.operation);
    }
    catch(e){
      dialogs.alert({title: 'Error', message: 'Cannot evaluate expression!', okButtonText: 'OK'});
    }
  }

I figured out this function definition as I wanted to change the default title which is ‘Alert’. I basically navigated to the definiton of the function, and noticed that this one was available:

    export function alert(options: AlertOptions): Promise<void>;

Navigating further to AlertOptions:

    /**
     * Provides options for the alert.
     */
    export interface AlertOptions extends DialogOptions {
        /**
         * Gets or sets the OK button text.
         */
        okButtonText?: string;
    }

And DialogOptions:

    export interface DialogOptions extends CancelableOptions {
        /**
         * Gets or sets the dialog title.
         */
        title?: string;

        /**
         * Gets or sets the dialog message.
         */
        message?: string;

    }

So I figured out the way to use alert(options: AlertOptions) without having to leave my editor: this is the power of TypeScript.

Let’s check out the error handling:

That’s it! we now have a simple mobile calculator (which should work on iOS as well).
You can checkout the code of this tutorial is available on GitHub.

Tips to Work Efficiently With Buffers in Vim

Vimlogo.
One of the things that determines your productivity in your text editor or IDE is the number of files you’re working with simultaneously. Ideally, you would have to be working with one single file at a time in order to focus your attention and limit the context switching which results in a costly loss of time and focus. But in reality, this isn’t likely to happen, since we’re not working within a single concern at a time (are you?). This implies having to go back and forth between the files, and Vim provides the possibility to handle this switching in a smart way, using buffers.

What is a buffer?

A buffer in Vim is simply a file that is loaded in memory so it can get accessed quickly. This is done automatically as soon as you open it up (typically using the :e command). You can also add a buffer associated to a file without having to edit it directly using :badd filename command.

Interact with buffers

To displays the open buffers, use the command:

:buffers

buffers
You’ll get a list with the buffers as well as their corresponding data. The first column is the unique number identifying the buffer. The second is the set of attributes. In the above example, there is:

  • a buffer with the attribute #, which corresponds to the ‘alternate’ (last edited buffer). You can open it directly using :e #
  • a buffer with the attribute %a, which means that it’s displayed in the current window (%), and visible (a). This is technically possible for a buffer to be visible, but not in the current window when working with splits for example.

For more information regarding the :buffers command, consult the official documentation.

You can close one or multiple buffers using:

:bdelete buffer1 buffer2 ...

To switch between buffers you can use:

:bnext

To open the next buffer in the list

:bprevious

To open the previous one.

:ball

will open up all the available buffers.

Another useful command is :bufdo which enables you to execute an operation on all open buffers. For example, to do a search replace through all buffers, you can execute:

:bufdo %s/search_tearm/replace_term/g | update

The update keyword at the end automatically saves the buffer if a replace actually takes place. To avoid having to do it every time I’m operating through multiple buffers, I have put:

set autowrite

in my .vimrc which takes care of saving the changes when vim automatically switch buffers.

To learn more about commands related to buffers manipulation, read the Vim FAQ on the subject.

Useful tips to work with buffers

Now, I’ll give you a few tips to work efficiently and move quickly around buffers.

buffers line with vim-airline

vim-airline is considered as a must-have plugin for Vim, as it adds up the graphics features cruelly missing from the default version. To me, the most useful feature is the bar displaying the list of open buffers. This is really useful because it makes you notice when you’re tending to have a list getting too big (which becomes a clear brake to your productivity).
To install vim-airline using Vundle, just place the following line in your .vimrc:

Plugin 'vim-airline/vim-airline'

And run :PluginInstall
vim-airline uses the Powerline patched fonts in order to display graphics elements within the terminal. The easiest way is to use this GitHub repository.
Clone it using:

git clone https://github.com/powerline/fonts

Then switch to the created repository and run the install script:

./install.sh

The next step is to configure your terminal to use one of the installed fonts (ending with for Powerline). Just be aware that all the fonts won’t necessarily work well with your airline configuration and you might notice graphic bugs. But there will be a few of them that’ll do the job properly though. The one I use is called Liberation Mono for Powerline (I’m using Ubuntu).

fonts

I won’t go into the features of vim-airline because it’s not the topic of this article but I’ll share the (basic) configuration I’ve set for it within my .vimrc:

let g:airline#extensions#tabline#enabled = 1
let g:airline_powerline_fonts = 1
let g:airline_theme='powerlineish'
let g:airline#extensions#syntastic#enabled = 1
set laststatus=2

The first parameter is what I’m talking about when I say the line displaying the list of open buffers. The second is here to make airline work with the Powerline patched fonts which is mandatory to have the graphics displayed properly. Then, we set the theme, the Syntastic integration and finally we use the status bar everywhere (displayed only when you open a split otherwise).

Now, start Vim and open up a bunch of files sequentially. You’ll have the associated buffers displayed in the top bar, and have the currently open one highlighted:

Swap and close buffers quickly

Here are mappings that I use quite often:

:nnoremap <Tab> :bnext<CR>
:nnoremap <S-Tab> :bprevious<CR>
:nnoremap <C-X> :bdelete<CR>

They allow you to switch buffers: Tab for the next one, Shift-Tab for the previous one. To quickly close a buffer, I use Ctrl-X. The cool thing is that you can actually see what happens with the vim-airline buffer line:

Fuzzy Search within Buffers with ctrlp.vim

ctrlp.vim is a Vim plugin providing fuzzy finding features for files, MRU (Most Recently Used files), and Buffers. Install it with Vundle:

Plugin 'ctrlpvim/ctrlp.vim'

To open the list of buffers and start searching through it, use:

:CtrlPBuffer

I’ve decided to map this command to Leader-B:

nmap <Leader>b :CtrlPBuffer<CR>

Now press Leader-B and start fuzzy navigating through the open buffers:

The alternate buffer is always the one selected by default, so to come back to it, just press Leader-B + Enter and there you are.

That’s it ! for any trouble / advice, please go ahead and write a comment.

You can checkout my dotfiles here.

My Vim Configuration for Rails Development

vim-rails

Vim is the editor of choice for many rails developers (including me). One of the reasons is the almost infinite level of configuration and customization it provides, as well as the massive choice of already existing plugins from the community. In this article, I’ll share my configuration which is really rails oriented, although there are some general tips you can use for something else.

Tmux and dispatch.vim

I’ve recently written an article to show the benefits of using Tmux and Time Pope’s dispatch.vim for quick and effective testing feedback. But you can actually achieve much more and we’ll see some example of integrations that are natively supported later in the article.
To install Tmux using Mac OS X:

brew install tmux

Using Debian / Ubuntu

sudo apt-get install tmux

You can install the dispatch plugin using Vundle:

Plugin 'tpope/vim-dispatch'

Rails Integration With vim-rails

If you had to choose only one vim plugin for Rails development, it would definitely be this one. It provides features such as inter (Rails) resources navigation, improved syntax highlighting, built-in Rake / Rails commands within Vim.
To make all this wonder available, just install it using Vundle:

Plugin 'tpope/vim-rails'

Resources Navigation

The navigation has been made easy yet extremely powerful. You just have to use the command with the following pattern :Eresource_type resource_name
For instance, opening up the UsersController file would simply be:

Econtroller users

To open the User model:

Emodel user

Without any parameter, the :Eresource will try to open up the resource linked to the one currently open one. For instance, if you have user.rb open, Econtroller will open up the associated users_controller.rb

E is actually using the Vim :e command under the hood. You can also use the alternatives like S (for :split), V (:vsplit) or T (:tabedit). If you’d like to open a controller in a new vertical split, you could do:

Vcontroller users

There are a lot of resources supported by the navigation mechanism, but here are the one I use the most often:

controller
model
spec
view
migration
helper

Navigate to Alternate File

Another helpful command is the :A (for Alternate) which takes you to the corresponding spec (or test) file. the plugin will first look for a MiniTest file if it exists, and then a spec file otherwise, so make sure that you don’t use generators automatically creating MiniTest files if you use RSpec instead. the :A command is compatible with the previously mentioned modes as well (S, V, T…).
For instance, opening the spec corresponding to the currently opened file in a new vertical split is done by calling :AV (mode placed at the end)

Navigate to Related File

We sometimes have to jump to file that is closely related to the one we’re currently editing. The command for this is :R (again, works with the S, V and T). The related files mappings are the following:

  • Model -> Schema definition
  • Controller -> View
  • Migration -> Next Migration (Previous Migration accessible using :A)

Jump to a File

the vim gf command is originally used to jump to the file described under the cursor. rails-vim extends this behavior by allowing to edit the file corresponding to the resource pointed by the cursor.

This works with multiple resources: Class / Module names, Active Record associations, layouts, stylesheets…

I usually work with vertical splits so I’ve used the key binding from this page to be able to open the file under the cursor to a new vertical split:

:map <F8> :vertical wincmd f<CR>

Command Line Integration

rails-vim enables the integration of rails and rake commands directly within Vim. The rails and rake commands are respectively accessible via :Rails and :Rake.
You can use them as you would normally do, but there are also some additional uses available. For instance, :Rgenerate model <model> will call the normal rails generate model user, and open the corresponding migration at the end of the process. :Rgenerate controller <model> will run the process and open the newly created controller:

You can also integrate with rails server using the :Rserver command. Now, if you have followed my advice, you probably have installed Tmux and the Dispatch.vim plugin. After having run a Tmux session with Vim, try to run :Rserver:

Instead of running the rails server in background as the :Rserver command normally does, it creates a new Tmux windows and run the command in it. You can now restart the server by running :Rserver! or kill it using :Rserver!- (or simply switch to the corresponding Tmux window and press Ctrl-C).

Concern and Partial Extraction

Concern and Partial extraction are made possible through the :Rextract command.
If the current opened file is a model, the result will be a concern extraction. For example, if I have the user model open and I want to select three methods for extraction. I can just go into visual mode, highlight the methods and run:

:'<,'>Rextract <concern_name>

This works with line numbers as well:

:9,19Rextract <concern_name>

The extracted methods will be put in a new file placed in app/models/concerns and inherits from ActiveSupport::Concern

The same mechanism applies for Partial extraction. Run:

:'<,'>Rextract <partial_name>; 

on the selected lines of one of your views and it will automatically create the partial with the _ prefix and replace the lines with the partial reference:

Fuzzy Finding With ctrlp.vim

ctrlp.vim is one of my favorite multi-purpose plugins for Vim. It enables to search through a directory of light using a fuzzy logic (and more). Start by installing it using Vundle:

Plugin 'ctrlpvim/ctrlp.vim'

Then go ahead and use it executing Vim in any directory and pressing Ctrl-P. You can then just type whatever you’re looking for in a fuzzy manner and press Enter to open up the found file (Ctrl-V to open in a new vertical split, Ctrl-T in a new tab):

You can also mark multiple files to open by cycling around using Ctrl-K (up) and Ctrl-J (down), then Ctrl-Z (mark file) and finally Ctrl-O to open:

You can also cycle through the available Search modes (File by default) to MRU (Most Recent Used) or opened Buffers by pressing Ctrl-F and Ctrl-B:

There is also a feature I find particularly useful: the file creation. Open up ctrlp, type the file you want to create and then press Ctrl-Y:

Why not simply use the :e command? well, this method will also create the corresponding folders if they don’t exist, which prevents you from having to manually create them before writing the new file.

The main downside with ctrlp.vim is to have to manually refresh the cache when something changes withing your current project structure (file renamed, deleted or added). you can do this by pressing F5 while ctrlp.vim open. But fortunately, there is a way around this which we’ll see later in the article.

Find Your Way in Your Source Code With CTags

Moving quickly between the references of your Rails project becomes a must when its size reaches a certain point. CTags enables you to do just that, by creating tags indexing the references of your project, in order to be reached wherever you are. You can read the following article which gives a good overview of the possibilities of using CTags with Rails.
Go ahead and generate the tags of your Rails project by running the following command, at the root:

ctags -R --languages=ruby --exclude=.git --exclude=log .

Or if you want to tag the external bundle dependencies as well (extremely useful):

ctags -R --languages=ruby --exclude=.git --exclude=log . $(bundle list --paths)

One of the common use cases is to reach a method definition while pointing at one of its calls. Point the cursor to the method for which you’d like to know the definition, and press Ctrl-] to jump to it:

Going back to your previous location is as easy as pressing Ctrl-T.
You can also do exactly the same by entering the method name instead of having to point to it by using the command:

:ta method_name

Blazing Fast Searching With the Silver Searcher

The Silver Searcher or is a searching tool which started as a clone of ack. Its creator claims a speed improvement of 5 to 10 times. Making it the default grepping command of Vim will make your search operations executed with the speed of light.
First of all, install it using:

brew install the_silver_searcher

For Mac OS X
and:

sudo apt-get install silversearcher-ag

For Debian / Ubuntu.
Note: I had to compile from the sources in order to get the latest version of ag on Ubuntu. You might have to do this if you notice that your .gitignore is not respected while using the command.

Then, do as it’s suggested in this article from Thoughtbot and write the following lines into your .vimrc:

" The Silver Searcher
if executable('ag')
  " Use ag over grep
  set grepprg=ag\ --nogroup\ --nocolor

  " Use ag in CtrlP for listing files. Lightning fast and respects .gitignore
  let g:ctrlp_user_command = 'ag %s -l --nocolor -g ""'

  " ag is fast enough that CtrlP doesn't need to cache
  let g:ctrlp_use_caching = 0
endif

This sets ag as the default grep command, as well as the ctrlp.vim files listing one. The speed provided by ag allows us to disable the cache, which solves the F5 problem we mentioned previously !

Then, you can map the :Ag command for instance to the \ key:

nnoremap \ :Ag<SPACE>

Using it with the text you want to search within your current project’s directory will display the result in the Vim Quickfix window. You can then press Enter on the row you want to open the corresponding file:

You can also pass an argument, like the folder in which you want to search:

:Ag app/controllers

Global Replace With qfdo.vim

qfdo.vim is a simple Vim plugin allowing to run a command for each line within the Quickfix window. Mixed with the :Ag command, it allows you to perform a two step global search replace.
Go ahead and install the plugin using Vundle:

Plugin 'karlbright/qfdo.vim'

Then write the following line to your .vimrc, which allows to auto save buffers after being automatically switched (needed for the replace withing multiple files which has to open and close multiple buffers sequentially):

set autowrite

Now suppose you want to change a method name. You can just search it using the :Ag command (through the \ key we previously mapped), then run:

:Qfdo s/method_to_search/method_to_replace_with/gce

With using the gce suffix, you’ll be prompted each time in order to confirm or reject the change. You could also replace all at one time using simply g. In action:

Syntax analysis with Syntastic and RuboCop

Syntastic is a plugin enabling the check the syntax of your source files. It displays potential warning as well as errors directly in the Vim interface and where it’s actually detected. The default checker for Ruby is RuboCop and we’ll see how to integrate it with Syntastic. First, install syntastic using Vundle:

Plugin 'scrooloose/syntastic'

Then, write the following configuration in your .vimrc:

set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0
let g:syntastic_ruby_checkers = ['rubocop']

I use a minimal configuration to automatically check the syntax on open and omit on close. I also set the ruby checker to RuboCop which you can install either using:

gem install rubocop

Or by referencing the gem within your Gemfile:

gem 'rubocop', require: false

Now, you can start writing and get noticed from syntax warning and error as you save your buffers:

There is another cool trick that turns out to be really useful to do some refactoring. Go ahead and install the vim-rubocop plugin:

Plugin 'ngmy/vim-rubocop'

It enables you to use the RuboCop command directly in vim using :RuboCop. using it with the –auto-correct flag This can correct automatically some content marked as warning, use the best practices of the Ruby community instead and display the results within the QF window. Since I’m using I quite often, have mapped it to the Ctrl-A key. In action:

vim-rspec and Asynchronous Specs Run

I’ve already described this workflow in this article. It basically uses the combination of Tmux, Dispatch.vim and vim-rspec to run the specs asynchronously, and display the errors if some in the QF window. Install vim-rspec using Vundle:

Plugin 'thoughtbot/vim-rspec'

Configure the key mappings to run the appropriate specs:

map <Leader>t :call RunCurrentSpecFile()<CR>
map <Leader>s :call RunNearestSpec()<CR>
map <Leader>l :call RunLastSpec()<CR>
map <Leader>a :call RunAllSpecs()<CR>

Then, configure vim-rspec to start the rspec command using Dispatch:

let g:rspec_command = "Dispatch rspec {spec}"

When there are errors after the spec execution, the output is displayed within the QF Window. There is another plugin I use to toggle the QF maximization called vim-maximizer. It enables you to maximize / minimize the current focused window by pressing F3.
Installation with Vundle:

Plugin 'szw/vim-maximizer'

I also use the following mapping to be able to interact quickly with resulted errors:

map <F2> :Copen

To open the QF (or focus it if already open)
and:

map , :ccl

To close it.

Since you’re still in Vim while looking at the error, you can use the usual workflow to move around and for instance copy an error message to the clipboard.

vim-surround

vim-surround is another multi-purpose plugin that can’t miss from your .vimrc. It enables you to work with surrounding elements (parenthesis, quotes, tags…). You can either add one, change it or delete it. It’s also compatible with the usual Vim motions (i for inside, a for around, w for word…).

Surround a word using ysiw and then the surrounding character you want, for instance ysiw' (add surrounding character ' in word):

You can also change the surround to another character, like " with csi' (change surround inside '):

It also works in visual mode. Just select the text you want to surround, and then press S. The following example uses first a tag, and then the character ' for the final output:

Installation with Vundle:

Plugin 'tpope/vim-surround'

vim-endwise

Simple yet very handy plugin to automatically close blocks as you open them (def, if, do …). There is not much more to say about it apart that it saves a lot of time:

Installation with Vundle:

Plugin 'tpope/vim-endwise'

Git Integration with fugitive.vim

fugitive.vim is another great plugin from Tim Pope allowing you to use Git without making even a step out of Vim. You can basically use git as you would normally do using the :Git command but there’s obviously more.
Use for example :Gstatus to manage the staging area. You can select one (or many using visual mode) to commit and then press the - key. With :Gcommit, you can then write the commit message (by entering the edit mode) and save the buffer to execute your commit. Then, you can push using :Gpush. If you use the Tmux and Dispatch workflow, the push output will be displayed in a Tmux pane instead of blocking the window. In action:

Another great example is the use of :Gblame. Executing the command will open a vertical split with the output of a normal git blame. You can then select one row and press o to display the result into a split:

To execute git checkout on the current file, run :Gread:

For more detailed use cases, I suggest the following Vim Cast.

To install it using Vundle:

Plugin 'tpope/vim-fugitive'

Final Words

This is not the entire description of the configuration I use for Vim, but definitely the plugins I use the most often for my Rails development workflow. I hope you’ll be able to benefit from the tips and if you have anything useful to share or find a potential weakness / enhancements, please write a comment!
My dotfiles (Tmux, Vim) are available here.

Configure CORS to Allow Cross-Domain Requests in a Rails 5 API Application

If you have already tried to integrate a JavaScript Single Page Application with an API sitting in another domain, you’ve probably encountered blocked requests (and some frustration). This typically happens because most of the browsers don’t allow cross-domain HTTP requests originated from scripts, for security reasons. This is typically the case of the AJAX requests (XMLHttpRequests) sent by your JavaScript application.
Fortunately, there is a way to allow these requests, using the CORS mechanism.
A Rails 5 application created with the –api flag comes shipped with the rack-cors gem which provides full support for CORS. After your app creation, you’ll notice the file: config/initilizers/cors.rb which is commented out by default:

# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

# Rails.application.config.middleware.insert_before 0, Rack::Cors do
#   allow do
#     origins 'example.com'
#
#     resource '*',
#       headers: :any,
#       methods: [:get, :post, :put, :patch, :delete, :options, :head]
#   end
# end

The only thing to do is to uncomment the lines corresponding to the actual configuration and adapt it to your needs. One example would be:

# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'myfrontendapp.example.com'

    resource '*',
             headers: :any,
             methods: [:get, :post, :put, :patch, :delete, :options, :head],
             expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'],
             max_age: 0
  end
end

The ‘*’ symbol means no restriction. We could have also used origins '*' to allow requests coming from anywhere but this is highly unrecommended. We also added an option to expose some header values. In this example, we want to allow the usual headers needed for a token-based authentication.

And that’s basically everything you need to do to make CORS working with your Rails API. For more information on the subject, I recommend reading this article.

Behavior-Driven Development of a Rails REST API With Cucumber and RSpec (Part III)

In the previous post of this series, we covered the creation of an offer for an existing ad.
In this part, we will focus on the two remaining use cases: The retrieval of all the ads, and the retrieval of offers corresponding to an existing ad.

Retrieve ads

Let’s start by creating a Feature file:

Feature: Retrieve ads

  Scenario: Retrieve ads
    Given following ads:
      | id |  title                | description                                 | price |
      | 1  | Old computer to sell  | I'm selling my computer as it's getting old | 125   |
      | 2  | Give away my camera   | To the one making the best offer            | 0     |
    When I make a GET /ads
    Then response shoud have status 200 and JSON:
    """
    [ { "id": 1, "title": "Old computer to sell", "description": "I'm selling my computer as it's getting old", "price": 125 },
    { "id": 2, "title": "Give away my camera", "description": "To the one making the best offer", "price": 0 }]
    """

In the first Given step, we’re giving a table in input with two ads and their corresponding attribute values. After making the GET request against the /ads path, we’re expecting to get back a JSON array with the two existing ads.
We’re going to have to implement the first two steps of this Scenario, so let’s grab the generated snippets and paste them into the ad_steps.rb file that we already have:

Given(/^following ads:$/) do |ads_table|
  # table is a Cucumber::Core::Ast::DataTable
  pending # Write code here that turns the phrase above into concrete actions
end

When(/^I make a GET \/ads$/) do
  pending # Write code here that turns the phrase above into concrete actions
end

Let’s go and implement the first step. Since we get a Cucumber Table in input, we can do the following:

Given(/^following ads:$/) do |ads_table|
  ads_table.hashes.each do |ad_hash|
    FactoryGirl.create(:ad, ad_hash)
  end
end

We iterate through the table hashes (where the keys are the columns of the first row of the table), and create a new Ad with the given hash, corresponding to the attributes. Again, we use FactoryGirl to make sure we create valid models (here, the users associations will be provided for us).
The second step is relatively simple:

When(/^I make a GET \/ads$/) do
  get "/ads"
end

But it won’t pass until we adapt the routes.rb file like this:

Rails.application.routes.draw do
  resources :ads, only: [:index, :create] do
    resources :offers, only: [:create]
  end
end

We’ve just added the :index method to the :ads resources definition. Now, after running the Scenario again, we get the following:

The action 'index' could not be found for AdsController (AbstractController::ActionNotFound)

So we go ahead and create an empty index method to the AdsController:

class AdsController < ApplicationController
  include ResourceController

  def index
  end

  private

  def permitted_params
    params.permit(:title, :price, :description, :user_id)
  end
end

Now we get:

expected #<Fixnum:401> => 200
     got #<Fixnum:409> => 204

Which tells us that we get no content and we’ll have to come up with something more than an empty method. Let’s now work on this. It’s time to get the ads_controller_spec.rb file from its grave and start writing the specification of the index method:

require 'rails_helper'
require 'controller_shared_contexts'

RSpec.describe AdsController, type: :controller do
  describe 'GET index' do
    before do
      first_ad = Ad.new(id: 1, title: 'First title', description: 'First description', price: 1)
      second_ad = Ad.new(id: 2, title: 'Second title', description: 'Second description', price: 2)
      expect(Ad).to receive(:all).and_return([first_ad, second_ad])
    end
    it_responds_with 'a success status'
    it_responds_with 'JSON body',
                     '[{ "id": 1, "title": "First title", "description": "First description", "price": 1 },
                      { "id": 2, "title": "Second title", "description": "Second description", "price": 2 }]'
  end
end

Since we’ve gained confidence by writing the previous specs, we can do a slightly bigger batch of testing and assertion. As you can see, I’m already assuming that we’ll have to call Ad.all and stubbing it out. To me, that’s still acceptable since we’re talking about recurring patterns that come to mind right away.
If we run this spec now, we get the following failure:

  1) AdsController GET index responds with a success status should respond with a success status code (2xx)
     Failure/Error: Unable to find matching line from backtrace
       (<Ad(id: integer, title: string, description: string, created_at: datetime, updated_at: datetime, user_id: integer, price: integer) (class)>).all(any args)
           expected: 1 time with any arguments
           received: 0 times with any arguments

Let’s then update the AdsController like this:

class AdsController < ApplicationController
  include ResourceController

  def index
    render json: Ad.all
  end

  private

  def permitted_params
    params.permit(:title, :price, :description, :user_id)
  end
end

We’re just rendering a JSON response containing the result of Ad.all that we’ve stubbed out in our spec. Running rspec again now gives us the following failure:

  1) AdsController GET index responds with JSON body should eq [{"id"=>1, "title"=>"First title", "description"=>"First description", "price"=>1}, {"id"=>2, "title"=>"Second title", "description"=>"Second description", "price"=>2}]
     Failure/Error: expect(JSON.parse(response.body))
       
       expected: [{"id"=>1, "title"=>"First title", "description"=>"First description", "price"=>1}, {"id"=>2, "title"=>"Second title", "description"=>"Second description", "price"=>2}]
            got: [{"id"=>1, "title"=>"First title", "description"=>"First description", "created_at"=>nil, "updated_at"=>nil, "user_id"=>nil, "price"=>1}, {"id"=>2, "title"=>"Second title", "description"=>"Second description", "created_at"=>nil, "updated_at"=>nil, "user_id"=>nil, "price"=>2}]
       
       (compared using ==)
       
       Diff:
       
       @@ -1,9 +1,15 @@
        [{"id"=>1,
          "title"=>"First title",
          "description"=>"First description",
       +  "created_at"=>nil,
       +  "updated_at"=>nil,
       +  "user_id"=>nil,
          "price"=>1},
         {"id"=>2,
          "title"=>"Second title",
          "description"=>"Second description",
       +  "created_at"=>nil,
       +  "updated_at"=>nil,
       +  "user_id"=>nil,
          "price"=>2}]

The problem is that we’re getting back too many attributes. We really want just title, description and price (at least, for now). We could make this attributes restriction in the controller, but pushing this responsibility to the model makes more sense to me. So will consider that by the time we call the all method on the Ad model, we’ll already get a list of ads with a list of filtered attributes:

require 'rails_helper'
require 'controller_shared_contexts'

RSpec.describe AdsController, type: :controller do
  describe 'GET index' do
    before do
      first_ad = Ad.new(id: 1, title: 'First title', description: 'First description', price: 1)
      second_ad = Ad.new(id: 2, title: 'Second title', description: 'Second description', price: 2)
      expect(Ad).to receive(:all).and_return([sliced(first_ad), sliced(second_ad)])
      get :index
    end
    it_responds_with 'a success status'
    it_responds_with 'JSON body',
                     '[{ "id": 1, "title": "First title", "description": "First description", "price": 1 },
                      {"id": 2, "title": "Second title", "description": "Second description", "price": 2 }]'
  end
  def sliced(ad)
    ad.slice('id', 'title', 'description', 'price')
  end
end

We use the slice method in order to select only the attributes we’re interesting in (we’ll actually get hashes instead of actual Objects but that’ll work just fine for us). This modification should make the spec pass already.
Now, let’s run the Cucumber Scenario once more. As expected, we get the following failure:

expected: [{"id"=>1, "title"=>"Old computer to sell", "description"=>"I'm selling my computer as it's getting old", "price"=>125}, {"id"=>2, "title"=>"Give away my camera", "description"=>"To the one making the best offer", "    price"=>0}]                                                                                                                                                                                                                               
got: [{"id"=>1, "title"=>"Old computer to sell", "description"=>"I'm selling my computer as it's getting old", "created_at"=>"2016-06-19T10:49:49.192Z", "updated_at"=>"2016-06-19T10:49:49.192Z", "user_id"=>1, "price"=>12    5}, {"id"=>2, "title"=>"Give away my camera", "description"=>"To the one making the best offer", "created_at"=>"2016-06-19T10:49:49.194Z", "updated_at"=>"2016-06-19T10:49:49.194Z", "user_id"=>2, "price"=>0}]                           

Let’s head down to the Ad model to correct its behavior. Open up the ad_spec.rb file and add the following lines:

  describe '.all' do
    before { FactoryGirl.create(:ad) }
    let(:ad_attributes) { Ad.all.first.attributes }
    it 'includes default attributes attribute' do
      expect(ad_attributes).to include('id', 'title', 'description', 'price')
    end
  end

We just make sure that we get the attributes we need when calling Ad.all. This should already pass. But now if we add this:

    it 'includes 4 attributes' do
      expect(ad_attributes.count).to be(4)
    end

We get the failure:

     Failure/Error: expect(ad_attributes.count).to be(4)
       
       expected #<Fixnum:9> => 4
            got #<Fixnum:15> => 7

This makes sense, we’re still getting too many attributes. Now that we have caught this into a spec, let’s adjust this. The easiest way to do it is to add a default scope to the Ad model, to limit the attributes to the list we need. For now, we only need id, title, description and price. We can do this by adding the following line to ad.rb:

  default_scope { select('id', 'title', 'description', 'price') }

At this stage, we should have a 100% green wave so we’re good to move further.

Retrieve offers

The last Feature we need to implement is the retrieval of offers, corresponding to a given ad. As always, let’s start with a Cucumber Feature and Scenario:

Feature: Retrieve offers

  Scenario: Retrieve offers
    Given an ad with id "1"
    Given following users:
      | id |
      | 2  |
      | 3  |
      | 4  |
    And following offers:
      | id | message                    | price | ad_id | user_id |
      | 1  | Exactly what I need !      | 500   | 1     | 2       |
      | 2  | Would you lower the price? | 25    | 1     | 3       |
      | 3  |                            | 75    | 1     | 4       |
    When I make a GET /ads/1/offers
    Then response should have status 200 and JSON:
    """
    [{ "id": 1, "message": "Exactly what I need !", "price": 500, "user_id": 2 },
    { "id": 3, "message": "", "price": 75, "user_id": 4 },
    { "id": 2, "message": "Would you lower the price?", "price": 25, "user_id": 3}]
    """

We start with an existing ad and 3 users. We attach 3 offers to the created ad using the different user ids. Then we make the GET request to /ads/1 and we finally expect to get a JSON array with the existing offers and their attributes, sorted by price (descending).
Alright, so let’s implement the missing steps here. First, the two Given:

Given(/^following users:$/) do |users_table|
  users_table.hashes.each do |user_hash|
    FactoryGirl.create(:user, user_hash)
  end
end

Given(/^following offers:$/) do |offers_table|
  offers_table.hashes.each do |offer_hash|
    FactoryGirl.create(:offer, offer_hash)
  end
end

We’ve already used this pattern for the creation of the ad table on the previous Feature. Now for the When step:

When(/^I make a GET \/ads\/(\d)\/offers$/) do |ad_id|
  get "/ads/#{ad_id}/offers"
end

Next step, update the routes:

Rails.application.routes.draw do
  resources :ads, only: [:index, :create] do
    resources :offers, only: [:index, :create]
  end
end

Now, before we go and add the index method to the OffersController, let’s think about how it’ll actually look like. Again, this will be exactly the same as for the AdsController. So let’s push this behavior direct into the ResourceController that we have and write a test for it. Open up the resource_controller_spec.rb file and do the following:
First of all, we need to update the Foo Fake model to implement an empty all class method that we want to stub out:

class Foo
  include ActiveRecord::Persistence
  include ActiveModel::Model
  attr_accessor :id, :name
  def self.all
  end
end

We have to do it that way since the method is originally included in ActiveRecord::Base. We didn’t want to inherit from it in order to avoid the overhead of creating a table definition for our test model.
Then, we can add the spec description for the GET index action:

  describe 'GET index' do
    before do
      first_foo = Foo.new(id: 1, name: 'First foo')
      second_foo = Foo.new(id: 2, name: 'Second foo')
      expect(Foo).to receive(:all).and_return([first_foo, second_foo])
      get :index
    end
    it_responds_with 'a success status'
    it_responds_with 'JSON body',
                     '[{"id": 1, "name": "First foo"},
                      {"id": 2, "name": "Second foo"}]'
  end

Now, running rspec indicates that there is no ‘index’ method defined in ResourceController, so let’s add it:

  def index
    resource_name = controller_name.classify
    render json: resource_name.constantize.all
  end

This makes the resource_controller_spec file pass. Let’s eliminate some duplication within the module:

module ResourceController
  def create
    resource = resource_class.create!(permitted_params)
    render json: { message: "#{resource_name} created successfully", id: resource.id }
  rescue ActiveRecord::RecordInvalid => invalid
    render json: { errors: invalid.record.errors }, status: :unprocessable_entity
  end

  def index
    render json: resource_name.constantize.all
  end

  private

  def resource_name
    controller_name.classify
  end

  def resource_class
    resource_name.constantize
  end
end

Let’s run the Retrieve Offers Scenario again:

expected: [{"id"=>1, "message"=>"Exactly what I need !", "price"=>500, "user_id"=>1}, {"id"=>3, "message"=>"", "price"=>75, "user_id"=>3}, {"id"=>2, "message"=>"Would you lower the price?", "price"=>25, "user_id"=>2}]        
got: [{"id"=>1, "price"=>500, "message"=>"Exactly what I need !", "user_id"=>2, "ad_id"=>1, "created_at"=>"2016-06-19T19:18:31.889Z", "updated_at"=>"2016-06-19T19:18:31.889Z"}, {"id"=>2, "price"=>25, "message"=>"Would you lower the price?", "user_id"=>3, "ad_id"=>1, "created_at"=>"2016-06-19T19:18:31.892Z", "updated_at"=>"2016-06-19T19:18:31.892Z"}, {"id"=>3, "price"=>75, "message"=>"", "user_id"=>4, "ad_id"=>1, "created_at"=>"2016-06-19T19:18:31.894Z", "updated_at"=>"2016-06-19T19:18:31.894Z"}]                 

Ok, so we still made a step forward, but we need one more thing to do in order to make the Scenario pass: The limitation of returned attributes from the model. As always, we start by defining the behavior that we want within the corresponding spec file:

  describe '.all' do
    before { FactoryGirl.create(:offer) }
    let(:offer_attributes) { Offer.all.first.attributes }
    it 'includes default offer attributes' do
      expect(offer_attributes).to include('id', 'message', 'price', 'user_id')
    end
    it 'includes 4 attributes' do
      expect(ad_attributes.count).to be(4)
    end
  end

Since we’re now hitting the database in a spec file for the first time, we need to configure RSpec to clean up the mess between each test. We’ll use the DatabaseCleaner gem that we already use for Cucumber. Open up the rails_helper.rb file and add the following content:

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
    DatabaseCleaner.strategy = :transaction
  end
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

We tell RSpec to clean everything before running the suite using the truncation method. Then, we use the transaction method between each test to isolate them from each other.
We can now run the spec, which gives us the following failure:

     Failure/Error: before { FactoryGirl.create(:offer) }
     ActiveRecord::RecordInvalid:
       Validation failed: User must exist, Ad must exist

That’s because we didn’t declare any association to our :offer Factory as we didn’t really need it so far. Looks like we do know, so let’s do this:

FactoryGirl.define do
  factory :offer do
    message Faker::Lorem.sentence
    price Faker::Number.number(2)
    association :ad
    association :user
  end
end

Run again. Now we get:

     Failure/Error: expect(offer_attributes.count).to be(4)
       
       expected #<Fixnum:9> => 4
            got #<Fixnum:15> => 7
class Offer < ApplicationRecord
  belongs_to :user
  belongs_to :ad
  validates :price, presence: true

  default_scope { select('id', 'message', 'price', 'user_id') }
end

If we get back to the Cucumber Scenario and try to run it again, we get:

      expected: [{"id"=>1, "message"=>"Exactly what I need !", "price"=>500, "user_id"=>1}, {"id"=>3, "message"=>"", "price"=>75, "user_id"=>3}, {"id"=>2, "message"=>"Would you lower the price?", "price"=>25, "user_id"=>2}]
           got: [{"id"=>1, "price"=>500, "message"=>"Exactly what I need !", "user_id"=>2}, {"id"=>2, "price"=>25, "message"=>"Would you lower the price?", "user_id"=>3}, {"id"=>3, "price"=>75, "message"=>"", "user_id"=>4}]

We get the sorting wrong. As we said, we expect the offers to be sorted by price descending. So let’s update the Offer.all method description like this:

  describe '.all' do
    before { FactoryGirl.create(:offer, id: 1, price: 3) }
    let(:offer_attributes) { Offer.all.first.attributes }
    it 'includes default offer attributes' do
      expect(offer_attributes).to include('id', 'message', 'price', 'user_id')
    end
    it 'includes 4 attributes' do
      expect(offer_attributes.count).to be(4)
    end
    it 'sorts by price descending' do
      FactoryGirl.create(:offer, id: 2, price: 8)
      FactoryGirl.create(:offer, id: 3, price: 5)
      expect(Offer.all.collect(&:id)).to eq([2, 3, 1])
    end
  end

We’ve extended the previously created offer by giving it an id and a price. Then, in the price sorting description, we create two additional offers with given ids and prices. Finally, we assert on the correct offer ids order when calling the all method. As we expect, the result is the following:

     Failure/Error: expect(Offer.all.collect(&:id)).to eq([2, 3, 1])
       
       expected: [2, 3, 1]
            got: [1, 2, 3]

To make this one pass, we’ll update the default scope to add the sorting by price, like this:

class Offer < ApplicationRecord
  belongs_to :user
  belongs_to :ad
  validates :price, presence: true

  default_scope { select('id', 'message', 'price', 'user_id').order(price: :desc) }
end

And that was basically the last step of our acceptance loop! we should be in a green state on both RSpec and Cucumber sides.

Final thoughts

That was the last post of this series of three articles. We went through an outside-in process enabling us to first define how the outcomes of the requests our system should look like through Cucumber Features. We worked our way down to the Models, going through the Controllers using RSpec tests. You’ve noticed that I have chosen to isolate the Controllers specs from the Models which is a bit different from the conventional practices of the Rails community, tending to be more Integration tests. I personally prefer to do it that way I did because we’re getting much more focused feedback in case of failures, and since we don’t hit the database, we also get a speed advantage. The price however, is that we have to stub out the Model behavior which can makes us lose some confidence, and add some overhead (but rspec and the dynamic nature of ruby make it really easy).
In any case, we managed to shape the API by doing some incremental steps, each time, stating how we would like the behavior of each component under our radar. This also allowed us to manage safely some refactoring steps, for instance, by extracting the common behavior of our two controllers into a concern module which we could also test in isolation. For any remarks, troubles or enhancements tips, please write a comment !
The code is also available on GitHub at this address.

Behavior-Driven Development of a Rails REST API With Cucumber and RSpec (Part II)

In the previous article of this series, we covered the Ad creation use case given a title, a description, a price and the corresponding user id. Now will focus on enabling a user to place an offer on an existing ad. We said that an offer contains a price and a message. We’ll consider it as valid if it has at least a price (message being optional).

Make an offer to an existing Ad

  Scenario: using valid data
    Given an ad with id "1"
    And a user with id "2"
    When the client makes a valid POST /ads/1/offers with user_id: "2"
    Then response should have status 200 and JSON:
    """
      {"message": "Offer created successfully", "id": 1}
    """

The implementation of the first step simply makes use of FactoryGirl to create a new ad, with the given id:

 Given(/^an ad with id "([^"]*)"$/) do |id|
   FactoryGirl.create(:ad, id: id)
 end

Running the Cucumber Scenario after this gives us the following error:

Validation failed: User must exist (ActiveRecord::RecordInvalid)

That’s because we didn’t declare any relation to the default Ad factory yet. Let’s correct this:

FactoryGirl.define do
  factory :ad do
    title Faker::Lorem.word
    price Faker::Number.number(2)
    description Faker::Lorem.sentence
    association :user
  end
end

This tells FactoryGirl to automatically associate the ad with a user when using the create method. The :user factory needs to be existing, and since we have already defined one during the first part of the article, we’re good to go. Now for the second step:

When(/^the client makes a valid POST \/ads\/(\d+)\/offers with user_id: "([^"]*)"$/) do |ad_id, user_id|
  params = FactoryGirl.attributes_for(:offer).merge(user_id: user_id)
  post "/ads/#{ad_id}/offers", params
end

We also use FactoryGirl but only to generate the parameters we need to make the request, in addition to the given user_id.
We need to introduce the :offer factory as well as the corresponding model. Let’s start by the model generation:

rails g model offer price:integer message:string user:references ad:references
rails db:migrate

And then the factory, using Faker generated attributes:

FactoryGirl.define do
  factory :offer do
    message Faker::Lorem.sentence
    price Faker::Number.number(2)
  end
end

We’re now ready to run the Scenario again, and we come to the following failure:

No route matches [POST] "/ads/1/offer" (ActionController::RoutingError)

Which brings us to the following update of routes.rb:

Rails.application.routes.draw do
  resources :ads, only: [:create] do
    resources :offers, only: [:create]
  end
end

Again, we just define the route we actually need for the moment. Now the OffersController containing an empty create method:

class OffersController < ApplicationController
  def create
  end
end

Before going any further, let’s start the implementation of the OffersController spec. It’ll be very similar as the AdsController one and we’ll see how we can refactor to eliminate duplications afterwards:

require 'rails_helper'

RSpec.describe OffersController, type: :controller do
  describe 'POST create' do
    let(:permitted_params) { ActionController::Parameters.new(params) }
    let(:params) { FactoryGirl.attributes_for(:offer).merge(ad_id: '1', user_id: '2') }
    let(:execute_request) { post :create, params: params }

    before(:all) do
      ActionController::Parameters.permit_all_parameters = true
    end

    context 'with valid parameters' do
      before do
        execute_request
      end
      it 'returns a successful response' do
        expect(response).to have_http_status(:success)
      end
      it 'returns returns success message with offer id' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "message": "Offer created successfully", "id": 1 }'))
      end
    end
  end
end

Let’s then update the OffersController with the code required to make these examples pass:

class OffersController < ApplicationController
  def create
    offer = Offer.create!(offer_params)
    render json: { message: 'Offer created successfully', id: offer.id }
  end
end

private

def offer_params
  params.permit(:message, :ad_id, :user_id)
end

But now, running this spec file throws the following failure:

     Failure/Error: let(:execute_request) { post :create, params: params }
     ActiveRecord::RecordInvalid:
       Validation failed: User must exist, Ad must exist

which is related to the validation of the model. Since we’re interested in the controller behavior only for now, let’s stub out the Offer.create! method to return a valid model, that we declare through a let variable:

require 'rails_helper'
require 'controller_shared_contexts'

RSpec.describe OffersController, type: :controller do
  describe 'POST create' do
    let(:permitted_params) { ActionController::Parameters.new(params) }
    let(:offer) { FactoryGirl.build(:offer, id: 1) }
    let(:params) { FactoryGirl.attributes_for(:offer).merge(ad_id: '1', user_id: '2') }
    let(:execute_request) { post :create, params: params }

    before(:all) do
      ActionController::Parameters.permit_all_parameters = true
    end

    context 'with valid parameters' do
      before do
        expect(Offer)
          .to receive(:create!)
          .with(permitted_params)
          .and_return(offer)
        execute_request
      end
      it 'returns a successful response' do
        expect(response).to have_http_status(:success)
      end
      it 'returns returns success message with offer id' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "message": "Offer created successfully", "id": 1 }'))
      end
    end
  end
end

And we now have two passed examples. So let’s stop for a minute and try to eliminate some of the duplications we have introduced between this spec and the AdsController one.
First of all, there is the mechanism for the permitted parameters, which is common to the two specs. Let’s create a new RSpec shared context file:

#spec/controller_shared_context.rb

shared_context 'permit_params' do
  let(:permitted_params) { ActionController::Parameters.new(params) }
  before(:all) do
    ActionController::Parameters.permit_all_parameters = true
  end
end

Now we can make use of the shared_context we declared, by including it into our spec (and requiring the associated file on top):

require 'rails_helper'
require 'controller_shared_contexts'

RSpec.describe OffersController, type: :controller do
  describe 'POST create' do
    include_context 'permit_params'
    let(:offer) { FactoryGirl.build(:offer, id: 1) }
    let(:params) { FactoryGirl.attributes_for(:offer).merge(ad_id: '1', user_id: '2') }
    let(:execute_request) { post :create, params: params }

    context 'with valid parameters' do
      before do
        expect(Offer)
          .to receive(:create!)
          .with(permitted_params)
          .and_return(offer)
        execute_request
      end
      it 'returns a successful response' do
        expect(response).to have_http_status(:success)
      end
      it 'returns returns success message with offer id' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "message": "Offer created successfully", "id": 1 }'))
      end
    end
  end
end

Next, let’s tackle the successful response assertion. We can also use a shared_context like the following:

# spec/controller_shared_contexts.rb

RSpec.configure do |c|
  c.alias_it_should_behave_like_to :it_responds_with, 'responds with'
end
shared_context 'a success status' do
  it { expect(response).to have_http_status(:success) }
end

We created an alias for the it_should_behave_like method to it_responds_with, which enables us to change

it 'returns a successful response' do
  expect(response).to have_http_status(:success)
end

to

it_responds_with 'a success status'

This enables us to reuse the response status expectation, but staying expressive enough at the same time.
Let’s now create a shared_context for the response body assertion as well:

shared_context 'JSON body' do |json|
  it do
    expect(JSON.parse(response.body))
      .to eq(JSON.parse(json))
  end
end

which we can now use like this:

it_responds_with 'JSON body',
                 '{ "message": "Offer created successfully", "id": 1 }'

Now, our OffersController spec file should look like:

require 'rails_helper'
require 'controller_shared_contexts'

RSpec.describe OffersController, type: :controller do
  describe 'POST create' do
    include_context 'permit_params'
    let(:offer) { FactoryGirl.build(:offer, id: 1) }
    let(:params) { FactoryGirl.attributes_for(:offer).merge(ad_id: '1', user_id: '2') }
    let(:execute_request) { post :create, params: params }

    context 'with valid parameters' do
      before do
        expect(Offer)
          .to receive(:create!)
          .with(permitted_params)
          .and_return(offer)
        execute_request
      end
      it_responds_with 'a success status'
      it_responds_with 'JSON body',
                       '{ "message": "Offer created successfully", "id": 1 }'
    end
  end
end

This should make our Cucumber Scenario pass as well. So let’s write the unsuccessful case within the Feature file:

  Scenario: using blank price
    When the client makes a POST /ads/1/offers request with blank price and user_id: "2"
    Then response should have status 422 and JSON:
    """
      {"errors": {"price": ["can't be blank"]}}
    """

Before implementing the new When step, let’s change the helper method:

def valid_ad_creation_params(user_id)
  FactoryGirl.attributes_for(:ad).merge(user_id: user_id)
end

to:

def valid_creation_params_for(resource, user_id)
  FactoryGirl.attributes_for(resource).merge(user_id: user_id)
end

Now, let’s push it as well as the Transform method into a new file in order to bring a little organization:

# features/support/step_helpers.rb

module StepHelpers
  def valid_creation_params_for(resource, user_id)
    FactoryGirl.attributes_for(resource).merge(user_id: user_id)
  end
end
World(StepHelpers)

Transform /(\d+)$/ do |number|
  number.to_i
end

And adapt the step definitions file like this:

# features/step_definitions/ad_steps.rb

Given(/^a user with id "([^"]*)"$/) do |id|
  FactoryGirl.create(:user, id: id)
end

Given(/^an ad with id "([^"]*)"$/) do |id|
  FactoryGirl.create(:ad, id: id)
end

When(/^the client makes a valid POST \/ads request with user_id: "([^"]*)"$/) do |user_id|
  post '/ads', valid_creation_params_for(:ad, user_id)
end

When(/^the client makes a POST \/ads request with blank title and user_id: "([^"]*)"$/) do |user_id|
  params = valid_creation_params_for(:ad, user_id).merge(title: '')
  post '/ads', params
end

When(/^the client makes a valid POST \/ads\/(\d+)\/offers with user_id: "([^"]*)"$/) do |ad_id, user_id|
  params = valid_creation_params_for(:offer, user_id)
  post "/ads/#{ad_id}/offers", params
end

When(/^the client makes a POST \/ads\/(\d+)\/offers request with blank price and user_id: "([^"]*)"$/) do |ad_id, user_id|
  params = valid_creation_params_for(:offer, user_id).merge(price: '')
  post "/ads/#{ad_id}/offers", params
end                                                                                                                  
Then(/^response should have status (\d+) and JSON:$/) do |status, json_string|
  expect(last_response.status).to be(status)
  expect(JSON.parse(last_response.body)).to eq(JSON.parse(json_string))
end

Now we are ready to deal with the error provided by Cucumber when running the Scenario:

    Then response should have status 422 and JSON:
      """
        {"errors": {"price": ["can't be blank"]}}
      """

      expected #<Fixnum:845> => 422
           got #<Fixnum:401> => 200

We’re still getting a success status because we don’t have any other possible path yet.
So, time to jump back to the OffersController spec. We can now add the following context:

# spec/controllers/offers_controller_spec.rb

  context 'with validation error' do
    before do
      offer.errors.add(:price, "can't be blank")
      expect(Offer)
        .to receive(:create!)
        .with(permitted_params)
        .and_raise(ActiveRecord::RecordInvalid.new(offer))
      execute_request
    end
    it_responds_with 'a unprocessable entity status'
    it_responds_with 'JSON body',
                     '{ "errors": {"price": ["can\'t be blank"] }}'
  end

We need to add the following shared context as well:

# spec/controller_shared_contexts.rb

shared_context 'a unprocessable entity status' do
  it { expect(response).to have_http_status(:unprocessable_entity) }
end

And we should be now have a green state within our OffersController spec. Let’s see what Cucumber has to say to us when executing the Scenario:

    Then response should have status 422 and JSON:    
      """
        {"errors": {"price": ["can't be blank"]}}
      """

      expected #<Fixnum:845> => 422
           got #<Fixnum:401> => 200

We get a success response again because we miss the model validation. Let’s write a spec for the Offer model:

require 'rails_helper'

RSpec.describe Offer, type: :model do
  it { should belong_to(:user) }
  it { should belong_to(:ad) }
  it { should validate_presence_of(:price) }
end

And update the model accordingly:

class Offer < ApplicationRecord
  belongs_to :user
  belongs_to :ad
  validates :price, presence: true
end

At this stage, everything should be green.

Refactoring the Resource Creation Pattern

Now, compare the two controllers and associated spec. They are pretty similar, right? Since our back is covered by all sort of tests, we can go on safely and try to refactor the pattern out of the controllers. Let’s introduce a module called ResourceController.

# app/concerns/resource_controller.rb

module ResourceController
  def create
    resource_name = controller_name.classify
    resource = resource_name.constantize.create!(permitted_params)
    render json: { message: "#{resource_name} created successfully", id: resource.id }
  rescue ActiveRecord::RecordInvalid => invalid
    render json: { errors: invalid.record.errors }, status: :unprocessable_entity
  end
end

We start by extracting the model name out of the controller using the classify method. Then, we get the actual resource by calling create! on the result of the constantize method (which transform the model name into a class).
The implementation also implies having a permitted_params method, which is the responsibility of the concrete controller itself to declare it. The rest is the logic you already know: we either render a successful response using the resource name and the created instance id, or we render an error response with the generated errors from the validation process.
Now, we can simplify the AdsController like this:

class AdsController < ApplicationController
  include ResourceController

  private

  def permitted_params
    params.permit(:title, :price, :description, :user_id)
  end
end

And run the tests to make sure everything is working as it was before. Since it does, we can do the same with the OffersController:

class OffersController < ApplicationController
  include ResourceController

private

def permitted_params
  params.permit(:message, :price, :ad_id, :user_id)
end

Good ! but we’re not quite over yet. Now that we’ve extracted the logic into a new module, we need cover it with its own spec. We basically have the main logic already, but since we are now testing a concern, we have to do some tweaks:

# spec/concerns/resource_controller_spec.rb

require 'rails_helper'

require 'controller_shared_contexts'

class Foo
  include ActiveRecord::Persistence
  include ActiveModel::Model
  attr_accessor :id, :bar
end
class FoosController < ApplicationController; end

RSpec.describe ResourceController, type: :controller do
  controller FoosController do
    include ResourceController

    def permitted_params
      params.permit(:bar)
    end
  end
  describe 'POST create' do
    include_context 'permit_params'
    let(:foo) { Foo.new(id: 1) }
    let(:params) { { bar: 'foo' } }
    let(:execute_request) { post :create, params: params }

    context 'with valid parameters' do
      before do
        expect(Foo)
          .to receive(:create!)
          .with(permitted_params)
          .and_return(foo)
        execute_request
      end
      it_responds_with 'a success status'
      it_responds_with 'JSON body',
                       '{ "message": "Foo created successfully", "id": 1 }'
    end
    context 'with validation error' do
      before do
        foo.errors.add(:bar, "can't be foo")
        expect(Foo)
          .to receive(:create!)
          .with(permitted_params)
          .and_raise(ActiveRecord::RecordInvalid.new(foo))
        execute_request
      end
      it_responds_with 'a unprocessable entity status'
      it_responds_with 'JSON body',
                       '{ "errors": {"bar": ["can\'t be foo"] }}'
    end
  end
end

First, we create a fake model with the ActiveRecord modules containing the methods that we need for our spec (typically, create! and initialize), as well as the attribute accessors. We could also directly inherit from ApplicationRecord but that would require to create a database table which we don’t need. Then, we create a FoosController to serve the model. We use it as context for our spec, and include the ResourceController module, and declare the permitted_params method. The rest is similar to the specs we’ve already created for the Ad and Offer models.

At this stage, we should remain 100% green. We can now delete the ad_spec.rb and offer_spec.rb files as we’re already covering the ResourceController module with what we have just written.

And that was it for the second part of the series. For the next one, we’ll focus on the retrieval of ads and offers.
If you have any remark, critiques or potential enhancements regarding the content of this article, don’t hesitate to let a comment !

Behavior-Driven Development of a Rails REST API With Cucumber and RSpec (Part I)

Some weeks ago, I’ve written an article on how to behavior-driven develop a SPA application using AngularJS and a completely mocked out back-end.
This time, I want to show you how to tackle the back-end side of things, through the development of a RESTful API using Rails 5 and an outside-in approach.
The tutorial will focus on a simplified marketplace application where users are able to create ads. We will specifically concentrate on the following use cases:

  • Users are able to create ads containing a title, a description and a price.
  • Users can make an offer to an existing ad, with a message and a price which can be different.
  • The client can retrieve all existing ads.
  • The client can retrieve an ad with its corresponding offers, sorted by price.

To simplify the process, we won’t go through the authentication of the user.
For the different tests, we will use Cucumber and RSpec. We’ll run on Rails 5, so if you haven’t upgraded yet, follow this link.

Setting up the Boilerplate

Let’s first create the application, using the –api parameter:

rails new marketplace --api -T

We also use -T in order to skip the creation of MiniTest dependencies which we won’t use.
Open up the Gemfile and setup the following gems, that we’ll use:

group :test do
  gem 'cucumber-rails', require: false
  gem 'rspec-rails'
  gem 'database_cleaner'
  gem 'factory_girl_rails'
  gem 'faker'
end

and run bundle install
Then, run the generate commands for both RSpec and Cucumber:

rails generate rspec:install
rails generate cucumber:install

Let’s also configure the test environment to use Factory Girl. Edit the config/environment/test.rb file and add the following line:

config.gem 'factory_girl'

Ad creation Feature

Let’s start with the ad creation feature. We want to be able to send a request to create an new ad, and if successful, get a response back containing the id of the created ad and a message saying “Ad created successfully”. We come up with the following Cucumber Feature:

Feature: Create an ad
  In order to sell an object
  I want to create an ad

  Scenario: using valid data
    Given a user with id "1" 
    When the client make a valid POST /ads request with user_id: "1"
    Then response should have status 200 and JSON:
    """
      {"message": "Ad created successfully", "id": "1"}
    """

You might first find this incorrect to find things like POST and user_id within a Feature file. Isn’t it supposed to be readable by all the involved stakeholders after all? well yes, but since the other party involved in this case is the consumer of the API, it is also another technical stakeholder. And we’re describing exactly what he’s supposed to do: make a POST request to a defined path, with a JSON containing fields like user_id.

So, when we now run the cucumber command on this, it will give us the following snippets, that we will put in a new file features/step_definitions/ad_steps.rb:

Given(/^a user with id "([^"]*)"$/) do |id|
  pending # Write code here that turns the phrase above into concrete actions
end

When(/^the client make a valid POST /ads request with user_id: "([^"]*)"$/) do |id|
  pending # Write code here that turns the phrase above into concrete actions
end

Then(/^response should have status (\d+) and JSON:$/) do |status, string|
  pending # Write code here that turns the phrase above into concrete actions
end

We will consider a valid request a one containing non-blank fields (title, price, description). The purpose of the first step is to consider the system provided with a user, having the id 1. We don’t know anything about the user apart from that, so let’s keep things simple:

Given(/^a user with id "([^"]*)"$/) do |id|
  FactoryGirl.create(:user, id: id)
end

Our next step is to create the actual User model:

rails generate model User

Let’s add the corresponding factory as well:

# spec/factories/users.rb

FactoryGirl.define do
  factory :user do
  end
end

now after a rake db:migrate, we should be able to make the first step pass. Let’s work on the request:

When(/^the client make a valid POST \/ads request with user_id: "([^"]*)"$/) do |id|
  params = FactoryGirl.attributes_for(:ad).merge(user_id: id)
  post '/ads', params
end

We use FactoryGirl to delegate the generation of the fields we don’t care about (we just want them to be valid). Let’s create an Ad model with the three fields we need:

rails generate model Ad title:string price:integer description:string
rake db:migrate

as well as the corresponding factory:

# spec/factories/ads.rb

FactoryGirl.define do
  factory :ad do
    title Faker::Lorem.word
    price Faker::Number.number(2)
    description Faker::Lorem.sentence
  end
end

After running cucumber again, we get the error:

No route matches [POST] "/ads" (ActionController::RoutingError)

Let’s fix this by editing the routes.rb file:

Rails.application.routes.draw do
  resources :ads, only: [:create]
end

This is the only path we need for now. Let’s now write enough code to make the second step path. If you now run cucumber again, it should complain about the missing AdsController. Let’s create it with an empty create action:

# app/controllers/ads_controller.rb

class AdsController < ApplicationController
  def create
  end
end

And that should make the second step pass. Now if we go further to the third step implementation, we come up with this:

Then(/^response should have status (\d+) and JSON:$/) do |status, json_string|
  expect(last_response.status).to be(status)
  expect(JSON.parse(last_response.body)).to eq(JSON.parse(json_string))
end

As you can see, we can use the last_response helper from Rack::Test available through Cucumber.
Since we want to capture a number for the status, we need to tell cucumber how to transform it into the correct format (captured as a string by default). We can use the Cucumber Transform helper for this. Add the following method to the step definitions file:

Transform /(\d+)$/ do |number|
  number.to_i
end

Now we need to implement the create action to provide the client with the response it expects:

class AdsController < ApplicationController
  def create
    Ad.create(ad_params)
    render json: { message: 'Ad created successfully', id: ad.id }
  end

  private

  def ad_params
    params.permit(:title, :price, :description, :user_id)
  end
end

Now, running cucumber again should provoke the following error:

unknown attribute 'user_id' for Ad. (ActiveModel::UnknownAttributeError)

We’re missing the association with the User model. Let’s work on this right now. First, generate the migration:

rails generate migration AddUserToAds user:references
rake db:migrate

Then, make the actual association between the Ad model:

class Ad < ApplicationRecord
  belongs_to :user
end

Let’s move on to the unhappy scenario (invalid data). We’ll take the blank title case. First, let’s move the Given step onto a Background section, since it’ll be common to the two scenarios:

  Background:
    Given a user with id "1"

Then, update the first scenario to remove the Given step

  Scenario: using valid data
    When the client make a valid POST /ads request with user_id: "1"
    Then response should have status 200 and JSON:
    """
      {"message": "Ad created successfully", "id": 1}
    """

And then add a second scenario:

  Scenario: using blank title
    When the client make a POST /ads request with blank title and user_id: "1"
    Then response should have status 422 and JSON:
    """
      {"errors": {"title": ["can't be blank"]}}
    """

We now have a new step we need to implement. Let’s first extract the parameters retrieve into a reusable method:

def valid_ad_creation_params(user_id)
  FactoryGirl.attributes_for(:ad).merge(user_id: user_id)
end

Then, implement the step like this

When(/^the client make a POST \/ads request with blank title and user_id: "([^"]*)"$/) do |id|
  params = valid_ad_creation_params(id).merge(title: '')
  post '/ads', params
end

Now, running cucumber provokes a failure at the statuses comparison:

expected #<Fixnum:845> => 422
     got #<Fixnum:401> => 200

We actually get a success response here. This is because we don’t have any validation yet. Let’s correct this in the Ad model:

class Ad < ApplicationRecord
  belongs_to :user
  validates :title, presence: true
end

We now need to modify the create method to handle the invalid model case. I think it’s a good time to introduce a unit tests for the Controller, since it’s starting to expand. Let’s add a spec file and start with the successful case:

# spec/controllers/ads_controller_spec.rb

require 'rails_helper'

RSpec.describe AdsController, type: :controller do
  describe 'POST create' do
    let(:params) { FactoryGirl.attributes_for(:ad).merge(user_id: '1') }
    let(:execute_request) { post :create, params: params }

    context 'with valid parameters' do
      before do
        ActionController::Parameters.permit_all_parameters = true
        permitted_params = ActionController::Parameters.new(params)
        expect(Ad)
          .to receive(:create)
          .with(permitted_params)
          .and_return(FactoryGirl.build(:ad, id: 1))
        execute_request
      end
      it 'returns a success response' do
        expect(response).to have_http_status(:success)
      end
      it 'returns returns success message with ad id' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "message": "Ad created successfully", "id": 1 }'))
      end
    end
  end
end

As you can see, we isolate the controller by explicitly setting the behavior of the Ad model, when receiving the create method. We basically want to avoid going into the actual validation process which is the responsibility of the model.
Now we need to handle the invalid parameters case.
We want to be notified whenever a validation problem occurs when creating a new Ad model. This is something we can’t actually get with the create method, which returns the instance of the model in both cases (valid and invalid). We can use create! instead which throws an ActiveRecord::RecordInvalid exception, along with the invalid record. Let’s do some preparatory refactoring first:

RSpec.describe AdsController, type: :controller do
  describe 'POST create' do
    let(:ad) { FactoryGirl.build(:ad, id: 1) }
    let(:params) { FactoryGirl.attributes_for(:ad).merge(user_id: '1') }
    let(:permitted_params) { ActionController::Parameters.new(params) }
    let(:execute_request) { post :create, params: params }

    before(:all) do
      ActionController::Parameters.permit_all_parameters = true
    end

    context 'with valid parameters' do
      before do
        expect(Ad)
          .to receive(:create)
          .with(permitted_params)
          .and_return(ad)
        execute_request
      end
      it 'returns a success response' do
        expect(response).to have_http_status(:success)
      end
      it 'returns returns success message with ad id' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "message": "Ad created successfully", "id": 1 }'))
      end
    end

Then, let’s implement the unsuccessful case, and stub out the call of create!:

    context 'with validation error' do
      let(:ad) { FactoryGirl.build(:ad, id: 1) }
      before do
        ad.errors.add(:title, "can't be blank")
        expect(Ad)
          .to receive(:create!)
          .with(permitted_params)
          .and_raise(ActiveRecord::RecordInvalid.new(ad))
        execute_request
      end
      it 'returns model validation errors' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "errors": {"title": ["can\'t be blank"] }}'))
      end
    end

Let’s edit the Controller create method like this:

  def create
    ad = Ad.create!(ad_params)
    render json: { message: 'Ad created successfully', id: ad.id }
  rescue ActiveRecord::RecordInvalid => invalid
    render json: { errors: invalid.record.errors }
  end

Here, we are catching the exception thrown by create! in case of invalid parameters and rendering a response containing the errors of the retrieved record.
Let’s add the expectation on the correct status:

      it 'returns unprocessable entity response' do
        expect(response).to have_http_status(:unprocessable_entity)
      end

And update the method accordingly:

  def create
    ad = Ad.create!(ad_params)
    render json: { message: 'Ad created successfully', id: ad.id }
  rescue ActiveRecord::RecordInvalid => invalid
    render json: { errors: invalid.record.errors }, status: :unprocessable_entity
  end

And this should make the unsuccessful case pass. Now let’s run a full rspec. You’ll notice that the two first tests are now failing:

Failures:

  1) AdsController POST create with valid parameters returns a success response
     Failure/Error: expect(response).to have_http_status(:success)
       expected the response to have a success status code (2xx) but it was 422
     # ./spec/controllers/ads_controller_spec.rb:23:in `block (4 levels) in <top (required)>'

  2) AdsController POST create with valid parameters returns returns success message with ad id
     Failure/Error: expect(JSON.parse(response.body))
       
       expected: {"message"=>"Ad created successfully", "id"=>1}
            got: {"errors"=>{"user"=>["must exist"]}}

The problem here is that we’re still stubbing out the wrong method: create instead of create!. This basically let the application go into the normal flow until the model validation mechanism. Here, we’re in front of the case where there is no user corresponding to the given id. This validation is ensured by the belongs_to keyword we used to describe the Ad model. This type of ‘wrong’ failure is actually good because it gives you confidence in the behavior of the application, even if it’s not a case we want to test directly. Let’s update the context:

    context 'with valid parameters' do
      before do
        expect(Ad)
          .to receive(:create!)
          .with(permitted_params)
          .and_return(ad)
        execute_request
      end
      it 'returns a success response' do
        expect(response).to have_http_status(:success)
      end
      it 'returns returns success message with ad id' do
        expect(JSON.parse(response.body))
          .to eq(JSON.parse('{ "message": "Ad created successfully", "id": 1 }'))
      end
    end


4 examples, 0 failures

What about our Cucumber tests?

2 scenarios (2 passed)
6 steps (6 passed)

So far so good. But we’re not completely done in terms of acceptance criteria. We’ve said that in order to be valid, an Ad needs to have non-blank title, description and price. We’ve put an expectation on the title, but not on the other fields.
What we can do now, is to write a spec file for the Ad model, and expect the actual presence validations. I know that a lot of developers find that doing this falls into over-testing because of the ridiculously inexpensive way Rails provides the validation mechanism. But how else do you make sure that this validation takes place? We certainly don’t want to test every single field validation error within slow Cucumber tests, but at the same time, we don’t want our suite to tell us that everything is fine, although the user could potentially create an ad without a description or price which would be a business rule violation.
Let’s use Thoughtbot’s shoulda gem which allows us to do this in a nice and cheap way. Add the gem into your Gemfile:

gem 'shoulda-matchers'

And run bundle install. Then, we need to configure Shoulda to integrate with both RSpec and ActiveRecord. Open up spec/rails_helper.rb and add the following lines

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

And we can now write the following spec file:

require 'rails_helper'

RSpec.describe Ad, type: :model do
  it { should belong_to(:user) }
  it { should validate_presence_of(:title) }
  it { should validate_presence_of(:description) }
  it { should validate_presence_of(:price) }
end

I have included the belong_to relation as well. The two first cases should actually already pass. Now let’s update the Ad model:

class Ad < ApplicationRecord
  belongs_to :user
  validates :title, presence: true
  validates :description, presence: true
  validates :price, presence: true
end

Which brings us to a fully green suite and accepted Feature.

That’s it for the first part! The next one will focus on the next requirement which is the placing of offers.
Don’t hesitate to leave a comment if you have any question / remark regarding the content.

Some Tips Against A Slow Rails Test Suite

route

As your application grows, so does your test suite. This inevitably means an ever increasing running time and eventually at some point, a loss of control resulting in the abandonment of the test suite.
Here are some tips to help you keep your Rails test suite fast:

Keep the Pyramid Up

The Test Pyramid is a concept stating that your suite should be composed of three different layers of tests:
pyramid.png.pagespeed.ce.duElm9DDx5

The top includes the end-to-end tests (or Features) going through the whole stack of your application, typically used to validate the features seen from the final user’s perspective. One of the advantages of writing this kind of tests is the ability to let the application flow without the overhead of isolation. This means that for a few lines of code, you’re able to go through long paths within the application. While this is what you want in order to mark a whole feature as complete, this makes it slow and unfocused. That’s why you should keep these tests in a small amount and rely on a majority of lower level tests. They are typically written using a framework like Cucumber or RSpec feature tests

The middle is composed of integration tests that also go through multiple components of your application, but in a more limited manner. They usually avoid going through processes like the UI which makes them faster. Controller tests fit for example in this layer (if you don’t completely isolate them which would make them populate the bottom).

The bottom contains the unit tests, which are meant to test small components. They make the parts under tests completely isolated from their dependencies making them fast, focused and independent. For this reason, you want tons of them in order to get quick and targeted feedback. Their nature should assure you that a failure is only due to the behavior of the component under test itself and not one of its dependencies. They are also the ones you need to focus on in order to target a high code coverage.

A typical user sign up feature for instance would have only two scenario sitting on the top of the Pyramid: using valid credentials, and using invalid credentials. You don’t want to go through all the invalid cases (password and confirmation don’t match, password too short, email already used…) for a slow end-to-end test, but push all the way them down to the User model unit tests, which is responsible for the validation.

You can also use some temporary end-to-end tests helping you shape the application and build confidence that all the components are playing well together, as soon as you keep in mind that they are not meant to stay and that they will cause more harm than benefit in the long-term.

Use a Headless Browser for End-to-end Tests Using JavaScript

Testing Features using JavaScript usually means the use of Selenium to be able to automatically drive the browser attacking your application. This makes the process extremely slow, because every scenario relies on the use of the real browser, popping up, physically filling inputs, clicking buttons and closing down the windows.
Capybara-Webkit is an example of headless browser which makes use of the JavaScript features, but eliminating the need of the UI. This cut-off makes things a lot faster but still allows us to simulate the path that would take the final user. You can check out my article on Capybara-webkit for more info about its use with Cucumber.

Don’t Systematically Hit the Database

Hitting the database is a really common practice when testing Rails applications, mainly because of the ActiveRecord’s nature being extremely coupled with it. There are a lot of cases where this can be avoided though, which brings some undeniable speed gains. With FactoryGirl for instance, using build on a defined factory will provide you with an instance of it without persisting it in the database. For tests involving associations, you can use the build_stubbed method to build a completely stubbed instance of an object without having to hit the database at all.

Let’s consider an example of application where users are able to follow and be followed by other users. The two main models would be:

class User < ActiveRecord::Base
  has_many :relationships, foreign_key: 'follower_id', dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :followers, through: :reverse_relationships, source: :follower
end

And:

class Relationship < ActiveRecord::Base
  belongs_to :follower, class_name: 'User'
  belongs_to :followed, class_name: 'User'
end

Now we want to test the User model without hitting the database, but still be able to access the relationships association. Using FactoryGirl, we can define the following Factories:

FactoryGirl.define do
  factory :user do
    sequence(:name) { |n| "User#{n}" }
    trait :with_followed_user do
      after(:stub) do |instance|
        instance.relationships = build_list(:relationship, 1)
      end
    end
  end

  factory :relationship do
    association :follower, factory: :user
    association :followed, factory: :user
  end
end

We have our User Factory, and a corresponding trait :with_followed_user. We define the after(:stub) hook within it to build a list composed of one Relationship instance. This hook can be triggered with the build_stubbed method that we’ll use in our User spec:

require 'rails_helper'

describe User, type: :model do
  context 'with a followed user' do
    let(:user) { build_stubbed(:user, :with_followed_user) }
    it 'sets followed users count to 0' do
      followed = user.followed_users.first
      user.unfollow(followed)
      expect(user.followed_users.count).to be 0
    end
  end
end

Now, we can define an empty unfollow method in our User model:

class User < ActiveRecord::Base
  has_many :relationships, foreign_key: 'follower_id', dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :followers, through: :reverse_relationships, source: :follower

  def unfollow(other)
  end
end

And run the test to make sure that our stubbing is correct and we actually have already a followed user:

spec/models/user_spec.rb|9 error|  Failure/Error: expect(user.followed_users.count).to be 0 expected #<Fixnum:1> => 0 got #<Fixnum:3> => 1 Compared using equal?, which compares object identity, but expected and actual are not the same object. Use `expect(actual).to eq(expected)` if you don't care about object identity in this example.

Now we can go ahead and implement the method:

  def unfollow(other)
    relationships.find_by(followed_id: other.id).destroy
  end

And watch the test pass, without touching the database.

Parallelize Your Suite

The gem parallel_test helps optimize the use of the resources available in your machine and run the tests in parallel.

To use it, put the following in your Gemfile:

gem 'parallel_tests', group: [:development, :test]

Update your database.yml:

test:
  database: your_test_database<%= ENV['TEST_ENV_NUMBER'] %>

The environment variable TEST_ENV_NUMBER will be set by every process responsible for executing the subset of test within your suite. Each of them uses a different database to avoid concurrent accesses.
After running the command:

rake parallel:create

you’ll notice the creation of additional databases relative to the number of cores your machine is hosting:
databases

Then you can migrate all the databases:

rake parallel:prepare

The example uses the source code of the diaspora project and its (at this time) 2743 RSpec tests.
Let’s run a normal RSpec command. We get the following result:
without_parallel

And now, using the parallel command:

rake parallel:spec

with_parallel

We just increased the speed of a factor of 3 !

Backendless BDD With Angular Using Cucumber.js, Zombie.js and Nock

angularcucumber
When you start a new web project, deciding to have a front-end (mobile client, Single-Page Application, or both), served via a REST API by a back-end and both living on their own has largely become a standard, and naturally makes sense from an architectural point of view. One of the problems involved is the overhead for functional testing. For a standard Rails monolith application, this is quite easy as the whole system is at one place and we already have all the tools available to manipulate it wherever we are (We can access the database directly within a Cucumber Step implementation right before submitting a form contained in a view). Most of the commands used also already load rails for you, guaranteeing that the application is available, and in the correct environment.
Now, if the client sits outside of our rails world, it becomes difficult to maintain the whole system synchronized while running the functional tests. Going through the very complete path driven by the external client, as it’s actually done in production, would be slow, error prone, and unfocused (too many reasons to fail). My suggestion is: don’t go that way. Consider the project as it is in reality: A set of two different systems living on their own, but linked together by a contract which is the API definition.
This perspective implies the following:

  • the back-end, has to make sure that the data is delivered properly. The flow starts from the API entry point, down to the database. In that sense, functional testing against it aims to verify that the delivered responses are correctly sent, and in the right format.
  • on the front-end side Functional testing covers the behavior of the application, but already assumes the correct responses from the back-end, and focus on what happens after their reception. The flow starts from the browser (or the mobile client) and ends right before the queries are actually sent to the server.

This post will focus on the second case, with an AngularJS front-end.

Setting up the project

In this tutorial, we’ll test-drive a simple authentication application using Angular and the ng-token-auth module. The other tools we’ll be using are the following:

For this article, we’ll consider the following features:

  • The user is able to register using an email address and a password (including password confirmation)
  • The user is able to login with his registered credentials
  • The logged in user is able to logout

Middleman

If you don’t already have middleman installed, get it using:

gem install middleman

Then, you can create a new project using:

middleman init angular-authentication

Now, let’s configure middleman to use haml for the templating. Open the config.rb file within the newly created project, and add.

set :haml, { :ugly => true, :format => :html5 }

Then, remove the source/layouts/layout.erb and add source/layouts/layout.haml instead with the converted content:

# source/layouts/layout.haml  

!!!
%html
  %head
    %meta{content: 'IE=edge', 'http-equiv': 'X-UA-Compatible'}/
    %meta{charset: 'utf-8'}/
    %meta{content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no', name: 'viewport'}/
    %title Angular Authentication
    %link{href: '//fonts.googleapis.com/css?family=Lato:300,400', rel: 'stylesheet', type: 'text/css'}/
    = stylesheet_link_tag :site
    = javascript_include_tag :all
  %body{ class: page_classes }
    = yield

Let’s update the config.rb file to set the javascript directory to javascripts:

set :js_dir, 'javascripts'

Time to start the server. Run:

middleman server

Open your browser to visit: http://localhost:4567. You should the the default welcome page from middleman, which means that you’re all set up.

Install Angular

Next, let’s install angular. We’ll do it using npm. If you don’t have node installed already, you can follow the instructions of the official page. You will then have to create a package.json within your project by running:

npm init

Then you can install the angular module:

npm install --save angular

We use the –save argument to declare angular as a dependency of our project.

Install Cucumber.js

For Cucumber.js, you probably want to have the executable file available wherever on your machine. To do this using npm, run:

sudo npm install -g cucumber

Install Zombie.js

Zombie.js is a headless browser using the Node.js stack. I prefer it over PhantomJS because of its speed, and its very handy built-in DSL for the browser manipulation. Install it using npm:

npm install --save zombie

Install Nock

Nock is a Node.js library used to intercept HTTP request in order to provide custom responses. It helps preventing your application from actually reaching the back-end when used for testing purposes. Go ahead and install it:

npm install --save nock

Setup the Angular application

Our stack is now ready. Before heading into the writing of our Cucumber Features, let’s make angular up and running withing our application.
First, let’s include the node_components path into our config.rb file to make it usable within the application:

# config.rb

import_path File.expand_path( 'node_modules', root )

Then, modify the layout.haml file to make it look like this:

# source/layouts/layout.haml

%html
  %head
    %meta{ content: 'IE=edge', 'http-equiv': 'X-UA-Compatible' }/
    %meta{ charset: 'utf-8'}/
    %meta{ content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no', name: 'viewport' }/
    %title Angular Authentication
    %link{ href: '//fonts.googleapis.com/css?family=Lato:300,400', rel: 'stylesheet', type: 'text/css' }/
    = stylesheet_link_tag :site
    = javascript_include_tag :all
    = javascript_include_tag 'node_modules/angular/angular'
    = javascript_include_tag 'app/app'
  %body{ 'ng-app': 'angularAuthentication', class: page_classes }
    = yield

We did the following: reference the angular library, an app.js file contained in an app folder and the ng-app element referencing angularAuthentication. To make this work, we need to actually bootstrap the app. Start by creating the app folder:

mkdir source/javascripts/app

And then create the app.js file, with the content:

var app = angular.module('angularAuthentication', []);

Write the Features

Start by creating the common Cucumber structure:

mkdir -p features/support
mkdir features/step_definitions

The Sign up Feature

We are now ready to write our first Feature and Scenario. Let’s write:

Feature: Sign up
  As a user
  In order to have access to the application
  I want to sign up

Scenario: using valid credentials
  Given I am on the sign up page
  When I sign up using valid credentials
  Then I should be logged in

Now run the cucumberjs command. You should get the following snippets:

Warnings:

1) Scenario: using valid credentials - features/sign_up.feature:6
   Step: Given I am on the sign up page - features/sign_up.feature:7
   Message:
     Undefined. Implement with the following snippet:

       this.Given(/^I am on the sign up page$/, function (callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

2) Scenario: using valid credentials - features/sign_up.feature:6
   Step: When I sign up using valid credentials - features/sign_up.feature:8
   Message:
     Undefined. Implement with the following snippet:

       this.When(/^I sign up using valid credentials$/, function (callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

3) Scenario: using valid credentials - features/sign_up.feature:6
   Step: Then I should be logged in - features/sign_up.feature:9
   Message:
     Undefined. Implement with the following snippet:

       this.Then(/^I should be logged in$/, function (callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

1 scenario (1 undefined)
3 steps (3 undefined)
0m00.000s

Let’s create a step definition file:

// features/step_definitions/authentication_steps.js

module.exports = function () {
  this.Given(/^I am on the sign up page$/, function (callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });

  this.When(/^I sign up using valid credentials$/, function (callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });


  this.Then(/^I should be logged in$/, function (callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });
};

Before trying to implement them, let’s setup Zombie.js.

Setup Zombie.js

The configuration of Cucumber is typically done within a World object, which is automatically instantiated before scenario. Let’s create it and start configure Zombie in it:

// features/support/world.js

var Zombie = require('zombie');

function World() {
  var server = process.env.testServer || 'http://localhost';
  var port = process.env.testPort || '4567';
  this.browser = new Zombie({site: server + ':' + port});
};

module.exports = function() {
  this.World = World;
};

We configure the static pages server using environment variables to be CI friendly, but we use the middleman local values by default. Zombie is now instantiated, we can use it within the first step definition:

  this.Given(/^I am on the sign up page$/, function (callback) {
    var self = this;
    this.browser.visit('/#/sign_up', function() {
      self.browser.assert.text('h2', 'Sign up');
      callback();
    });
  });

Given the nature of Angular, visiting the specified URL is not enough to make the step actually pass. Here, we make sure that we reached the page by asserting on the presence of the ‘Sign up’ title within a h2 markup. After running cucumber, we now get:

   AssertionError: Expected selector "h2" to return one or more elements
         at Assert.text (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:366:7)
         at /home/bruno/workspace/angular-authentication/features/step_definitions/authentication_steps.js:5:27
         at EventLoop.done (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/eventloop.js:589:11)
         at EventLoop.g (events.js:273:16)
         at emitNone (events.js:85:20)
         at EventLoop.emit (events.js:179:7)
         at Immediate._onImmediate (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/eventloop.js:688:71)
         at tryOnImmediate (timers.js:534:15)
         at processImmediate [as _immediateCallback] (timers.js:514:5)

Let’s go and make this one pass. Start by creating an index.html.haml page containing the ng-view directive:

# source/index.html.haml

%ng-view

At this point, it is good to move all the javascripts references from layout.haml to index.html.haml, because the latter will be rendered every time by angular and it would be wasteful to include the files twice:

= javascript_include_tag :all
= javascript_include_tag 'node_modules/angular/angular'
= javascript_include_tag 'app/app'
%ng-view

We then need to install the ngRoute module to handle the routes within our angular application:

npm install --save angular-route

Reference the module in the index.html.haml page:

# source/index.html.haml

...
= javascript_include_tag 'node_modules/angular-route/angular-route'
%ng-view

Now you can edit the app.js file to declare a route matching the sign up page:

// source/javascripts/app/app.js

var app = angular.module('angularAuthentication', ['ngRoute']);
app.config(function ($routeProvider) {
  $routeProvider
    .when('/sign_up', {
      templateUrl: 'sign_up.html'
    })
});

And the last step is to create the corresponding sign_up.html.haml page:

# source/sign_up.html.haml

%h2
  Sign up

which should make the first step pass.
To make the second pass, we will have to fill up the sign up form and submit it. Let’s do this without worrying about the data we’re supposed to receive back for now:

// features/step_definitions/authentication_steps.js

  this.When(/^I sign up using valid credentials$/, function (callback) {
    var email = 'test@example.com';
    var password = 'password';
    this.browser
      .fill('email', email)
      .fill('password', password)
      .fill('password_confirmation', password)
      .pressButton('Sign up', callback);
  });

Let’s edit the sign_up.html.haml page to include the form:

# source/sign_up.html.haml

%form{ role: 'form' }
  .form-group
    %label email
    %br
    %input{ name: 'email', type: 'email' }
  .form-group
    %label password
    %br
    %input{ name: 'password', type: 'password' }
  .form-group
    %label password confirmation
    %br
    %input{ name: 'password_confirmation', type: 'password' }
  %button{ type: 'submit' }
    Sign up

And this should turn the second step green. Now we’re ready to start attacking the meat of the application: the authentication itself. Let’s use the ng-token-auth for this. As usual, install it using npm, as well as the angular-cookie module which is a required dependency:

npm install --save ng-token-auth
npm install --save angular-cookie

Then reference both of the libraries inoto index.html.haml:

# source/index.html.haml
...
= javascript_include_tag 'node_modules/ng-token-auth/dist/ng-token-auth'
= javascript_include_tag 'node_modules/angular-cookie/angular-cookie'
%ng-view

Now before going any further, let’s implement the last step:

  this.Then(/^I should be automatically logged in$/, function (callback) {
    this.browser.assert.text('h3', 'Registration successful');
    callback();
  });

This won’t be the final implementation but this will serve as a scaffold to make sure that we receive the registration response successfully.
Let’s start by updating the sign up page to display an authentication result, through a variable within the $scope:

# source/sign_up.html.haml

%h2
  Sign up
%h3
  {{ authResult }}
...

Now it’s time to introduce a controller serving the sign up template. We will bind a function that will be called by the template when submitting the form. This function will be responsible for the transmission of the registration information to the backend through ng-token-auth:

// source/javascripts/app/controller/signupcontroller.js

app.controller('SignupController', function($scope, $auth) {
  $scope.handleRegBtnClick = function() {
    $auth.submitRegistration($scope.registrationForm)
      .then(function(resp) {
        $scope.authResult = 'Registration successful';
      })
  };
});

Let’s update sign_up.html.haml to transmit the registrationForm to the contoller:

# source/sign_up.html.haml

%h2
  Sign up
%h3
  {{ authResult }}
%form{ 'ng-init': 'registrationForm = {}', 'ng-submit': 'handleRegBtnClick()', role: 'form' }
  .form-group
    %label email
    %br
    %input{ name: 'email', 'ng-model': 'registrationForm.email', type: 'email' }
  .form-group
    %label password
    %br
    %input{ name: 'password', 'ng-model': 'registrationForm.password', type: 'password' }
  .form-group
    %label password confirmation
    %br
    %input{ name: 'password_confirmation', 'ng-model': 'registrationForm.password_confirmation', type: 'password' }
  %button{ type: 'submit' }
    Sign up

We also need to update the routes to map the controller with the corresponding template:

// source/javascripts/app/app.js

app.config(function ($routeProvider) {
  $routeProvider
    .when('/sign_up', {
      templateUrl: 'sign_up.html',
      controller: 'SignupController'
    })

And include the created controller into the layout file:

// source/index.html.haml
...
= javascript_include_tag 'app/controllers/signupcontroller'
...

let’s run the cucumberjs command again to see if we got any progress. We should get the following message:

 AssertionError: '' deepEqual 'Registration successful'
         at assertMatch (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:24:212)
         at Assert.text (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:370:7)
         at World.<anonymous> (/home/bruno/workspace/angular-authentication/features/step_definitions/authentication_steps.js:22:25)
         at _combinedTickCallback (internal/process/next_tick.js:67:7)
         at process._tickCallback (internal/process/next_tick.js:98:9)

As we expected, the authentication message is empty. This is a good indicator against a potential false positive case.

Ok, so now, we need to be provided with the answer to the request we’re making by actually clicking on the submit button. And that’s where Nock comes in. But first, let’s provide ng-token-auth with an API URL to talk to. Edit your app.js:

// source/javascripts/app/app.js

var app = angular.module('angularAuthentication', ['ngRoute', 'ng-token-auth']);

app.config(function($authProvider) {
  $authProvider.configure({
    apiUrl: 'http://localhost:3000'
  });
});

app.config(function ($routeProvider) {
  $routeProvider
    .when('/sign_up', {
      templateUrl: 'sign_up.html'
    })
});

In our example, the API URL is hard-coded for the sake of simplicity. For production purposes, you would probably store it in a config file.
Update world.js:

//features/support/world.js

var nock = require('nock');
var Zombie = require('zombie');

function World() {
  var server = process.env.testServer || 'http://localhost';
  var port = process.env.testPort || '4567';
  this.browser = new Zombie({site: server + ':' + port});
  this.backend = nock(process.env.backendServer || 'http://localhost:3000');
};

module.exports = function() {
  this.World = World;
}

We now have a ‘backend’ through which we can intercept the requests sent to the URL given as parameter (by default http://locahost:3000). After taking a look at the ng-token-auth documentation, we know that in order to get a correct response, we need to send a post to the path mounted to /auth with the parameters: email, password and password_confirmation. We can update the second step in order to intercept such a request with the parameters contained in the field we filled:

//features/step_definitions/authentication_steps.js

  this.When(/^I sign up using valid credentials$/, function (callback) {
    var email = 'test@example.com';
    var password = 'password';
    this.backend.post('/auth', {
      email: email,
      password: password,
      password_confirmation: password
    })
    .reply(200);
    this.browser
      .fill('email', email)
      .fill('password', password)
      .fill('password_confirmation', password)
      .pressButton('Sign up', callback);
  });

Now run cucumberjs. We still get an error:

SecurityError
   at /home/bruno/workspace/angular-authentication/node_modules/zombie/lib/xhr.js:176:28
   at /home/bruno/workspace/angular-authentication/node_modules/zombie/lib/eventloop.js:374:13
   at Immediate._onImmediate (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/eventloop.js:676:13)
   at tryOnImmediate (timers.js:534:15)
   at processImmediate [as _immediateCallback] (timers.js:514:5)
   in http://localhost:4567/#/sign_up

What happened here? We basically tried to make a Crosss Domain Request without being allowed to do so. What we need to do is to configure Nock to accept our host as origin to send CORS requests:

  this.backend = Nock(process.env.backendServer || 'http://localhost:3000')
    .defaultReplyHeaders({
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": '*'    
    });

‘*’ means ‘every host’. Since we’re using this for test purposes, this is acceptable.
We should now be nice an green:

    Given I am on the sign up page
    When I sign up using valid credentials
    Then I should be logged in

1 scenario (1 passed)
3 steps (3 passed)
0m00.437s

Now that we have confidence that the application reacts correctly to a successful response, let’s work on the actual redirection.
Update the last step:

// features/step_definitions/authentication_steps.js

  this.Then(/^I should be logged in$/, function (callback) {
    this.browser.assert.text('h3', 'Welcome test@example.com!');
    this.browser.assert.link('a', 'Sign out', '#');
    callback();
  });

And remove:

%h3
  {{ authResult }} 

From the sign up template. We should now be back to red.
Create a new template home.html.haml which will serve as the landing one. For now, add the following content in it:

# source/home.html.haml

%h3
  Welcome {{ user.email }}

%a{ 'ng-href': '#'}
  Sign out

Then, we map it to the / path in our angular routing:

app.config(function ($routeProvider) {
  $routeProvider
    .when('/', {
      templateUrl: 'home.html'
    });
    .when('/sign_up', {
      templateUrl: 'sign_up.html',
      controller: 'SignupController'
    })
});

We want to make a login request as soon as the registration has worked out:

// source/javascripts/app/controllers/signupcontroller.js

app.controller('SignupController', function($scope, $auth) {
  $scope.handleRegBtnClick = function() {
    $auth.submitRegistration($scope.registrationForm)
      .then(function(resp) {
        $auth.submitLogin({
          email: $scope.registrationForm.email,
          password: $scope.registrationForm.password
        });
      })
  };
});

And now we need to do the actual redirection in case of successful login. Open up app.js and add:

// source/javascripts/app/app.js

...
app.run(['$rootScope', '$location', function($rootScope, $location) {
  $rootScope.$on('auth:login-success', function() {
    $location.path('/');
  });
}]);

What we need to do at this stage is to intercept the POST request originated from the submitLogin() function. Let’s update the second step:

// features/step_definitions/authentication_steps.js

...
  this.When(/^I sign up using valid credentials$/, function (callback) {
    var email = 'test@example.com';
    var password = 'password';
    this.backend.post('/auth', {
      email: email,
      password: password,
      password_confirmation: password
    })
    .reply(200);
    this.backend.post('/auth/sign_in', {
      email: email,
      password: password
    })
    .reply(200);
    this.browser
      .fill('email', email)
      .fill('password', password)
      .fill('password_confirmation', password)
      .pressButton('Sign up', callback);
  });

It’s starting to get a bit messy but we’ll take care of it later on. Run cucumberjs:

 AssertionError: 'Welcome' deepEqual 'Welcome test@example.com!'
         at assertMatch (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:24:212)
         at Assert.text (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:370:7)
         at World.<anonymous> (/home/bruno/workspace/angular-authentication/features/step_definitions/authentication_steps.js:33:25)
         at _combinedTickCallback (internal/process/next_tick.js:67:7)
         at process._tickCallback (internal/process/next_tick.js:98:9)

It looks like we got redirected, but we’re missing the email address. We used the user.email property, but it hasn’t been correctly populated because in our mocked login response, we didn’t specified any returned attribute. Let’s correct this, by including the data (as specified in the ng-token-auth documentation) within the mocked response:

    this.backend.post('/auth/sign_in', {
      email: email,
      password: password
    })
    .reply(200, {
      data:{
        email:email
      }
    });

It is important to note that we include only the data that we actually need in our tests, and not everything that we expect from the API. For instance, we omit to return the email and the authentication token by the registration, because the success response is enough within our scenario context.
Now the scenario should be entirely green again. Before going further, let’s clean this up a bit. Create a new support file that contain the code for our backend mocking:

// features/support/backend_mock.js

var exports = module.exports = {};
var nock = require('nock');
var server = nock(process.env.backendServer || 'http://localhost:3000')
  .defaultReplyHeaders({
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*'    
  });

exports.mockSuccessfulSignUp = function(email, password) {
  server.post('/auth', {
    email: email,
    password: password,
    password_confirmation: password
  })
  .reply(200);
};

exports.mockSuccessfulSignIn = function(email, password) {
  server.post('/auth/sign_in', {
    email: email,
    password: password
  })
  .reply(200, {
    data:{
      email:email
    }
  });
};

We pushed the code for the initialization of the Nock library, and created two mock functions, one for the successful sign up, and one for the successful sign in.
Then, update world.js to make use of this file, and remove the Nock reference:

// features/support/world.js

var Zombie = require('zombie');

function World() {
  var server = process.env.testServer || 'http://localhost';
  var port = process.env.testPort || '4567';
  this.backendMock = require('./backend_mock.js');
  this.browser = new Zombie({site: server + ':' + port});
};

module.exports = function() {
  this.World = World;
};

We can now update the When step to make it more concise:

// features/step_definitions/authentication_steps.js

...
  this.When(/^I sign up using valid credentials$/, function (callback) {
    var email = 'test@example.com';
    var password = 'password';
    this.backendMock.mockSuccessfulSignUp(email, password);
    this.backendMock.mockSuccessfulSignIn(email, password);
    this.browser
      .fill('email', email)
      .fill('password', password)
      .fill('password_confirmation', password)
      .pressButton('Sign up', callback);
  });

Now, both of the mock functions can be reuse in any other step.
It’s time to include another scenario. Let’s do this by updating the feature file:

Feature: Sign up
  As a user
  In order to have access to the application
  I want to sign up
 
  Background: 
    Given I am on the sign up page

  Scenario: using valid credentials
    When I sign up using valid credentials
    Then I should be automatically logged in

  Scenario: using invalid credentials
    When I sign up using inconsistent password and confirmation
    Then I should see the error "Confirmation does not match password"

Notive that we pushed the first step up to a Background section.
Let’s implement the When step:

// features/step_definitions/authentication_steps.js

  this.When(/^I sign up using inconsistent password and confirmation$/, function (callback) {
    var email = 'test@test.com';
    var password = 'password';
    var confirmation = 'passwor';
    this.backendMock.mockConfirmationDoesNotMatchPassword(email, password, confirmation);
    this.browser
      .fill('email', email)
      .fill('password', password)
      .fill('password_confirmation', confirmation)
      .pressButton('Sign up', callback);
  });

And the corresponding mock function:

// features/support/backend_mock.js

exports.mockConfirmationDoesNotMatchPassword = function(email, password, confirmation) {
  server.post('/auth', {
    email: email,
    password: password,
    password_confirmation: confirmation
  })
  .reply(422, {
    errors: ['Confirmation does not match password'],
  });
};

Now the two first steps are passing. Let’s do the last one:

  this.Then(/^I should see the error "([^"]*)"$/, function (error, callback) {
    this.browser.assert.text('h3', error);
    callback();
  });
};

To make the last Then step pass, we need to update both the controller and the view for the sign up, in order to: catch an eventual registration error, and display it within the template. Let’s start by the template:

# source/sign_up.html.haml

%h2
  Sign up
%h3
  {{ authError }}
...

Now the controller:

// source/javascripts/app/controllers/signupcontoller.js

app.controller('SignupController', function($scope, $auth) {
  $scope.$on('auth:registration-email-error', function(ev, reason) {
    $scope.authError = reason.errors[0];
  });
  $scope.handleRegBtnClick = function() {
    $auth.submitRegistration($scope.registrationForm)
      .then(function(resp) {
        $auth.submitLogin({
          email: $scope.registrationForm.email,
          password: $scope.registrationForm.password
        })
      })
  };
});

And this should bring us to a 100% green Feature.

Feature: Sign up
  As a user
  In order to have access to the application
  I want to sign up

  Scenario: using valid credentials
    Given I am on the sign up page
    When I sign up using valid credentials
    Then I should be automatically logged in

  Scenario: using invalid credentials
    Given I am on the sign up page
    When I sign up using inconsistent password and confirmation
    Then I should see the error "Confirmation does not match password"

2 scenarios (2 passed)
6 steps (6 passed)
0m00.629s

The Sign in feature

Write a new sign_in.feature file:

Feature: Sign in
  As a user
  In order to use my registered account
  I want to sign in

  Scenario: using valid credentials
    Given I am on the sign in page
    When I sign in user valid credentials
    Then I should be logged in

You can see that we are reusing a Then step that we have written during the previous feature. Let’s run it, grab the snippets and put them into the authentication_steps.js file:

// features/step_definitions/authentication_steps

...
  this.Given(/^I am on the sign in page$/, function (callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });

  this.When(/^I sign in user valid credentials$/, function (callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });

Let’s go and implement the first one:

  this.Given(/^I am on the sign in page$/, function (callback) {
    var self = this;
    this.browser.visit('/#/sign_in', function() {
      self.browser.assert.text('h2', 'Sign in');
      callback();
    });
  });

This is exetremely similar to the Given step we used for the sign up. Let’s extract a function out of this, and put it in world.js:

// features/support/world.js

...
function World() {
  var server = process.env.testServer || 'http://localhost';
  var port = process.env.testPort || '4567';
  this.backendMock = require('./backend_mock.js');
  this.browser = new Zombie({site: server + ':' + port});

  this.visitPage = function(link, title, callback) {
    var self = this;
    this.browser.visit(link, function() {
      self.browser.assert.text('h2', title);
      callback();
    });
  };
};

And update both of the Given steps:

  this.Given(/^I am on the sign up page$/, function (callback) {
    this.visitPage('/#/sign_up', 'Sign up', callback);
  });

  this.Given(/^I am on the sign in page$/, function (callback) {
    this.visitPage('/#/sign_in', 'Sign in', callback);
  });

Let’s run the feature again, and watch it fail. We need to add a new template, a corresponding controller and route. Create the following sign_in.html.haml template file:

# source/sign_in.html.haml

%h2
  Sign in
%h3
  {{ authError }}
%form{'ng-init': 'loginForm = {}', 'ng-submit': 'handleLoginBtnClick()', role: 'form'}
  .form-group
    %label email
    %br
    %input{name: 'email', 'ng-model': 'loginForm.email', type: 'email'}
  .form-group
    %label password
    %br
    %input{name: 'password', 'ng-model': 'loginForm.password', type: 'password'}
  %button{type: 'submit'}
    Sign in

The route:

app.config(function ($routeProvider) {
  $routeProvider
    .when('/', {
      templateUrl: 'home.html'
    })
    .when('/sign_up', {
      templateUrl: 'sign_up.html',
      controller: 'SignupController'
    })
    .when('/sign_in', {
      templateUrl: 'sign_in.html',
      controller: 'SigninController'
    })
});

The controller:

// source/javascripts/app/controllers/signincontroller.js

app.controller('SigninController', function($scope, $auth) {
});

And don’t forget to include the template in index.html.haml:

...
= javascript_include_tag 'app/controllers/signincontroller'
...

And this should make the first step pass.

For the second one:

  this.When(/^I sign in user valid credentials$/, function (callback) {
    var email = 'test@test.com';
    var password = 'password';
    this.browser
      .fill('email', email)
      .fill('password', password)
      .pressButton('Sign in', callback);
  });

And the last one is already implemented, so let’s try to make everything pass now. Update the signupcontroller:

app.controller('SigninController', function($scope, $auth) {
  $scope.handleLoginBtnClick = function() {
    $auth.submitLogin($scope.loginForm);
  };
});

Let’s run the feature and, another error!

AssertionError: 'Welcome test@test.com!' deepEqual 'Welcome test@example.com!'
         at assertMatch (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:24:212)
         at Assert.text (/home/bruno/workspace/angular-authentication/node_modules/zombie/lib/assert.js:370:7)
         at World.<anonymous> (/home/bruno/workspace/angular-authentication/features/step_definitions/authentication_steps.js:20:25)
         at _combinedTickCallback (internal/process/next_tick.js:67:7)
         at process._tickCallback (internal/process/next_tick.js:98:9)

What’s wrong now? well, we used test@test.com for this step, and the assertion is done against the hard-coded value: test@example.com. Let’s correct this. We need a way to keep track of the email address. The best place to do this is inside World, since we assured that it will be cleaned up between each Scenario and we won’t have any leaky situation. Update the authentication_steps.js file like this:

module.exports = function () {
  this.Given(/^I am on the sign up page$/, function (callback) {
    this.visitPage('/#/sign_up', 'Sign up', callback);
  });

  this.Given(/^I am on the sign in page$/, function (callback) {
    this.visitPage('/#/sign_in', 'Sign in', callback);
  });

  this.When(/^I sign up using valid credentials$/, function (callback) {
    this.currentEmail = 'test@example.com';
    var password = 'password';
    this.backendMock.mockSuccessfulSignUp(this.currentEmail, password);
    this.backendMock.mockSuccessfulSignIn(this.currentEmail, password);
    this.browser
      .fill('email', this.currentEmail)
      .fill('password', password)
      .fill('password_confirmation', password)
      .pressButton('Sign up', callback);
  });

  this.When(/^I sign up using inconsistent password and confirmation$/, function (callback) {
    this.currentEmail = 'test@test.com';
    var password = 'password';
    var confirmation = 'passwor';
    this.backendMock.mockConfirmationDoesNotMatchPassword(this.currentEmail, password, confirmation);
    this.browser
      .fill('email', this.currentEmail)
      .fill('password', password)
      .fill('password_confirmation', confirmation)
      .pressButton('Sign up', callback);
  });

  this.When(/^I sign in user valid credentials$/, function (callback) {
    this.currentEmail = 'test@test.com';
    var password = 'password';
    this.backendMock.mockSuccessfulSignIn(this.currentEmail, password);
    this.browser
      .fill('email', this.currentEmail)
      .fill('password', password)
      .pressButton('Sign in', callback);
  });

  this.Then(/^I should be logged in$/, function (callback) {
    this.browser.assert.text('h3', 'Welcome ' + this.currentEmail + '!');
    this.browser.assert.link('a', 'Sign out', '#');
    callback();
  });

  this.Then(/^I should see the error "([^"]*)"$/, function (error, callback) {
    this.browser.assert.text('h3', error);
    callback();
  });
};

And we now have a fully green state again, and there is one more step to implement:

  this.When(/^I sign in using invalid credentials$/, function (callback) {
    this.currentEmail = 'invalid@email.com';
    var password = 'password'
    this.backendMock.mockInvalidSignIn(this.currentEmail, password);
    this.browser
      .fill('email', this.currentEmail)
      .fill('password', password)
      .pressButton('Sign in', callback);
  });

The corresponding mock function:

// features/support/backend_mock.js

...
exports.mockInvalidSignIn = function(email, password) {
  server.post('/auth/sign_in', {
    email: email,
    password: password
  })
  .reply(422, {
    errors: ['Invalid credentials'],
  });

And the signupcontroller:

// source/javascripts/app/controllers/signincontroller.js

app.controller('SigninController', function($scope, $auth) {
  $scope.$on('auth:login-error', function(ev, reason) {
    $scope.authError = reason.errors[0];
  });
  $scope.handleLoginBtnClick = function() {
    $auth.submitLogin($scope.loginForm);
  };
});

And we’re done. Now if you take a look at the whole application, you should still notice something incorrect: the home path always display a Welcome message, as well as a Sign out link although we’re not actually signed it. We want to differentiate the case where the user is signed in and where he’s not. In the latter, we want to display the authentication alternatives: Sign in or Sign up. Let’s write a new feature visit_home_page.feature:

Feature: Visit home page
  As a user
  In order to step into the application
  I want to visit the home page

  Scenario: without being signed in
    Given I am on the home page
    Then I should see the available options to authenticate

Create a new step definitions file:

// features/step_defintions/home_steps.js

module.exports = function () {
  this.Given(/^I am on the home page$/, function (callback) {
    this.visitPage('/', 'Home', callback);
  });

  this.Then(/^I should see the available options to authenticate$/, function (callback) {
    this.browser.assert.link('a', 'Sign up', '#/sign_up');
    this.browser.assert.link('a', 'Sign in', '#/sign_in');
  });
};

Change the home.html.haml template:

%h2
  Home
%h3{ 'ng-show': 'user.email' }
  Welcome {{ user.email }}!

%p{ 'ng-show': 'user.email'}
  %a{ 'ng-href': '#'}
    Sign out

%p{ 'ng-show': '!user.email'}
  %a{ 'ng-href': '#/sign_up'}
    Sign up
  %br
  %a{ 'ng-show': '!user.email', 'ng-href': '#/sign_in'}
    Sign in

And it should pass.

The Sign out Feature

Let’s now implement the sign out feature. For this tutorial, we’ll add another feature but that’s a good time to ask yourself if you would not be better writing a simple unit test for this to avoid overloading the top of the Test Pyramid.

Feature:
  As a signed in user
  In order to become a simple visitor
  I want to sign out

  Scenario: signing out
    Given I am signed in
    And I am on the home page
    When I sign out
    Then I should see the available options to authenticate

We need two new steps (Given I am signed in, When I sign out). You may be wondering why we’re not using the steps allowing us to sign in that we already have. Well, we’re in a different context here. We don’t really care about the result of the sign it itself, but assume it for our actual action which is the sign out. That doesn’t mean we won’t reuse some of the code we’ve already written. Let’s take a look:

// features/step_definitions/authentiation_steps.js

...
  var submitValidLogin = function (callback) {
    this.currentEmail = 'test@test.com';
    var password = 'password';
    this.backendMock.mockSuccessfulSignIn(this.currentEmail, password);
    this.browser
      .fill('email', this.currentEmail)
      .fill('password', password)
      .pressButton('Sign in', callback);
  };

  this.Given(/^I am signed in$/, function (callback) {
    var self = this;
    this.browser.visit('/#/sign_in').then(function() {
      submitValidLogin.bind(self)(callback);
    });
  });

  this.When(/^I sign out$/, function (callback) {
    this.backendMock.mockSignOut(this.currentEmail);
    this.browser.clickLink('Sign out', callback);
  });

We have extracted the submitValidLogin out so it can be reused. We can now shorten the following step:

  this.When(/^I sign in user valid credentials$/, submitValidLogin);

The mock:

// features/support/backend_mock.js

exports.mockSignOut = function(email) {
  server.post('/auth/sign_out', {
    email: email
  })
  .reply(200)
};

In the home.html.haml template, we’ll call the ng-token-auth signOut() method directly when clicking on the Sign out link:

%h2
  Home
%h3{ 'ng-show': 'user.email' }
  Welcome {{ user.email }}!

%p{ 'ng-show': 'user.email'}
  %a{ 'ng-href': '#', 'ng-click': 'signOut()'}
    Sign out

%p{ 'ng-show': '!user.email'}
  %a{ 'ng-href': '#/sign_up'}
    Sign up
  %br
  %a{ 'ng-show': '!user.email', 'ng-href': '#/sign_in'}
    Sign in

The last step is to tell angular how to handle the logout success event, through ng-token-auth:

// source/javascripts/app/app.js

...
app.run(['$rootScope', '$location', function($rootScope, $location) {
  $rootScope.$on('auth:login-success', function() {
    $location.path('/');
  })
  $rootScope.$on('auth:logout-success', function() {
    $location.path('/');
  });
}]);

We can now run a full cucumberjs, and enjoy the full green outcome.

Final thoughts

We now have reached the functional acceptance of the features. There are still some possible enhancements, like to make a configuration for the API paths, which are for the moment hard-coded, or use a library like Faker.js for the input values (mail, password) but this is a good base for the outside-in development of our front-end. The use of Zombie.js makes the executions of the scenarios much faster that with Selenium and PhantomJS. Not having to directly interact with a real backend is also something that gives us not only a speed boost but also more flexibility, without the overhead to try to synchronize by using scripts to start the server, or clean up the mess between each Scenario. When we’ll go down to the backend and API implementation, we will then have to make sure that the responses in return of your provided requests are conform to what it is expected by the client(s).

I hope this article has been useful for you to start using BDD with angular and Cucumber. If you have any trouble with the steps described (or enhancements), don’t hesitate to write a comment. You’ll also find all the code on GitHub.