NEW Run your Ruby tests in a few minutes and ship faster with Boosters, one-click auto parallelization · Learn more…
×

Semaphore Blog

News and updates from your friendly continuous integration and deployment service.

Smarter Email Notifications

Get future posts like this one in your inbox.

Follow us on

After recent introduction of default email notification settings and consolidation in one tab, today we are happy to announce more improvements.

With verified emails, you are now able to safely register all emails you would like to use to receive build and deploy status notifications.

Most importantly, we are making it possible to receive notifications only for the branches to which you contribute. This is very useful in larger teams with lots of activity going on where getting emails for all branches quickly becomes overwhelming.

Receive email only for your work

Email notifications for big teams can be a bit noisy. That is spoiling the way of getting information about status of builds, for many people.

Who would not like to receive notifications only for his work, and ignore the noise made by other members of the team?

You’re now able to subscribe for build notifications of only the builds that contain your commits. Semaphore matches your work via committers email address and your personal (verified) email address.

If you commit via various email addresses add them to Semaphore account and upgrade your notifications policy to “My work”.

Email Account Menu - My Work

You can find “My work” policy under project’s email notifications settings, or new “Notifications” tab under Account settings.

Email as a first class citizen

We are introducing a way to add, verify and remove multiple emails to be used for notifications. You can find this screen in your Account Settings. Each new email you add needs to be verified. Once you do that, the email can be selected as an option in notification settings.

Email List

Another new feature is “Primary email address” which represents your main email address. By default, the email you use to sign in is set to be your primary email address, used by default for notifications on all projects.

If you open the Account Settings screen, you will notice that we’ve already migrated all email addresses that you have used on Semaphore so far. That means that for most users there will be no need for any migration process, or manual actions.

Feel free to let us know what you think about these improvements.

Happy building!

Platform update on December 23rd

The upcoming platform update is scheduled for Tuesday, December 23rd, 2014.

Bundler is updated to version 1.7.9.

Firefox is updated to version 34 which requires all projects using the selenium-webdriver gem to update it with bundle update selenium-webdriver.

Git is now on version 2.2.0.

libmysqlclient is updated to version 5.6.22.

PHP gets three version updates with 5.4.35, 5.5.19, 5.6.3.

Redis is now on version 2.8.18 which includes various new features and bugfixes.

New things

Go is now installed by default. The included version is 1.4, which among other things, adds support for Android applications.

Trying the new platform

You can evaluate the new platform right away by selecting Ubuntu 14.04 LTS v1412 (release candidate) from the Platform section of Project Settings. We encourage you to try it out, so that we can iron out any issues you may find along the way until the final release on next Tuesday.

A full list of changes is available in the platform changelog.

Queued Builds Cancellation

Last week we introduced high priority branches. Today, we’re introducing a new feature that gives more control over the build queue - queued builds cancellation.

Queued Builds Cancellation in Semaphore Continuous Integration Service

With queued builds cancellation strategy, if you push few times to a branch you don’t have to wait for all builds to complete to get the build results of the last code version. Instead, queued builds will be cancelled and only the most recent version of the code will be tested.

You’ll find the screen to configure this in your project settings, Branches tab.

Queued Builds Cancellation Settings in Semaphore CI

With queued builds cancellation you don’t need to hold your pushes or to cancel builds manually. Just push when you like and get the latest results as soon as possible.

Happy building!

High Priority Branches

Semaphore features a custom build queue algorithm that was designed to make the build flow for a team as pleasant as possible. A developer that pushes frequently won’t be able to “hijack” Semaphore for him or herself. Instead, builds will be shared across the team making everyone happy and productive.

Even though the algorithm serves us well, being mostly invisible, some teams need more control over the build queue. Today we’re introducing high priority branches.

High
Priority Branches in Semaphore Continuous Integration Service

With high priority branches you can choose one or more branches that will be scheduled before others. They can be specified with a list of regular expressions. For example:

hotfix-.*
master

will match hotfix-start-build and master as high priority branches.

We recommend setting only one or two branches as high priority since adding more can make the algorithm insignificant.

You’ll find the screen to configure this in your project settings, Branches tab.

High
Priority Branches Settings in Semaphore Continuous Integration Service

With high priority branches you will be able to build and deploy important code as soon as possible. We’re working on other improvements in this area, so stay tuned for more.

Happy building!

Updated PhantomJS 1.9.8 In a Minor Platform Update

