Skip to main content

Mobomo webinars-now on demand! | learn more.

For Federal Offices of Communication, the act—and art—of balancing websites that both cater to the public and promote the organizational structure and mission of the organization is always top of mind. Accordingly, those partnering with Federal offices must prioritize meeting both needs when designing and building agency sites. On numerous projects, our team has successfully managed to increase usability and deliver user-centric designs while simultaneously building sites that allow our Federal clients to bolster their brand. A sample of results for some clients:

-a swift 4% increase in first-time visitor overall satisfaction
-76% of all mobile users strongly agreeing that the new site made content easier to find
-88% of frequently visiting teens being satisfied with the new site

Below are some of the tools we’ve implemented to achieve success:

Navigation and Information Architecture

Treejack is a great usability testing tool that development teams can wield to test the information architecture and navigation of the site prior to even beginning a design. It is best used to test the findability of topics in a website using different navigational hierarchies. For one of our projects, both internal and external stakeholders were given 46 tasks to perform using a variety of different navigation hierarchies to find the most optimal site organization for both constituent groups.

treejack-information-architecture-software

Usability Testing

For usability testing, our team leverages both Loop11 and Usertesting.com. Using a live, interactive environment, both of these tools allow development teams to gain deep understanding of user behavior by observing users as they complete a series of tasks and questions on the site and/or mobile app in question. Interactions are captured and then analyzed in comprehensive reports. As an added bonus, Usertesting.com provides video footage of the interaction for review:

user-testing-video-footage

http://bit.ly/1rRvEAm

In summary, Federal websites and applications are often designed with too much emphasis on organizational hierarchy and goals, and too little focus on meeting end-users’ needs and expectations. User-Centric Design (UCD) tools can help government agencies buck this trend, however, allowing them to create websites and applications that engage users and maximize their interaction. Ultimately, this results in a sure win-win: Federal agencies’ constituents can experience an efficient, satisfying, and user-friendly design, and—with constituents’ increased engagement—organizations can ensure that their missions and information are communicated effectively. Act balanced.

Categories
Author

With the proliferation of client side apps and frameworks, testing strategies for writing client side code has become a major necessity. However, unlike most of the ruby community, effort in this department had been lacking…

Working on apps with zero client side tests, creates a series of unnecessary hurdles. It limits the developers ability to perfect the code, transfer knowledge to other developers, even pinpoint needed improvements.

Over the last few years though, I’ve developed a few techniques to counteract this lack of testing; by creating custom unit tests for Backbone.Marionette applications.

