Skip to content

metaskills/holy_grail_harness

Repository files navigation

HolyGrailHarness

A curated Rails application prototype that focuses on simple test patterns for Ruby & JavaScript!

Unlike normal Rails Application Templates or more modern Rails application generators like Rails Composer, the HolyGrailHarness is a basic Rails application that can be considered a prototype and customized via a simple setup script. It is also somewhat opinionated in that it promotes simple and powerful testing choices and focuses on using Ruby 1.9 and up, MiniTest::Spec, Capybara, Poltergeist/PhantomJS, and Konacha. More details on each component and what HolyGrailHarness provides are below.

The HolyGrailHarness is perfect for any of the following:

  • Bootstrapping your next Rails application.
  • Learning and promoting MiniTest::Spec
  • Modern JavaScript testing setups.
  • Teaching Rails and/or JavaScript at your next meetup.

Usage

  • Download the project.
  • Now from the root of "holy_grail_harness" directory.
$ bundle install
$ bundle exec thor setup my_app_name

Make sure to replace my_app_name above with the name of your new Rails application. The setup script has a few options, but the end result will be a new Rails application all ready to go. So why not a normal Rails application template? Although, Rails application templates provide a really nice feature set. It was much easier to bootstrap a new Rails application using this prototype method. The end result is a cleaner Gemfile and application setup that can be vetted and tested from within HolyGrailHarness itself.

The script will rename your directory and prompt you to cd to that directory. Once you do that, run rake test:all to see that everything is working.

$ cd ../my_app_name
$ bundle exec rake test:all

Rails 3

This application prototype will focus on the latest Rails version. At this time, the bundle is locked down to v3.2.11. As Rails updates and is compatible with each component, so will this prototype application be updated. The bundle includes:

  • QuietAssets gem for silent pipeline logging.
  • Thin webserver. Primarily to be automatically used by Konacha but also good for development if you are not using something like Pow.

Testing

MiniTest::Spec All The Way Across The Sky!

Don't wait for Rails 4 to use MiniTest::Spec! This application is using the minitest-spec-rails gem which forces ActiveSupport::TestCase to subclass MiniTest::Spec. This means that you can start using the MiniTest's Spec or Unit structure and assertions directly within the familiar Rails unit, functional, or integration directories. For full details, check out the minitest-spec-rails documentation or some of the test shims within HolyGrailHarness. For example, a test/unit/user_test.rb might look like this.

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  
  let(:bob)   { users(:bob) }
  let(:admin) { users(:admin) }

  it 'must respond true to #admin? for administrators only' do
    admin.must_be :admin?
    bob.wont_be   :admin?
  end

end

Capybara Integration Tests With Poltergeist Using PhantomJS

You don't need Cucumber to write good integration tests. Instead use the basic Capybara DSL directly within a Rails integration tests with the most bad ass driver available, Poltergeist, which is built on top of PhantomJS. Never again worry about installing Qt so you can compile capybara-webkit, just go download a pre-compiled PhantomJS binary for your specific platform and enjoy 20% faster integration test runs vs capybara-webkit.

Integration tests are still within the ActionDispatch::IntegrationTest class and as promised, MiniTest::Spec is available here too. Each test file needs to require the test_helper_integration which provides the following base features.

  • Sets page size to that of a 13" MacBook Air.
  • Resets Capybara sessions after each test.
  • Provides a #save_and_open_page, or #page! for short, screen shot method.
  • Ensures a single ActiveRecord DB connection for transactional test runs.
  • An #execjs helper for bridging Ruby and the JavaScript under test.

HolyGrailHarness comes with a integration test example in the test/integration/application_test.rb file. An integration test might look something like this.

require 'test_helper_integration'

class ApplicationTest < ActionDispatch::IntegrationTest
  
  before { visit root_path }

  let(:h1) { find 'h1' }

  it 'renders' do
    h1.must_be :present?
  end

end

Konacha JavaScript Tests Using PhantomJS

Move over Jasmine(rice), Konacha is the way to test your JavaScript now. Konacha is a Rails engine that allows you to test your JavaScript with the Mocha test framework and Chai assertion library. Konacha's killer feature is a sandboxed <iframe> for each test spec to run within as well as full Rails asset pipeline integration. The HolyGrailHarness does all the work to get your Konacha spec/javascripts directory all setup and ready to go. Highlights include:

  • An initializer that sets up Poltergeist as the Capybara driver.
  • A directory structure for model, view, and controller specs.
  • A spec_helper.js.coffee for your specs to require. Provides global setup, configurations and vendor requires.

HolyGrailHarness also has a spec/javascripts/spec_helper directory meant for helpers and extensions that should be available to all specs. We have included a fixtures.js.coffee file that demonstrates how to setup JSON data fixtures for use from anything to stubbing requests to instantiating new model objects. We also have a helpers.js.coffee file that exposes a few top level functions that make debugging your JavaScript easy. Below are the vendored JavaScript libraries that are required by the spec_helper.