We just released a minor platform update — v1411.1.

The official release of PhantomJS 1.9.8 had an issue with guard-jasmine tests which required a custom compilation of PhantomJS 1.9.8 because there’s a very slim chance that it will be officially released. The binary in this platform update includes the fix.

New things

RabbitMQ is now included in the platform.

If you experience any issues, please let us know immediately so that we can fix them as soon as possible.

A full list of changes is available in the platform changelog.

One Place for All Email Notification Settings

Today, we’re releasing a new way of managing email notifications on Semaphore.

Now you can update your project notification settings from one place, and set a default email notification policy for all projects you or your colleagues add in the future.

You’ll find the new notifications tab in the account menu.

In front of the whole team I would like to thank all the folks that have shared their experiences and workflows with us.

We know that notifications can be a big part of your CI setup, so we will be further refining our notifications system to make it even better. Stay tuned, more updates coming soon.

Meteor.js: Getting Started

Meteor.js tutorial with meteor.js tests.

MeteorJS stands out among the recent big Javascript frameworks with its radical view on creating web applications. It seamlessly connects the client side code with a Node + MongoDB based backend and promises unbelievably fast application development.

As an introduction to Meteor.js, I set out to build a simple but properly tested web application and share what I learned.

Installing the Meteor.js framework

Meteor has a one line install process based on a script from its website:

$ curl https://install.meteor.com | /bin/sh

The above command installs all the needed dependencies along with a MongoDB database. After this you are ready to start writing applications.

Creating an example Todo application

$ meteor create todos

Right after the above command finishes you can launch a working web application with the following:

$ cd todos
$ meteor --port 3000
=> Meteor server running on: http://localhost:3000/

Meteor.js tutorial initial screen.

Unit tests

Meteor works with various testing frameworks and test runners. One of the most popular test runners is Velocity that supports Javascript test frameworks like Jasmine and Mocha.

Jasmine — my favorite Javascript test environment — is easy to set up with Meteor and Velocity.

$ meteor add sanjo:jasmine

In Meteor you should put all your tests in a tests directory. Before you start writing your own tests, first create a directory structure like the following one, as required by sanjo:jasmine.

$ mkdir -p tests/jasmine/server/unit
$ mkdir -p tests/jasmine/client/integration

Velocity also offers a nice HTML reporter that overlays your application and shows a red/gray circle in the upper right corner of the application.

Meteor.js velocity tests.

When you click on the circle it will list all the applications test statuses.

Meteor.js test run.

You can install it with the following command:

$ meteor add velocity:html-reporter

Unfortunatelly, in the moment of writing this blog post, there is no standard way to run Velocity tests from the command line. A Node package velocity-cli is trying to change that, at least until there is no meteor test command.

Integration and acceptance tests

For integration tests you can use Nightwatch which will run Selenium-based browser tests on your application. To install it for your application, run:

$ meteor add clinical:nightwatch

Unfortunately, the setup for the rest of the framework is a little painful. First you need to install npm and node, and java runtime.

$ sudo apt-get install nodejs
$ sudo apt-get install openjdk-7

After which you should execute the following installation steps for nightwatch.

$ ln -s .meteor/local/build/programs/server/assets/packages/clinical_nightwatch/launch_nightwatch_from_app_root.sh run_nightwatch.sh
$ sudo chmod +x run_nightwatch.sh
$ sudo ./run_nightwatch.sh

Your integration tests should be placed inside tests/nightwatch directory.

Writing a todo application

To demonstrate the power and expressiveness MeteorJS, I will walk you through the design process of a simple Todo application. This application will have only two features. It will show all todos a user saved in the database, and it will allow us two add more todos.

The server and the client in a MeteorJS application are interconnected, and as a matter of fact, can be even defined in the same file. Let’s create a file named todos.js in the projects root and initialize a Mongo model that can store todos for our application.

Todos = new Mongo.Collection("todos")

In order to handle and check the validity of newly created todos, a class that represents a Todo can come quite handy. Right under the definition of the Todos collection we can create a Todo class.

function Todo(name) {
  this.name = name;
}

Todo.prototype = {
  valid: function() {
    return this.name && this.name != "";
  },

  save: function() {
    Todos.insert({name: this.name});
  }
};

As a good TDD practitioner we should also create a matching test using Jasmine. We can define a test inside the tests/jasmine/server/unit/todo_spec.js file. At the moment we’re still curious and learning, hence implementation first.

describe("Todo", function() {

  beforeEach(function() {
    this.name = "wash dishes"
    this.todo = new Todo(this.name);
  });

  it("accepts name", function() {
    expect(this.todo.name).toEqual(this.name);
  });

  describe("#save", function() {

    beforeEach(function() {
      spyOn(Todos, "insert");

      this.todo.save();
    });

    it("inserts into the database", function() {
      expect(Todos.insert).toHaveBeenCalled();
    });
  });

});

Filling the database on startup

On application startup we can invoke various actions. For example we could prefill our database with Todos if there is no todo defined.

Put the following in the todos.js file.

function prefillTodos() {
  if(Todos.find().count() !== 0) return;

  var todos = ["Create a Todo", "Learn MeteorJS"];

  todos.forEach(function(todo) { new Todo(todo).save(); });
}

if (Meteor.isServer) Meteor.startup(prefillTodos);

And its tests in the tests/jasmine/server/unit/prefill_todos_spec.js file.

describe("prefillTodos", function() {

  beforeEach(function() {
    spyOn(Todos, "insert");
    spyOn(Todos, "find").and.returnValue({count: function() { return 0; }})

    prefillTodos();
  });

  it("initializes the database with default todos", function() {
    expect(Todos.insert).toHaveBeenCalledWith({name: "Create a Todo"});
    expect(Todos.insert).toHaveBeenCalledWith({name: "Learn MeteorJS"});
  });

});

Templates

MeteorJS uses templates to display information to the user. To create a HTML view for the todos, first create a todos.html in the project root and create a todo view like the following.

<head>
  <title>todo_app</title>
</head>

<body>
  <h1>Welcome to Meteor!</h1>

  {{> todos}}
</body>