Notes on Backbone.Marionette testing:

  • Coffeescript will be utilized for the backbone code and tests.
  • Tests will be written using the Jasmine testing library
  • Backend for this app is a simple rails app
  • Jasminerice gem will compile coffeescript jasmine tests to javascript for browser.

    The Application:

    Alright, so you’ve got your requirements to build a very exciting to-do list application:

  • Ability to add new to-do items
  • Ability to mark to-do items as done
  • Ability to delete to-do items
  • For the sake of brevity, I will assume you have the basic setup and boot strap of a backbone application and are ready to render the view to your root route /.

    To quickly illustrate this, see index.html.erb file below. Notice it bootstraps my Todo items from the server into the ToDoApp backbone application.

    <div id="todos"></div>  <script type="text/javascript">   $(function() {      window.MyApp.start({todos: <%= @todos.to_json.html_safe %>});   }); </script> 

    Also, below is the main todo_app.js.coffee file for my ToDoApp backbone application:

    #= require_self #= require_tree ./templates #= require_tree ./models #= require_tree ./views #= require_tree ./routers  window.ToDoApp =   Models: {}   Collections: {}   Routers: {}   Views: {}  window.MyApp = new Backbone.Marionette.Application()  MyApp.addRegions   main: "#todos"  MyApp.addInitializer( (options) ->   todoView = new ToDoApp.Views.TodoCollectionView     collection: new ToDoApp.Collections.TodosCollection(options.todos)   MyApp.main.show(todoView) ) 

    As you can see, I am simply bootstrapping Todo objects from the server and rendering a TodosCollectionView into the #todos div.

    Test-Driving Functionality:

    Now to test drive our TodoCollectionView...

    Starting with the tests, the functionality of this view enables you to create a new Todo item as well as render it. Thus, we can write tests assuming there will be some sort of text field and add button to create the item.

    Here’s a test file below:

    describe "ToDoApp.Views.TodoCollectionView", ->   describe "rendering", ->     describe "when there is a collection", ->       it "renders the collection", ->         collection = new ToDoApp.Collections.TodosCollection([           {id: 1, title: 'make example test', done: false}           {id: 2, title: 'make example work', done: false}         ])         view = new ToDoApp.Views.TodoCollectionView(collection: collection)         setFixtures(view.render().$el)         expect(view.children.length).toEqual(2)      describe "when there is not a collection", ->       it "renders the collection", ->         collection = new ToDoApp.Collections.TodosCollection([])         view = new ToDoApp.Views.TodoCollectionView(collection: collection)         setFixtures(view.render().$el)         expect(view.children.length).toEqual(0)    describe "events", ->     describe "click .add", ->       it "adds a new model to the collection", ->         view = new ToDoApp.Views.TodoCollectionView(collection: new ToDoApp.Collections.TodosCollection())         setFixtures(view.render().$el)         $('.add').click()         expect(view.collection.length).toEqual(1)        it "sets the new model's title from the text field", ->         view = new ToDoApp.Views.TodoCollectionView(collection: new ToDoApp.Collections.TodosCollection())         setFixtures(view.render().$el)         $('#new-todo').val("This is new")         $('.add').click()         expect(view.collection.models[0].get("title")).toEqual("This is new")        it "clears the value in the text field", ->         view = new ToDoApp.Views.TodoCollectionView(collection: new ToDoApp.Collections.TodosCollection())         setFixtures(view.render().$el)         $('#new-todo').val("This will be cleared")         $('.add').click()         expect($('#new-todo').val()).toEqual("") 

    When we run these tests (on the command line, for ease of copy + paste) we get:

    Run all Jasmine suites Run Jasmine suite at http://localhost:57702/jasmine Finished in 0.009 seconds ToDoApp.Views.TodoCollectionView   rendering     when there is a collection       ? renders the collection         ? TypeError: 'undefined' is not a constructor (evaluating 'new ToDoApp.Views.TodoCollectionView({ > [#]             collection: collection > [#]           })') in http://localhost:57702/assets/spec.js (line 16416)     when there is not a collection       ? renders the collection         ? TypeError: 'undefined' is not a constructor (evaluating 'new ToDoApp.Views.TodoCollectionView({ > [#]             collection: collection > [#]           })') in http://localhost:57702/assets/spec.js (line 16427)   events     click .add       ? adds a new model to the collection         ? TypeError: 'undefined' is not a constructor (evaluating 'new ToDoApp.Views.TodoCollectionView({ > [#]             collection: new ToDoApp.Collections.TodosCollection() > [#]           })') in http://localhost:57702/assets/spec.js (line 16439)       ? sets the new model's title from the text field         ? TypeError: 'undefined' is not a constructor (evaluating 'new ToDoApp.Views.TodoCollectionView({ > [#]             collection: new ToDoApp.Collections.TodosCollection() > [#]           })') in http://localhost:57702/assets/spec.js (line 16448)       ? clears the value in the text field         ? TypeError: 'undefined' is not a constructor (evaluating 'new ToDoApp.Views.TodoCollectionView({ > [#]             collection: new ToDoApp.Collections.TodosCollection() > [#]           })') in http://localhost:57702/assets/spec.js (line 16458) 5 specs, 5 failures Done. Guard::Jasmine stops server. rake aborted! Some specs have failed 

    We got one hundred perecent failure because we haven't created our TodoCollectionView yet. Let's do that:

    class ToDoApp.Views.TodoCollectionView extends Backbone.Marionette.CompositeView 

    Quick note: The backbone model and collection (Todo and TodosCollection) were pre-made. Thus, since there is no functionality outside of backbone, and therefore no tests, I'll illustrate here...

    class ToDoApp.Models.Todo extends Backbone.Model   paramRoot: 'todo'    defaults:     title: null     done: null  class ToDoApp.Collections.TodosCollection extends Backbone.Collection   model: ToDoApp.Models.Todo   url: '/todos' 

    Now re-run the tests.

    Run all Jasmine suites Run Jasmine suite at http://localhost:57732/jasmine Finished in 0.011 seconds ToDoApp.Views.TodoCollectionView   rendering     when there is a collection       ? renders the collection         ? TemplateNotFoundError: Cannot render the template since it's false, null or undefined. in http://localhost:57732/assets/spec.js (line 14154)     when there is not a collection       ? renders the collection         ? TemplateNotFoundError: Cannot render the template since it's false, null or undefined. in http://localhost:57732/assets/spec.js (line 14154)   events     click .add       ? adds a new model to the collection         ? TemplateNotFoundError: Cannot render the template since it's false, null or undefined. in http://localhost:57732/assets/spec.js (line 14154)       ? sets the new model's title from the text field         ? TemplateNotFoundError: Cannot render the template since it's false, null or undefined. in http://localhost:57732/assets/spec.js (line 14154)       ? clears the value in the text field         ? TemplateNotFoundError: Cannot render the template since it's false, null or undefined. in http://localhost:57732/assets/spec.js (line 14154) 5 specs, 5 failures Done. Guard::Jasmine stops server. rake aborted! Some specs have failed 

    At this stage, while all failed, we are getting better errors. Let’s try giving it a template to render (JST was utilized for template):

    class ToDoApp.Views.TodoCollectionView extends Backbone.Marionette.CompositeView   template: JST['backbone/templates/todos/index'] 

    and the template file:

    <div id="new">   <input id="new-todo" type="text" placeholder="new item" />   <button class='add'>Add</button> </div>  <ul id="todos"> </ul> 

    As you can see, I skipped ahead and put in the text field, add button, and container for the Todo items. Now, let's rerun the tests.

    Run all Jasmine suites Run Jasmine suite at http://localhost:57764/jasmine Finished in 0.026 seconds ToDoApp.Views.TodoCollectionView   events     click .add       ? adds a new model to the collection         ? Expected 0 to equal 1.       ? sets the new model's title from the text field         ? TypeError: 'undefined' is not an object (evaluating 'view.collection.models[0].get') in http://localhost:57764/assets/spec.js (line 16467)       ? clears the value in the text field         ? Expected 'This will be cleared' to equal ''. 5 specs, 3 failures Done. Guard::Jasmine stops server. rake aborted! Some specs have failed 

    Nice! Looks like a couple of our tests passed this time; namely the tests about rendering the view with a collection. However, our add button clicking tests still failed.

    One at a time, let's make them pass. First priority, making sure clicking the add button adds a model to the collection:

    class ToDoApp.Views.TodoCollectionView extends Backbone.Marionette.CompositeView   template: JST['backbone/templates/todos/index']    ui:     newTitle: "#new-todo"    events:     "click .add" : "addNewTodoItem"    addNewTodoItem: ->     @collection.create(new ToDoApp.Models.Todo(title: @ui.newTitle.val())) 

    Re-run the tests.

    Run all Jasmine suites Run Jasmine suite at http://localhost:57808/jasmine Finished in 0.027 seconds ToDoApp.Views.TodoCollectionView   events     click .add       ? clears the value in the text field         ? Expected 'This will be cleared' to equal ''. 5 specs, 1 failure Done. Guard::Jasmine stops server. rake aborted! Some specs have failed 

    Cool, only one failure left, and it looks like simply clearing the input field, after creation of a new Todo item, should do the trick. Here’s the code for that:

    class ToDoApp.Views.TodoCollectionView extends Backbone.Marionette.CompositeView   template: JST['backbone/templates/todos/index']    ui:     newTitle: "#new-todo"    events:     "click .add" : "addNewTodoItem"    addNewTodoItem: ->     @collection.create(new ToDoApp.Models.Todo(title: @ui.newTitle.val()))     @ui.newTitle.val("") 

    Re-run our tests.

    Finished in 0.025 seconds ToDoApp.Views.TodoCollectionView   rendering     when there is a collection       ? renders the collection     when there is not a collection       ? renders the collection   events     click .add       ? adds a new model to the collection       ? sets the new model's title from the text field       ? clears the value in the text field 5 specs, 0 failures Done. 

    Hoorah, all green!

    Alright, only one more housekeeping task. If you were following along, you may have noticed the application doesn’t work as expected in browser view. This is because we have not declared what the ItemView for this collection is supposed to be...

    Let's write a test that drives this functionality out:

    it "renders TodoViews as the itemViews", ->   collection = new ToDoApp.Collections.TodosCollection([     {id: 1, title: 'make example test', done: false}   ])   view = new ToDoApp.Views.TodoCollectionView(collection: collection   view.render().$el   expect(view.children.first().constructor.name).toEqual("TodoView") 

    Re-run the tests that fail.

    Expected 'TodoCollectionView' to equal 'TodoView'. 

    Next, lets make sure we set the ItemView property of our collection view (as well as the itemViewContainer property for good measure):

    class ToDoApp.Views.TodoCollectionView extends Backbone.Marionette.CompositeView   template: JST['backbone/templates/todos/index']   itemView: ToDoApp.Views.TodoView   itemViewContainer: "#todos"    ui:     newTitle: "#new-todo"    events:     "click .add" : "addNewTodoItem"    addNewTodoItem: ->     @collection.create(new ToDoApp.Models.Todo(title: @ui.newTitle.val()))     @ui.newTitle.val("") 

    Re-run our tests.

    Expected 'TodoCollectionView' to equal 'TodoView'. 

    Oh no! We get the same failure, what happened? Well, we never made our TodoView view. Let's do that now (see below):

    View:

    class ToDoApp.Views.TodoView extends Backbone.Marionette.ItemView   template: JST['backbone/templates/todos/_todo']   tagName: 'li' 

    Template:

    <p><%= title %></p> <p><%= done %></p> 

    Cool, now let's re-run our tests.

    Finished in 0.026 seconds ToDoApp.Views.TodoCollectionView   rendering     when there is a collection       ? renders the collection       ? renders TodoViews as the itemViews     when there is not a collection       ? renders the collection   events     click .add       ? adds a new model to the collection       ? sets the new model's title from the text field       ? clears the value in the text field 6 specs, 0 failures Done. 

    Ta-da! 100% success! We now have a small backbone app, fully tested and fully functioning, that allows you to create and list todo items.

    Want to learn more about client-side testing? Let us know! We'd love to hear from you.

Categories
Author

Don't cry. We've all been there too. Regression issues in the presentation layer make the entire team go crazy. Why can't we have a methodical way of testing the UI to ensure once designs are styled as views, they stay the way they were created?

The first release of GreenOnion is an attempt at just that. It moves from testing code to testing the visual interfaces that designers have envisioned and end-users will interact with. Developed after reading Jeff Kreeftmeijer's post on creating image diffs, GreenOnion brings together both OilyPNG and Capybara to test the UI.

RSpec, MiniTest, Test::Unit all allow devs to create solid code using BDD/TDD, but there has yet to be a good way to make sure bugs are not introduced in HTML and CSS. Sure, we have Watir and Capybara (a dependency of GreenOnion), but these tools are more focused on the functionality of views rather than how they actually look. GreenOnion captures and compares what the user would actually see. There will never be a sure-fire way to fully automate testing something so subjective as visual aesthetic, but GreenOnion should help the designer or developer by highlighting where things might be going wrong.

The GreenOnion workflow

  • Run skin method for the 1st time on a URL (best case would be if the styling of the page was correct)
  • Save skin
  • Run skin method for the 2nd time on a URL
  • Compare 1st skin to the 2nd (fresh) skin

Percent of change

GreenOnion.skin_percentage(url, threshold [optional])

The primary feature of GreenOnion is seeing how much (if at all) a view has changed from one instance to the next, and being alerted when a view has surpassed into an unacceptable threshold.

What do we mean by threshold?

The threshold is the percentage of pixels that are allowed to change from one iteration of a view to the next. This will give the view tolerance of change, and if that tolerance has been surpassed, then the test will alert the developer.

Visual change

GreenOnion.skin_visual(url)

Once you are aware of a issue in the UI, you can also check out where the offending issue may have taken place. You should open your "skins" directory, and check out the files with "_diff.png" at the end of the filename. The pixels will be highlighted and will have the same effect as the Photoshop "difference" filter.

Roadmap

We hope that this is just the beginning of more robust testing in the UI. Here are some areas that we will focus on in the future:

  • Screenshots can either be viewed as a visual diff, or overlayed newest over oldest and viewed as an onion-skin with sliding transparency.
  • Allow for flexibility in picking browsers
  • More robust skinner generator
  • Should allow for testing using fixtures/factories
  • Flexibility in skin filenaming
  • Test with UI events
Categories
Author

This post is a part of Polishing Rubies, an end-to-end guide to Ruby open source development.

« Back to Part 2: Creating a Gem

A good toolchain is important for any development project. It makes the lives of developers easier by abstracting away or automating repetitive tasks. You should always spend some time at the outset of a project making sure you're using all of the best tools available. On an open source project this is 10 times more important.

Instead of building a toolchain that will be used by your company or small team, you're building a toolchain that will potentially be used by dozens, hundreds, or even thousands of other developers. An annoyance that costs five minutes, multiplied by all those developers, equals hours and hours of lost potential productivity. Now that we've bootstrapped the directory structure of our gem, it's time to get our toolchain up and running so that development for you (and all of your contributors some day) is as painless and fast as possible.

Test Setup

Having tests for your gem is fundamentally important. If you have no tests, you have no way to vet incoming pull requests, check for regressions between versions, or really have any control over the quality of your project. Luckily, the Ruby community has a strong testing culture so there are great tools to use and widespread adoption of testing practices.

I'm going to walk through how to set your gem up for testing via RSpec. In general the Ruby community nearly universally uses either RSpec or the built-in Test::Unit as its framework of choice. I would love to cover Test::Unit as well, but I don't have enough of a grasp on best practices to be able to tell you how to do that here. If you would like to contribute such a section to this guide, by all means please fork it on GitHub and I'll be happy to update the content.

The first step to using RSpec in your tests is to add it to your project's gemspec:

gem.add_development_dependency 'rspec', '~> 2.9' 

This tells RubyGems that RSpec is a necessary library for developers who are going to try to contribute to your gem. Once you've added the dependency, run bundle to update your dependencies with Bundler. Note that because Bundler is capable of reading dependencies from a gemspec you don't need to manually specify them in your Gemfile.

With RSpec, it's a good practice to specify the most recent minor version (the second number in the MAJOR.MINOR.PATCH versioning system) for your dependency. This way if RSpec updates to a new major version that breaks existing functionality, you can review any breakages and fix them with a new release of your gem rather than having unstable tests in the wild.

Now that you have RSpec in your project's bundle, you should run bundle exec rspec --init. This is a simple generator that will bootstrap your project's RSpec setup. The output should look something like this:

create   spec/spec_helper.rb create   .rspec 

The .rspec file is an options file that lets you specify the default command-line options (such as output format and color) that will be used by RSpec's runner in your project. If you've written tests before, you should be familiar with spec/spec_helper.rb as a central place to bootstrap your test suite and add any dependent library requires, etc.

Because we're writing a gem we will want to set up our spec_helper.rb file to work with our gem's structure. Alter the file to look like this:

$:.unshift File.dirname(__FILE__) + '/../lib' require 'my-gem'  RSpec.configure do |config|   config.treat_symbols_as_metadata_keys_with_true_values = true   config.run_all_when_everything_filtered = true   config.filter_run :focus end 

The first line adds our gem's lib directory to Ruby's load path. This means that when we say something like require 'some-file' Ruby will automatically look in our gem's lib directory to see if that file exists. The second line, require 'my-gem', is the same line that your end users will type when they want to start using your gem. This allows us to write tests from the perspective of someone who has installed the gem and required it, either manually or automatically through a tool like Bundler.

Now that we have our spec_helper all set up, let's go ahead and create our first spec. Create a file spec/my_gem_spec.rb (replace my_gem with your gem's name) and put this in it:

require 'spec_helper'  describe MyGem do   it 'requires additional testing' end 

Now simply run the rspec command in your project's root directory. You should see output that looks like this:

Run options: include {:focus=>true}  All examples were filtered out; ignoring {:focus=>true} *  Pending:   MyGem requires additional testing     # Not yet implemented     # ./spec/my_gem_spec.rb:4  Finished in 0.00015 seconds 1 example, 0 failures, 1 pending 

Congratulations! You've got your gem's test suite up and running.

Testing With Rake

Another standard practice amongst all Ruby projects (apps and gems alike) is to make it possible to run the tests for a project via a Rake task. This is remarkably easy; all you need to do is open up the Rakefile in your project's directory and add the following to the bottom of your Rakefile:

require 'rspec/core/rake_task' RSpec::Core::RakeTask.new task :default => :spec 

This does two things: first, it creates a new RSpec rake task that, by default, can be run by typing rake spec at your project's root. Second, we assign the :default task to be :spec. This means that in your project's root you can simply run rake and all of the tests for your project will run. This is a convention in the Ruby community and a good practice for all projects.

To verify everything we've done we can simply run rake -T which is a command to list the available Rake tasks. You should see something like this:

rake build    # Build my-gem-0.0.1.gem into the pkg directory rake install  # Build and install my-gem-0.0.1.gem into system gems rake release  # Create tag v0.0.1 and build and push my-gem-0.0.1.gem to Ru... rake spec     # Run RSpec code examples 

As you can see, we have four tasks available. Three of them are provided to us by Bundler for packaging and releasing our gem, and one of them is provided by RSpec for testing our gem.

Autotesting With Guard

Guard is a gem that allows you to observe your project's files for change and perform actions based on that change. The first thing you will need to do is add Guard (and its helpers for Bundler and RSpec) to the end of your Gemfile:

gem 'guard' gem 'guard-rspec' gem 'guard-bundler' 

Note that, differently from RSpec, I prefer to add Guard and its related gems to my project's Gemfile, not its gemspec. This choice comes down to personal preference, but while RSpec is required for someone to do development on my gem, Guard is really more of a nice to have and so I relax the dependency somewhat. The effect is the same for contributors: simply running bundle in the project's directory will install all of the dependencies from the gemspec and the Gemfile. Speaking of which, you should now run bundle in your project root to install Guard if it isn't already.

Now we need to initialize Guard and configure it for our project. Luckily, Guard comes with its own command line helpers:

guard init guard init bundler guard init rspec 

Guard works similarly to Bundler and Rake by creating a Guardfile in your project's root directory. These commands automatically add example configuration for each guard type to the Guardfile (after guard init creates the file to begin with). While I'm going to tell you explicitly what to put in your Guardfile, the init commands can really help jog your memory if you're trying to do it from scratch. Let's modify our Guardfile to look like this:

guard 'bundler' do   watch('Gemfile')   watch(/^.+.gemspec/) end  guard 'rspec', :version => 2 do   watch(%r{^spec/.+_spec.rb$})   watch(%r{^lib/(.+).rb$})     { |m| "spec/#{m[1]}_spec.rb" }   watch('spec/spec_helper.rb')  { "spec" } end 

Guard works by watching certain files in your project and then performing actions when those files change. The first guard tells us to watch for the Gemfile and the gemspec to change, and re-bundle the application when that happens. The second guard tells us to watch all of our RSpec test files, our spec_helper, and the corresponding library files for each of our RSpec tests. This means that when you change a file, Guard can automatically re-run the tests for that specific file only.

Now that your Guardfile is properly configured, you can just run bundle exec guard from your project's root directory. This will get Guard up and running and you will now automatically run tests and re-bundle as you build your gem. Running Guard makes the development feedback loop as tight and automatic as possible.

Continuous Integration

Continuous Integration is the process of automatically running the tests for a project when new code is pushed to the central repository. Luckily, a team of developers has built Travis CI that provides free continuous integration for open source projects of all types.

Why do you need continuous integration for an open source project?

  • Travis allows you to test your code across multiple versions of Ruby. Even if you don't personally develop on older versions of Ruby or alternative implementations such as JRuby or Rubinius, Travis can run your tests on these Rubies and notify you of any build failures.
  • Travis gives you the confidence to merge pull requests from the web. If someone submits a pull request you can merge it into your repository knowing that if it breaks your test build Travis will let you know. In the future, Travis will even support testing pull requests before you merge them.

In addition to the benefits provided by Travis is it exceptionally easy to implement. Here's how:

  1. Go to http://travis-ci.org and sign in with your GitHub account. Travis needs access to your GitHub info to be able to set up the automatic build process for each repository.
  2. Hover over your name once logged in and go to your profile. This lists out your open source repositories; from here you can simply "switch on" CI for each project.
  3. Set up a .travis.yml file in your project.

We haven't yet created a GitHub repository for our project, so steps 1 and 2 will actually come a bit later. However, we can set up the configuration file so that when we're ready to push to GitHub we will get continuous integration immediately. To set up Travis for your project you need to create one more configuration file in your repository. Make a file called .travis.yml (the leading dot is not a typo) and add this to it:

rvm:   - 1.9.3   - 1.9.2   - jruby   - rbx script: "bundle exec rake" 

As you can see, the configuration is very straightforward. The rvm key allows us to specify the different versions of Ruby we want to test against. The script key tells Travis what command to run for the build. Any command that exits with a non-zero status code (a Unix standard for a command that had errors) will automatically fail the build in Travis. In our case we're going to run bundle exec rake which, as explained above, has been aliased to running our RSpec tests via Rake.

Once you've added your Travis configuration file, it's time to create another commit:

git add . git commit -m "Adds RSpec, Rake testing, Guard, and Travis." 

All right! Now we've not only created a gem but we've added robust tools for testing that will give us and other contributors the power to develop quickly and in a test-driven manner. We've also set up continuous integration to let us test against many different versions of Ruby, even if we don't have them installed! We're well on our way to building a gem. Stay tuned for the next installment!

Continue to Part 4: Writing Library Code »

Categories
Author

Last week, I attended the Emerging Technologies for the Enterprise conference outside of Philadelphia.

I love the way these events provide an opportunity to learn. This time, the most interesting things I picked up weren’t new languages, techniques, plugins or gems — they were new perspectives on the work we’re doing.

Brian Marick

One particularly interesting insight came from software testing proponent Brian Marick, co-author of the Agile Manifesto.

His metaphor was that small changes in how we approach software can make a big difference in how we develop it, like “the difference between water at 33 degrees and 32 degrees [ice].”

I experienced that very effect this morning. Because I have been testing more, I want to write code that is easy to test. This required a minor refactoring (yes, I know some of you write your tests first), which made the code more loosely-coupled.

The result: same functionality and a better design — not because I made dramatic changes in my approach, but as a result of putting slightly more emphasis on testability.

Corey Haines

Literal “software journeyman” Corey Haines shared lessons from his extended pair-programming tour of the US.

At one stop, he interviewed OOP guru “Uncle” Bob Martin, who said:

In general, the more people you can work with, the more insights you gain that you simply can’t get on your own. Everyone thinks differently about problems and everyone has a different way to approach them.

In the Rails culture of “convention over configuration” and similar dogmas, it is easy to forget that there isn’t one right way. TDD may do wonders for you, but others might not excel with that approach. That does not mean they are “doing it wrong.” Some of us like TextMate; others vim or emacs; none is “best” for Rails development.

Another example is pair programming: traditionally, one person “drives.” Corey prefers an unusual setup with two keyboards and two mice — though only one person should type at a time :) — it may not be the way you would approach things, but it works for him and few people have pair-programmed with so many different folks.

Categories
Author

Over the past few days I've been diving into Selenium, an automated functional testing tool for web apps. It's pretty slick - in fact, like most of the technologies I've been using over the past two years, it does so much for you that the struggle always ends up being a matter of getting out of its way and letting it do its thing. So I'm gonna throw out some pointers on using Selenium within the Rails testing framework via the selenium_on_rails plugin; the only caveat I'll mention is that this post is especially geared towards the newbie wading into a project with pre-existing selenium tests. So I'm focusing on how you can get started with writing tests and integrating them into what's already there.

Open up firefox and install the Selenium IDE extension. This allows you to record your test by simply using the browser. The IDE keeps track of where you click - just make sure the record button is on. Go ahead and click through your test application and watch as the IDE records your actions.

Now you need to go into your selenium_on_rails plugin area and edit config.yml. In the browser config area, make sure it's pointing to your browser binary. For example, I kept most of the defaults, but made sure the following lines were there, uncommented:

environments:
- test
browsers:
firefox: '/Applications/Firefox.app/Contents/MacOS/firefox-bin'

Next, especially if this is a project with existing tests, make sure you've installed the right user extensions. These are prefabbed actions for doing things like logging in via javascript. In my project I found this file in:

vendor/plugins/selenium_on_rails/selenium_core/scripts/user_extensions.js

All you need to do is point the IDE at this file by opening the IDE window and navigating in the top menu to "Options > Options > General tab". Put the path to user_extensions.js in the input box entitled "Selenium Core extensions (user-extensions.js)".

OK, so let's take a look at the existing selenium tests. They're probably in test/selenium. If there's subdirectories under that, these are probably test "suites". For example, in the project I'm working on, they're organized depending on which type of user the test is geared towards: user or admin. The actual test scripts can be recorded in a variety of formats; mine are all HTML tables, where each row is a step in the process.

So let's get to recording tests. First, start your server in the test environment:

mongrel_rails start -e test

The whole premise of testing is verifying the response to an action. In Selenium, this is accomplished via assertion statements. The IDE makes this elementary: when you've reached a page on which you need to verify the output, just highlight the text and right click. At the bottom of the popup menu, several approaches to verifying the text are mentioned. I don't understand all the "selenese" yet but there's simple commands like "assertText" that ensure a particular passage is included on the page - that should get you started.

So you've recorded the steps necessary - now click "File > Export Test As" and save it in the correct test directory in the format you wish (such as HTML). Now you should be able to run this test via rake test:all. That's it!

Categories
Author
1
Subscribe to Testing