Because your CI system should run all your tests, the HolyGrailHarness has added a Rake task to the test namespace that runs the default rails test task (units, functional, integrations) then your Konacha tests.

$ rake test:all     # Runs all Rails tests, then Konacha tests.

Guard

TDD in style and run your tests when you hit save! Both guard-minitest and guard-konacha are bundled and ready to go. A basic Guardfile is already setup too. Unlike most, this one is split into two groups :ruby or :js. This lets you focus on either everything or a specific language for your tests.

$ guard             # Monitor both Ruby and JavaScript tests.
$ guard -g ruby     # Monitor Ruby tests.
$ guard -g js       # Monitor JavaScript tests.

The Guardfile assumes you are running OS X and wish to use the Ruby GNTP (Growl Notification Transport Protocol). If this is not the case, consult the Guard documentation on different system notification alternatives.

Factories And Fixtures

ActiveRecord YAML fixtures suck, but so do slow tests that rely on an empty database with excessive setups based on factories. The answer? Take advantage of the best each has to offer. Use factories to populate fixtures into the test database while leveraging database transactions during your test runs. The end result is a known factory story with the ability to create more test data as needed using the same factories. Allowing factories to properly hook into model logic means no more decomposing business logic into YAML text files. How?

The HolyGrailHarness bundles the named_seeds gem along with the factory_girl gem. The NamedSeeds library checks for the existence of a db/test/seeds.rb file and if present, loads that file. Just like Rails' own db/seeds.rb anything in this file goes. The only difference is that this seed file is populated right before you tests are run so they persist between transactions. You also get the benefit of using this same seed data in development as part of the normal Rails db:setup process. Read the full documentationn on their site on how to use it. Below is a brief example.

Create factories in the test/factories directory. Note, factories are best when they make valid garbage™, so the HolyGrailHarness also requires the forgery gem to help with that.

# In test/factories/user_factory.rb

FactoryGirl.define do
  
  factory :user do
    email      { Forgery::Email.address }
    first_name { Forgery::Name.first_name }
    last_name  { Forgery::Name.first_name }
    password   'test'  
  end
  
end

When making seed data, be explicit with your attributes that may be forged in the factory, database seeds should be consistent and have meaningful attributes. In this example we are creating an admin user. Note too how we are using NamedSeeds.identify which mimics AcctiveRecord's fixture identity. This gives us a handle to the fixture within our tests. We also create the @admin instance variable because we might want to use that user later on in the fixture story.

# In db/test/seeds.rb

require 'factory_girl'
FactoryGirl.find_definitions rescue true
include FactoryGirl::Syntax::Methods

@admin = create :user, id: NamedSeeds.identify(:admin), 
                       first_name: 'Admin', last_name: 'User', email: 'admin@test.com'

Lastly, in your test/test_helper.rb file, declare that you have a named seed to the users model. This will allow your tests to act just like those with ActiveRecord fixtures and use the users(:admin) helper to get to that seeded fixture.

# In test/test_helper.rb

class ActiveSupport::TestCase
  
  named_seeds :users

end

MVC JavaScript

The HolyGrailHarness wants you to use some type MV* structure for your JavaScript. The setup script supports Spine.js as an option, however you can decline and all traces of Spine.js will be removed. If so, the following features will still remain.

A single JavaScript namespace on the window object. This namespace creates a model, view, controller object structure that direly matches to the app/assets/javascripts/#{my_app_name}/(model|view|controller) directory structure within the Rails asset pipeline. This JavaScript namespace and matching directories will be changed to your new application name as part of the setup task. Here is an example of a User model whose corresponding file would be found in the app/assets/javascripts/my_app_name/models/user.js.coffee file.

class @MyAppName.App.Models.User extends View  
  @configure 'User', 'id', 'email'

The main application.js file requires all vendor frameworks, then the index.js.coffee within your application name directory. Use this file to boot your JavaScript application and/or setup your root view controller.

Also included is the SpacePen view framework. SpacePen is a powerful and minimalist client-side view framework authored in CoffeeScript. It is actually a jQuery subclass which makes your views really easy to traverse and respond to controller events. Read my View Controller Patterns With Spine.js & SpacePen article to learn why views should not be dumb and how you can take advantage of SpacePen no matter what JavaScript MV* framework you use.

With Spine.js

If you choose to use Spine.js as your JavaScript MVC structure, the setup script will create a git submodule to the Spine repository to the vendor/assets/javascripts/spine directory. This allows your project to use the the source CoffeeScript files, which makes for a wonderful learning experience to both Spine.js and idomatic CoffeeScript.

By default the index.js.coffee will require all Spine components. This includes manager (stacks), ajax, route, and relation. Remove anything that you do not need. This file also defines the root view controller along with a MyAppName.App.Index.init() class level initialization function. This is called in the main application.html.erb layout file for you too. Likewise, the application init is done in the Mocha before filters mentioned above in both the spec_helper.js.coffee and fixtures.js.coffee files. If you examine these files closely, you will see how they make use of Mocha's done() callback so that you can cleanly abstract AJAX mocks and anything else related to your JavaScript application's boot process. Here is an example of how you might setup your initApplication().