<template name="todos">
  {{#each todos}}
    {{> todo}}
  {{/each}}
</template>

<template name="todo">
  <p>{{name}}</p>
</template>

This file contains regular HTML notation, spiced up with some curly braces. These curly braces are Handlebars-like constructs and allow us to dynamically insert data in our Html views.

Each template can receive arbitrary JSON data that can be used to parameterize the output of that template. To invoke the rendering of a template MeteorJS uses the {{> templateName}} notation.

Each of these templates can have a helper or an event handler.

Helpers are handy methods that we can invoke inside the definition of our templates. Event handlers let us react to various Javascript events. The following code segment shows how to set up a helper method for our todos template.

Append the following snippet to the todos.js file.

function setUpTodoTemplates() {
  Template.todos.helpers({
    todos: function () {
      return Todos.find();
    }
  });
}

if (Meteor.isClient) setUpTodoTemplates();

Testing templates

In MeteorJS there is a custom of testing templates with Jasmine. We should start with testing our todos template in the tests/jasmine/client/integration/todos_template_spec.js file.

describe("TodosTemplate", function() {

  beforeEach(function() {
    var div = document.createElement("div");
    var data = [{name: "test1"}, {name: "test2"}];

    var comp = Blaze.renderWithData(Template.todos, data);

    Blaze.insert(comp, div);
  });

  it("contains all the passed todos", function() {
    expect($(div).children().length()).toEqual(2)
  });

});

Creating new todos

Combining the above knowledge, we can easily extend our todo application with a form that creates new todos.

We should start with a template in our todos.html file.

<template name="createTodo">
  <input id="newTodo" type="text">
  <button>Save</button>
</template>

Now, insert a template render invocation inside our todos template.

<template name="todos">
  {{#each todos}}
    {{> todo}}
  {{/each}}
  {{> createTodo}}
</template>

To handle button clicks from out createTodo template, we should add an event handler inside the setUpTodoTemplates. The following code snippet registers an event handler that will create a new Todo.

Put the snippet inside the setUpTodoTemplates in the todos.js file.

Template.createTodo.events({
  'click button': function() {
    var input = $("#newTodo");
    var name = input.val();

    input.val("")

    new Todo(name).save();
  }
});

An acceptance scenario for the above action could be written as the following inside a tests/nightwatch/creatingTodos.js file.

module.exports = {
  "Adding a Todo" : function (client) {
    client
      .url("http://localhost:3000")
      .waitForElementVisible("body", 1000)
      .pause(10000)
      .assert.visible("input[type=text]")
      .assert.visible("button")
      .setValue("input[type=text]", "Test Todo")
      .click("button")
      .assert.containsText("body", "Test Todo")
      .end();
  }
};

To run this end-to-end test run the following.

$ sudo bash run_nightwatch.sh

Final screenshot and source code

Meteor.js finished todo application.

You can clone the source code for the whole todo application from my Github repository.

Summary

Meteor is an exciting new technology that allows us to create eventful application with ease. The Meteor platform seems to be quite stable, however the testing libraries seem to be lagging behind and there is a lack of good documentation.

I encountered several stumbling blocks while working with tests, when it was hard to find any information on the issue. For example incompatibilities between Nightwach and the version of Firefox I was using, running integration tests for the client side sometimes unexpectedly failed with an error or exception. Fortunately each of the test libraries are under heavy development and I am expecting high level of test integration in the near future.

Finally, here is a list of articles that helped me understand Meteor better.

Platform update on November 24th

The upcoming platform update is scheduled for Monday, November 24th, 2014.

Chrome driver is updated to version 2.12.

Git is now on version 2.1.3.

PHP updates include versions 5.4.34, 5.5.18, 5.6.2.

RethinkDB is updated to version 1.15.2.

Ruby gets two updates with 1.9.3p551 and 2.0.0p598.

New things

Ruby 2.1.5 is added to the platform. Read more about this version in the Ruby changelog.

Extending support for Ubuntu 12.04

After reading your letters, we decided that our support for Ubuntu 12.04 is going to be extended. Many servers are still running Precise and we really don’t want to rush anyone to do the switch. Especially in the holiday season when there are more important matters to deal with.

We are extending support for Ubuntu 12.04 LTS v1409 platform until June 1st, 2015.

Release candidate available now

You can try out this update right away by selecting version 1411 (release candidate) in the Platform section of Project Settings. To use Ruby 2.1.5, add the following build command rbenv global 2.1.5. In the final release, it will be avaliable through the interface as usual. As always, we’re eager to hear your feedback and thoughts.

Updates for the final release

PhantomJS is now on version 1.9.8 with an important security update for the notorious POODLE vulnerability.

Chrome has been updated to version 39.0.2171.85.

Some users have reported ocassional issues during apt-get update, so /etc/apt/servers.list is updated with new servers which will hopefully be more stable.

Updated libmysqlclient library

An updated version of libmysqlclient-dev is also included, and is now on version 5.6.21. Projects using the mysql2 gem, version 0.3.11 or older, are required to update the gem to at least version 0.3.12 because otherwise the gem can’t be compiled during bundle install.

For this reason we have postponed the platform update to Tuesday, November 25th.

If you experience any issues, please contact us immediately so that we can fix them as soon as possible.

A full list of changes is available in the platform changelog.

BDD on Rails with Minitest, Part 2: Implementing a Feature

BDD on Rails with Minitest tutorial, part 2

Chris Kottom, guest author on Semaphore blog

This is a guest blog post from Chris Kottom, a longtime Ruby and Rails developer, freelancer, proud dad, and maker of the best chili con carne to be found in Prague. He’s currently working day and night on the upcoming e-book The Minitest Cookbook.


A lot of Rubyists and Rails developers use a behavior-driven (BDD) workflow to give structure to feature development. Adoption of BDD using tools like RSpec and Cucumber has been fueled in part by the availability of high-quality books and blog posts on the subject, but RSpec is certainly not the only game in town. Many programmers have begun looking for alternative approaches, for example, based on the Minitest unit testing library which is included in Ruby distributions and provides similar functionality in a more compact implementation.

In this post, we’ll walk through a complete feature implementation and show how to build realistic functionality into our application using our tests to lead us through the process. If you haven’t read the first part of the series where we described how to assemble and get started using a lean and mean Rails testing stack based on Minitest, you should consult that post first since the examples in this post pick up where we left off.

Feature: Display a List of To-Do Items

In part one, we implemented an empty list of to-do items. Now we’re ready to display a full list of items from the database. To do this, we’ll update the tests to verify that all to-do items are shown in the list and that the name and description of each item is displayed. The updated feature to look like this:

require "test_helper"

feature "To Do List" do
  scenario "displays a list of to-do items" do
    visit root_path
    page.must_have_css("#items")
    within("#items") do
      Item.find_each do |item|
        selector = "#item-#{ item.id }"
        page.must_have_css(selector)
        within(selector) do
          page.must_have_content item.name
          page.must_have_content item.description
        end
      end
    end
  end
end

Once we’ve done that, we re-run our suite and see that our feature spec is once again failing.

ItemsController::GET :index
  test_0001_renders items/index                                  PASS (0.06s)
  test_0002_responds with success                                PASS (0.01s)

To Do List Feature Test
  test_0001_displays a list of to-do items                       ERROR (0.04s)
NameError:         NameError: uninitialized constant Item
        test/features/to_do_list_test.rb:8:in `block (3 levels) in <top (required)>'
        test/features/to_do_list_test.rb:7:in `block (2 levels) in <top (required)>'
        test/features/to_do_list_test.rb:8:in `block (3 levels) in <top (required)>'
        test/features/to_do_list_test.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.12654s
3 tests, 3 assertions, 0 failures, 1 errors, 0 skips

In strict BDD, we’d want to step through this very slowly and one step at a time, but to speed things along and keep from having to write separate migrations for each and every attribute separately, we’ll collapse what we currently know about to-do list items and their attributes into a single failing test case.

require "test_helper"

describe "Item" do
  before do
    @item = Item.new(name: "Write Minitest-BDD post",
                     description: "Show Rails and Capybara example")
  end

  it "has a name attribute" do
    @item.must_respond_to :name
  end

  it "has a description attribute" do
    @item.must_respond_to :description
  end
end

The test syntax you see here is provided by Minitest::Spec which provides a declarative API for describing the expected state of objects. In this example, I can call #must_respond_to on any object with a Symbol or String argument to test whether or not the object responds to the named method. The Minitest::Spec DSL is very compact by itself, but it’s trivial to extend t with your own custom expectations that will improve the readability and expressiveness of your tests.

These new model tests will fail for the same reason as the controller and feature tests: there’s no Item model yet defined.

ItemsController::GET :index
  test_0001_renders items/index                                  PASS (0.06s)
  test_0002_responds with success                                PASS (0.01s)

To Do List Feature Test
  test_0001_displays a list of to-do items                       ERROR (0.04s)
NameError:         NameError: uninitialized constant Item
        test/features/to_do_list_test.rb:8:in `block (3 levels) in <top (required)>'
        test/features/to_do_list_test.rb:7:in `block (2 levels) in <top (required)>'
        test/features/to_do_list_test.rb:8:in `block (3 levels) in <top (required)>'
        test/features/to_do_list_test.rb:7:in `block (2 levels) in <top (required)>'

Item
  test_0002_has a description attribute                          ERROR (0.00s)
NameError:         NameError: uninitialized constant Item
        test/models/item_test.rb:5:in `block (2 levels) in <top (required)>'
        test/models/item_test.rb:5:in `block (2 levels) in <top (required)>'

  test_0001_has a name attribute                                 ERROR (0.00s)
NameError:         NameError: uninitialized constant Item
        test/models/item_test.rb:5:in `block (2 levels) in <top (required)>'
        test/models/item_test.rb:5:in `block (2 levels) in <top (required)>'


Finished in 0.13013s
5 tests, 3 assertions, 0 failures, 3 errors, 0 skips

All of the current errors will be resolved by generating a new Item model with :name and :description attributes and running migrations. Our three errors collapse into one failure.

Item
  test_0001_has a name attribute                                 PASS (0.01s)
  test_0002_has a description attribute                          PASS (0.00s)

ItemsController::GET :index
  test_0001_renders items/index                                  PASS (0.30s)
  test_0002_responds with success                                PASS (0.01s)

To Do List Feature Test
  test_0001_displays a list of to-do items                       FAIL (0.05s)
Minitest::Assertion:         expected to find #item-298486374.
        test/features/to_do_list_test.rb:10:in `block (4 levels) in <top (required)>'
        test/features/to_do_list_test.rb:8:in `block (3 levels) in <top (required)>'
        test/features/to_do_list_test.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.38305s
5 tests, 6 assertions, 1 failures, 0 errors, 0 skips

We now need to modify the view to display our list of items, and just to speed things up here, we’ll break with strict BDD and once again combine individual changes to the view to include the name and description of the item as well as the id selector that we’ll need. In reality, our view might end up looking like this:

<%= content_tag :div, class: 'items' do %>
  <% @items.each do |item| %>
    <%= content_tag :p, id: "item-#{ item.id }", class: 'item' do %>
    <%= content_tag :b, item.name %>
      <br>
      <%= item.description %>
    <% end -%>
  <% end -%>
<% end -%>

In the end, we end up with errors across all tests that touch the view.

Item
  test_0002_has a description attribute                          PASS (0.00s)
  test_0001_has a name attribute                                 PASS (0.00s)

ItemsController::GET :index
  test_0001_renders items/index                                  ERROR (0.11s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___267186296561738095_69977433372580'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___267186296561738095_69977433372580'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'

  test_0002_responds with success                                ERROR (0.00s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___267186296561738095_69977433372580'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___267186296561738095_69977433372580'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'


To Do List Feature Test
  test_0001_displays a list of to-do items                       ERROR (0.01s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___267186296561738095_69977433372580'
        test/features/to_do_list_test.rb:5:in `block (2 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___267186296561738095_69977433372580'
        test/features/to_do_list_test.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.14093s
5 tests, 2 assertions, 0 failures, 3 errors, 0 skips

The cause of these failures is the same in all cases: the collection of to-do items is not yet being set by the controller. To resolve this, we first write a failing test on the controller that checks that the collection is assigned and has the right values.

it "fetches and assigns a list of to-do items" do
  assigns(:items).wont_be_nil

  item_ids = assigns(:items).map(&:id).sort
  item_ids.must_equal Item.pluck(:id).sort
end

Here again, we see more of the Minitest::Spec DSL in action. Minitest provides negative expectations like #wont_be_nil to check for the absence of a given condition - in this case, that the object under test is non-nil.

Unlike RSpec, Minitest doesn’t provide a way of testing whether two Arrays contain the same elements, but this is easy enough to implement in the case of ActiveRecord relations simply by comparing two sorted arrays of IDs as shown in the example.

Including this test adds one more failure to our suite:

To Do List Feature Test
  test_0001_displays a list of to-do items                       ERROR (0.14s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/features/to_do_list_test.rb:5:in `block (2 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/features/to_do_list_test.rb:5:in `block (2 levels) in <top (required)>'

Item
  test_0001_has a name attribute                                 PASS (0.00s)
  test_0002_has a description attribute                          PASS (0.00s)

ItemsController::GET :index
  test_0001_renders items/index                                  ERROR (0.00s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'

  test_0002_responds with success                                ERROR (0.00s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'

  test_0003_fetches and assigns a list of to-do items            ERROR (0.00s)
ActionView::Template::Error:         ActionView::Template::Error: undefined method `each' for nil:NilClass
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'
        app/views/items/index.html.erb:2:in `_app_views_items_index_html_erb___1343446998779429432_69831990148440'
        test/controllers/items_controller_test.rb:6:in `block (3 levels) in <top (required)>'

Finished in 0.15893s
6 tests, 2 assertions, 0 failures, 4 errors, 0 skips

And now we only need to update the controller action to make the tests pass:

class ItemsController < ApplicationController
  def index
    @items = Item.all
  end
end

And drumroll please…

Item
  test_0001_has a name attribute                                 PASS (0.00s)
  test_0002_has a description attribute                          PASS (0.00s)

To Do List Feature Test
  test_0001_displays a list of to-do items                       PASS (0.15s)

ItemsController::GET :index
  test_0001_renders items/index                                  PASS (0.00s)
  test_0002_responds with success                                PASS (0.00s)
  test_0003_fetches and assigns a list of to-do items            PASS (0.00s)

Finished in 0.16973s
6 tests, 13 assertions, 0 failures, 0 errors, 0 skips

This simple example only scratches the surface of what Minitest can do, of course, but it shows off its status as a viable alternative to RSpec. Minitest has a strong and growing ecosystem surrounding it that makes further customization and extension possible to suit your workflow - whether that’s BDD or any other. If you’re looking for a slim alternative to RSpec, it might be worth checking out.

For more information about Minitest and the related gems used in this post, check out some of the following resources:

Ruby 2.1.4 Available In a Minor Platform Update

We just released a minor platform update — v1410.1.

Ruby 2.1.4 is added. This is a security release recommended for all projects using 2.1.

Qt is now downgraded to version 4.8.1 due to the infamous Errno::EPIPE: Broken pipe error that many of you using capybara-webkit have encountered since the last update.

OpenJDK is now on version 1.7 update 71.

Cassandra is updated to 2.1.1.

A full list of changes is available in the platform changelog.

Get future posts like this one in your inbox.

Follow us on