@initApplication = (callback) =>
  bob = MyAppName.Test.Seeds.users.bob
  $.mockjax url: "/users/#{bob.id}", responseText: MyAppName.Test.Response.bobInitial.responseText
  MyAppName.App.Models.User.fetch id: bob.id
  MyAppName.App.Models.User.one 'refresh', callback

No JavaScript project should be without a local notification system to help keep disparate components up to date. Thankfully, Spine's event module makes a local PubSub system a breeze. The HolyGrailHarness has a notifications.js.coffee that exposes a class level bind() and trigger() to any event string/namespace you want. To make more simple, we recommend creating class level functions that expose the event name as the function name and pass the args to the handle() function. We have done this for the MyAppName.Notifications.appReady() to demonstrate. Calling this function will trigger the app.ready event and passing a function to this function will bind that function to the same event name.

Sass & Compass

Sass is the only way to write CSS for today's modern web applications. Compass is the CSS framework that no Sass user should go without. Together they provide a foundation for writing beautiful CSS using pre-built time saving functions. The HolyGrailHarness includes both the sass-rails and compass-rails gems.

To get you started on the right path, we have also created a basic structure within the app/assets/stylesheets asset pipeline directory to help you organize your Sass files. Here is the directory structure below.

├── application.css
├── application
│   ├── _layout.scss
│   ├── index.scss
│   ├── components
│   │   └── _foo.scss
└── shared
    ├── _animations.scss
    ├── _fonts.scss
    ├── _mixins.scss
    ├── _placeholders.scss
    ├── _variables.scss
    └── base.scss

The application.css file.

Never write CSS in application.css. Say what? I know right, but trust me. Just consider this file a top level bundle dependency that only requires other top level bundle assets. Here is the contents of that file. Notice how it requires a bundle called twitter and an index. One is for twitter bootstrap, see section below, and the other is the index to your own Sass framework.

/*
 *= require application/twitter
 *= require application/index
*/

The shared directory.

Think of this as your own Compass framework. The base.scss is your single file to @import to get everything loaded and ready to go. Nothing in any of the shared files should generate CSS! Importing shared/base should act just like importing compass. Use these files for setting your own variables and creating misc helper functions & mixins. There is a variables file for... variables! Another for animations, fonts and mixins too.

Pay special attention to the _placeholders.scss file. If you do not know about Sass 3.2's placeholder selectors (silent classes) and how they make presentational classes efficiently extended by semantic ones, then I highly suggest you read Dale Sande's presentation titled Sass 3.2 Silent Classes on Speaker Deck.

Below is the contents of the base.scss file, take note of the order. See too how we import the entire Compass framework. This means that all of your Sass code in any of the shared files can take full advantage of both Bootstrap and Compass' variables and mixins. Epic win!

// Think of this file as your own compass. Importing the base, never generates CSS.

@import "shared/variables";
@import "bootstrap/mixins";
@import "compass";
@import "compass/layout";
@import "compass/css3/user-interface";
@import "shared/fonts";
@import "shared/mixins";
@import "shared/animations";
@import "shared/placeholders";

The application directory.

Organize this as you see fit. We have started you off by creating a _layout.scss file for your general layout/structure styles. There is also a components directory which all sub files are imported via a glob. The idea is that components are not dependent upon another. Files that might go in here are things like datepicker, navigation, and general files named after components or widgets. Below is what the application/index.scss looks like.

@import "shared/base";
@import "./layout";
@import "components/*";

If you are more advanced with your CSS and like the idea of style guides, take a looks a the Toadstool style guide framework.

Twitter Bootstrap

Twitter Bootstrap is awesome, but LESS is not. That is why the HolyGrailHarness uses the bootstrap-sass gem that converts all the Bootstrap LESS files to Sass. Making them ready to import via the Rails asset pipeline.

As shown above in the Sass section, we require the application/twitter.scss bundle asset from the top level application.css bundle file. This twitter bundle file, contents below, take advantage of your shared variables before importing bootstrap from the gem. In this way you can define variables that tweak bootstrap. A good example would be button colors, column widths, etc. Later on in the file you can extend bootstrap styles to your liking. For instance, add more padding to buttons.

@import "shared/variables";
@import "bootstrap";
@import "font-awesome";

// Tweak or redefine Twitter classes below.

Font Awesome

The glyph icons included in Twitter Bootstrap are horrible for hi-resolution "retina" displays typically found on mobile devices. Thankfully the Font Awesome project provides a drop in replacement that instead uses icon fonts vs raster images.

The HolyGrailHarness vendors these font files and the needed font-awesome.scss file and requires them as part of the Twitter Bootstrap bundle shown above. More advanced users may prefer to only include the icon fonts needed in their application or a few custom icons. If that is the case, check out Font Custom, webfonts from the comfort of the command line.

About

🙏 A curated Rails application prototype that focuses on simple test patterns for Ruby & JavaScript!

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •