Rails – File, New Project…
Posted by John in Ruby and Rails on January 19, 2011
Even with RubyMine starting a new project is not simply File, New Project, like the rails new command, it can only take you so far.
It’s not complicated to set up a new Rails project but, as something you are likely to do farily infrequently, it is easy to skip or forget steps or commands. Charles Max Wood has posted a screencast of his Rails set up at what looks to be the start of a great screencast series.
Here is a terse run down of my setup:
rails new appname -J -T-J skips prototype files, -T skips testunit files.cd appnamervm --create --rvmrc 1.9.2@appnamervm rvmrc trustmvim .or mate . or whatever editor you use.- Amend the /Gemfile (Edit: I’ll update these files as I improve my process, add new gems to the default set and gems are updated/changed)
bundle installrails g rspec:installrails g cucumber:install --capybara --rspec --sporkspork --bootstraprails g jquery:install -uirake db:create- Amend /.gitignore
git flow initAccept the git-flow defaults. I like the structure provided by git-flow and will post on it in the future to explore it more.git add .git commit -m "Project skeleton"git remote add origin git@github.com:JohnPlummer/project_name.gitgit push -u origin developgit push -u origin master
Managing Ruby Gems
Posted by John in Ruby and Rails on December 29, 2010
Ruby gems are packages that add functionality to the core Ruby libraries. Each gem normally provides functionality in a specific area and can depend on other gems. Rails is a Ruby gem. Presumably, in the past, gem versioning caused problems for Ruby devs which has led to the development of a couple of tools:
RVM
RVM stands for Ruby Version Manager and allows a dev to switch betwen versions of Ruby. More importantly it can also sets of gems.
- Gemsets are associated with a particular version of Ruby.
- There is gemset named global (per Ruby version) and gems in this set are available to all other gemsets for that Ruby version.
- To associate a project with a particular gemset, include a .rvmrc file in the project root containing
rvm –create use version@project_name > /dev/null
This will create the gemset if it does not exist and switch to it automatically. - Useful commands to work with gemsets: rvm gemset [list|name|create|use|delete|empty|rename|copy]
Bundler
While RVM is used to manage and switch between installed gems, Bundler is a tool to install gems and associate them with a project.
- Bundler is itself a gem and is installed with Rails 3.
- Project dependencies are listed in a gemfile file in the project root.
- Gems versions can be specified in the gemfile but that is not really necessary with the gemfile.lock file which stores version numbers.
- bundle install ensures all gems listed in the gemfile are installed to the current gemset.
- Gems can be grouped by environment (development | test | production).
- Bundler ensures that the correct gems are ‘required’ by Ruby for the environment it is running under.
- The environment groups allow bundle install to be selective on what to install. e.g. for a production server
bundle install –without development test
RubyMine Gem Management
It looks as though RubyMine will be my environment of choice and it includes it’s own sdk (Ruby version) and gem management system that should integrate well with RVM’s gemsets and Bundler’s gemfile.
- A project can be associated with a particular version of Ruby and a gemset in RM’s settings pane.
- Once a gemset is selected, any gems installed by RM will be installed to this gemset.
- RM should also be able to read a .rvmrc file in the project root and switch automatically to the specified gemset. This currently (RM 3.0.1) seems not to work with newly created gemsets that have not yet had gems installed to them so best to check in the project settings to be sure.
- RM scans the gemfile, offering to install and attach and missing gems.
- Gems that are attached get scanned by RM to enable features such as autocomplete within the IDE.
Rails and Rake Commands and Rails Environments
Posted by John in Ruby and Rails on December 23, 2010
Rails
Rails seems to consist of two parts, a set of components to build an application on top of and a command line tool to help this process. The reason the rails command is so useful is the amount of convention in a Rails app: Certain files with certain names are expected in certain locations. When one file or class is created, the chances are that a number of further files and classes will be required. The most commonly used rails commands are:
rails new
The first command is rails new app_name. This can take a number of options to specify versions, templates, databases, or to skip certain parts of the application. View a full list with rails new –h. Rails new appname –t is possibly useful to skip creating the test folder if you intend to use rspec.
rails server
Runs the default rails dev server, WEBrick. which can be viewed on localhost:3000. Again use –h to view all the options. The –d option runs the server as a daemon and –u enables debugging. One option to be aware of is –e which allows you to specify an environment to run under, more on this later. rails s can be substituted for rails server.
rails generate
Creates boilerplate code for various parts of an application. Installing a gem (such as rspec) can add additional generators. The most common rails generators are probably scaffold, model, controller and migration. rails g can be used in place of rails generate.
- rails g controller controller_name action1 action2 … generates a controller with the specified actions and views to match the actions. e.g. rails generate controller Products buy sell will create app/controllers/products_controller.rb which will contain class ProductsController with methods buy and sell. It will also create the views app/views/products/buy.html.erb and app/views/products/sell.html.erb. The command will also create routes, test classes and helpers.
- rails g model model_name field1:type field2:type … creates a class for the model in app/models/model_name.rb and a database migration file to add a matching table to the database. It also creates a test class and a fixture file (to provide test data).
- rails g migration migration_name creates a migration file with the specified name (preceded by a timestamp). To add colums to an existing table rails g migration add_something_to_tablename field1:type field2:type can be used to create the correct migration automatically.
- rails g scaffold model_name field1:type field2:type … Combines all of the above, creating a model and migration, a controller with basic CRUD actions, matching views, tests, helpers etc. Although this would appear useful it is meant to be more of a learning tool (and useful for the various instant gratification demo’s of Rails).
rails console
Starts a console that can run ruby code. Like the rails server an environment can be specified to run under e.g. rails console test (note this is different to rails server).
Rake
Rake is a ruby command for ‘ruby make’ used for common administration tasks. The most common tasks are to administer databases and run tests.
rake test
Runs the applications tests, a number of options can be used to restrict the tests that are run.
rake db:command
There are a number of db commands but the most used is db:migrate. This command can take further options, one of which is used to specify the rails environment – rake db:migrate RAILS_ENV=test (and a third syntax to specify the environment.
Environments
After running rails new appname, three environments will be available, development, production, and test. The settings for the environments are specified in config/environments /*.rb and as seen above, various commands can specify the environment they run under. Further environments can be created if required e.g. staging.
The default environment for rails server, console and rake db:migrate is development. Tests run in the test environment. Running under other environments is normally done by specifying the environment in the server or hosting configuration.
The rails environment is useful in a number of other places such as:
Testing the environment from code -
<%= debug(params) if Rails.env.development? %>
Loading certain gems for specific environments with groups in the gemfile
source 'http://rubygems.org' gem 'rails', '3.0.3' gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3' gem 'gravatar_image_tag', '0.1.0' group :development do gem 'rspec-rails', '2.1.0' gem 'annotate-models', '1.0.4' end group :test do gem 'rspec', '2.1.0' gem 'webrat', '0.7.1' gem 'autotest', '4.3.2' gem 'autotest-fsevent', '0.2.2' gem 'autotest-growl', '0.2.8' gem 'autotest-rails-pure', '4.1.0' gem 'spork', '0.8.4' gem 'factory_girl_rails', '1.0' end
Learning Ruby: First Impressions
Posted by John in Ruby and Rails on December 17, 2010
Ruby (and Rails) seem to be designed to make the code look very like natural language. This makes the code very easy to read but less easy (for someone new to Ruby) to write. It seems like there is a lot you are required to know, although maybe a Ruby programmer looking at CSharp for the first time would think that there is a lot to know?
There are a lot of conventions in Ruby, the most obvious is that method names are all lower case with words separated by underscores. Combine this with other syntax such as brackets surrounding method parameters are optional, ‘unless’ being the inverse of ‘if’, and allowing the if (or unless) statement to be placed after the statement that is to run if it is one line and you get code that looks very close to natural language.
if(!user.Authorized)
RedirectTo(rootPath);
becomes
redirect_to root_path unless user.authorized?
The next things that strike you are the use of symbols and hashes, they seem to appear everywhere.
Symbols are similar to global constants in other languages, except they do not have to be declared and they aren’t assigned a value. A symbol is a colon followed by a name (:red, :monday, :ford etc). Ruby assigns a value to the symbol behind the scenes and this value will match wherever it is used in a program.
A common use of symbols is as keys for hashes. Hashes are declared as a number of key value pairs enclosed in braces. The key and value are separated by ‘=>’.
week = {
:sunday => 'weekend',
:monday => 'workday',
:tuesday => 'workday'
...
}
Hashes are often used to pass a variable number of parameters to a method:
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
Two things to note with this common pattern:
- If the hash is the last parameter in the method definition, it doesn’t require curly braces around it.
- The value of a hash item can be another hash (and this should have braces).
Code blocks are chunks of code surrounded by either braces or do … end and don’t appear too different from CSharp’s anonymous methods. A code block can be passed to a normally defined method and the yield keyword used to run the code block, passing parameters if needed.
This is only a first pass, but Ruby doesn’t look too far from CSharp. I haven’t mentioned the obvious difference that Ruby is dynamic but I doubt anyone would be ‘surprised’ by that.
Learning Ruby (on Rails)
Posted by John in Ruby and Rails on December 15, 2010
Most of my programming career has revolved around Microsoft technologies, starting with Visual Basic 3, up through the various versions to VB.Net, then switching to CSharp shortly after DotNet came out of beta.
I think I know the MS development stack fairly well and I like the direction they are moving in with the likes of MVC3 and Razor, Nuget etc. but it seems a fair amount of this ‘direction’ may be coming from the Rails community.
I have decided to give myself 2 weeks to ‘learn’ Rails, although I may get distracted by the Christmas holidays (and the fact that we will have an extra 6 kids and 3 adults staying with us for 2 weeks) and will record some of it here.
I have done a little research in preparation and bought a couple of ebooks from the prags:
They seem to be considered ‘definitive’ and cover the current versions of Rails (3.0.3) and Ruby (1.9.2).
The only other purchase I am likely to make is an IDE or editor. I code on a Mac, even when using Visual Studio so I have a few more options than most Windows users. The recommendation seems to be that, if you are on Windows, install a Linux VM for Ruby development. The favourite environment for the Mac looks to be Textmate although there are a few IDEs available such as JetBrain’s RubyMine and Aptana Studio, both of these are available for Windows, OS x, and Linux. I am a fan of JetBrain’s Resharper so will try RubyMine, the trial is 30 days so no need to make a purchase decision yet.
Other references that I expect to find useful over the next 2 weeks are:
BDD with SpecFlow, NUnit and MVC3–Refactored
Whilst the previous post (BDD with SpecFlow, NUnit and MVC3–More View a List Of Products) got the job done, it could have done it a lot better.
After a conversation on Twitter and in the comments on the previous post with Darren Cauthon (@darrencauthon, http://www.cauthon.com) and Marcus Hammarberg (@marcusoftnet, http://www.marcusoft.net), there are three areas where I can refactor to improve the tests:
1 Language
In an earlier post I was torn between ‘honest’ language and non-technical language:
Then comes the first scenario, ‘Display the default products view’, and where I run into naming trouble. I decided that I would start testing at the controller rather than the interface so, as much as I would like to write out the scenario as:
Scenario: Display the default products view
When I navigate to /product/
Then the products page should be displayed
And the page should contain a list of productsThat sounds misleading as I am not navigating to a URL or displaying a page. I’ll use a more honest description:
Scenario: Display the default products view
When the product controller is told to display the default view
Then the controller should return a view
And the view title should be products
And the view should contain a list of productsThis better describes what I want to do but is less easily understood by any non technical stakeholder (which is one of the benefits of BDD). I think it is probably better than the alternatives in my case which are either testing the GUI or a misleading scenario.
I have now swung to the non-technical viewpoint and rewritten my scenario to:
Scenario: Visit the product page
When I visit the product page
Then I should see the Index view
And the view title should be Products
And the view should contain a list of products
It’s then just a case of replacing the tags on the steps and, because I’m a neat freak, renaming the methods to match the tags.
2 State in Class Fields
Storing the controller and the result as private fields in the Step class is fine until you want to test a second controller. I added a scenario for testing a Tenant controller:
Scenario: Visit the tenant page
When I visit the tenant page
Then I should see the Index view
I added a new TenantControllerSteps file and wrote the ‘I visit the tenant page’ step, compiled and ran the tests and got a failure. The reason for this is that the test picked up the ‘Then I should see the Index view’ from the ProductControllerSteps which is great for reuse, not so great that the method used the _result field from the ProductControllerSteps which is of course null at that point. For a demonstration of this, check out Darren’s video: SpecFlow Anti-Pattern: Using Private Members to Retain State Between Steps. The answer is to store state in ScenarioContext.Current.
The refactored code is now in two class files, steps specific to ProductController
[Binding]
public class ProductControllerSteps {
[Given(@"There are (.*) products")]
public void GivenThereAreXProducts(int productCount) {
ProductController controller = CreateProductController(productCount);
ScenarioContext.Current.Set(controller);
}
[When(@"I visit the product page")]
public void VisitTheProductPage() {
var controller = ScenarioContext.Current.Get<ProductController>();
ActionResult result = controller.Index();
ScenarioContext.Current.Set(result);
}
[Then(@"the view should contain a list of products")]
public void ThenTheViewShouldContainAListOfProducts() {
var viewResult = (ViewResult) ScenarioContext.Current.Get<ActionResult>();
viewResult.ViewData.Model.ShouldBeType(typeof (List<Product>));
}
[Then(@"the view should contain a list of (.*) products")]
public void ThenTheViewShouldContainAListOfXProducts(int productCount) {
var viewResult = (ViewResult) ScenarioContext.Current.Get<ActionResult>();
var products = (List<Product>) viewResult.ViewData.Model;
products.Count.ShouldEqual(productCount);
}
private static ProductController CreateProductController(int productCount) {
var context = new FakeRavenContext();
context.AddProducts(productCount);
return new ProductController(new ProductRepository(context));
}
public ProductControllerSteps() {
ProductController controller = CreateProductController(0);
ScenarioContext.Current.Set(controller);
}
}
And steps that can be reused across different controllers
[Binding]
public class ControllerSteps {
[Then(@"I should see the (.*) view")]
public void ThenIShouldSeeTheView(string viewName) {
var result = ScenarioContext.Current.Get<ActionResult>();
result.ShouldBeType(typeof (ViewResult));
var viewResult = (ViewResult) ScenarioContext.Current.Get<ActionResult>();
viewResult.ViewName.ShouldEqual(viewName);
}
[Then(@"the view title should be (.*)")]
public void ThenTheViewTitleShouldBeProducts(string viewTitle) {
var viewResult = (ViewResult) ScenarioContext.Current.Get<ActionResult>();
viewResult.ViewData["Title"].ShouldEqual(viewTitle);
}
}
3 Use Tables to Specify Entities
In the code above, I am checking that the correct number of products are displayed, I am not testing they are the correct products though, this will become more important when I start adding and editing products. Darren has a library on Github to help with using tables in SpecFlow and there is a video on TekPub in which he explains it.
The refactored feature now looks like this:
Feature: View a list of products
In order to see what products there are
As a user
I want to view a list of products
Scenario: Visit the product page
When I visit the product page
Then I should see the Index view
And the view title should be Products
And the view should contain a list of products
Scenario: Display 10 products
Given There are these products
|Id |Active |Name |Reference |
|1 |true |Product 1 |Ref-1 |
|2 |true |Product 2 |Ref-2 |
|3 |true |Product 3 |Ref-3 |
|4 |true |Product 4 |Ref-4 |
|5 |true |Product 5 |Ref-5 |
|6 |false |Product 6 |Ref-6 |
|7 |false |Product 7 |Ref-7 |
|8 |false |Product 8 |Ref-8 |
|9 |false |Product 9 |Ref-9 |
|10 |false |Product 10 |Ref-10 |
When I visit the product page
Then the view should contain a list of these products
|Id |Active |Name |Reference |
|1 |true |Product 1 |Ref-1 |
|2 |true |Product 2 |Ref-2 |
|3 |true |Product 3 |Ref-3 |
|4 |true |Product 4 |Ref-4 |
|5 |true |Product 5 |Ref-5 |
|6 |false |Product 6 |Ref-6 |
|7 |false |Product 7 |Ref-7 |
|8 |false |Product 8 |Ref-8 |
|9 |false |Product 9 |Ref-9 |
|10 |false |Product 10 |Ref-10 |
Scenario: Display 0 products
Given There are these products
|Id |Active |Name |Reference |
When I visit the product page
Then the view should contain a list of these products
|Id |Active |Name |Reference |
And the two steps that have been amended are now:
[Given(@"There are these products")]
public void GivenThereAreTheseProducts(Table table) {
var products = table.CreateSet<Product>();
var context = new FakeRavenContext {Products = new FakeDbSet<Product>(products)};
var controller = new ProductController(new ProductRepository(context));
ScenarioContext.Current.Set(controller);
}
[Then(@"the view should contain a list of these products")]
public void ThenTheViewShouldContainAListOfTheseProducts(Table table) {
var viewResult = (ViewResult) ScenarioContext.Current.Get<ActionResult>();
var products = (List<Product>) viewResult.ViewData.Model;
table.CompareToSet(products);
}
I was expecting tables to be a pain but Darren’s Assist library really makes using tables easy in SpecFlow.
BDD with SpecFlow, NUnit and MVC3–More View a List Of Products
Following on from this post where it looked like BDD was a spectacularly inefficient way to drive out code, I’ll add a new feature to the scenario:
Scenario: Display 10 products
Given There are 10 products
When the product controller is told to display the default view
Then the view should contain a list of 10 products
Compiling and running the tests provides templates for the Given and the Then methods, we get to reuse the existing When method. This reuse is one of the big daws for BDD for me (plus the readability).
To write the method GivenThereAre10Products I need to tell the controller there are ten products. The simplest thing to do is to pass 10 as an argument to the controller constructor then write a loop to populate the product list. I need to move the controller out of WhenTheProductControllerIsToldToDisplayTheDefaultView and declare it as a class field, then re new it in GivenThereAre10Products:
private ProductController controller = new ProductController(0);
[Given(@"There are 10 products")]
public void GivenThereAre10Products() {
controller = new ProductController(10);
}
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
_result = controller.Index();
}
[Then(@"the view should contain a list of 10 products")]
public void ThenTheViewShouldContainAListOf10Products() {
var products = (List<Product>) ViewResult.ViewData.Model;
products.Count.ShouldEqual(10);
}
I can then write the code to make these tests compile then pass, the relevant changes to the controller are:
private readonly int _productsCount;
public ProductController(int productsCount) {
_productsCount = productsCount;
}
public ActionResult Index() {
ViewData["Title"] = "Products";
var products = new List<Product>();
var product = new Product();
for (int i = 0; i < _productsCount; i++) {
products.Add(product);
}
return View(products);
}
This makes the tests pass but needs refactoring. As is fairly standard practise I will refactor to pass a repository into the controller and the repository will be passed a context which will, in this instance fake the database connection. I will detail the repository and fake context in a separate post, but I include a method on the fake repository to add a number of an entity to help testing.
I need to refactor the specs to create a controller with a repository and the relevant refactored code is:
private ProductController _controller;
private ActionResult _result;
public ViewAListOfProducts() {
CreateController(0);
}
private void CreateController(int productCount) {
var context = new FakeRavenContext();
context.AddProducts(productCount);
_controller = new ProductController(new ProductRepository(context));
}
[Given(@"There are 10 products")]
public void GivenThereAre10Products() {
CreateController(10);
}
And the refactored code from the controller:
private readonly IProductRepository _repository;
public ProductController(IProductRepository repository) {
_repository = repository;
}
public ActionResult Index() {
ViewData["Title"] = "Products";
return View(_repository.All());
}
The final thing I want to do here is to add a scenario for 0 products:
Scenario: Display 0 products
Given There are 0 products
When the product controller is told to display the default view
Then the view should contain a list of 0 products
This is where SpecFlow begins to shine, I just need to parameterise 2 of the methods to implement this:
[Given(@"There are 10 products")]
public void GivenThereAre10Products() {
CreateController(10);
}
[Then(@"the view should contain a list of 10 products")]
public void ThenTheViewShouldContainAListOf10Products() {
var products = (List<Product>) ViewResult.ViewData.Model;
products.Count.ShouldEqual(10);
}
Becomes:
[Given(@"There are (.*) products")]
public void GivenThereAreXProducts(int productCount) {
CreateController(productCount);
}
[Then(@"the view should contain a list of (.*) products")]
public void ThenTheViewShouldContainAListOfXProducts(int productCount) {
var products = (List<Product>) ViewResult.ViewData.Model;
products.Count.ShouldEqual(productCount);
}
I still need to implement paging for this scenario then create the view details, add, edit, and delete product scenarios which should drive the design of the product class but I think the BDD side will be pretty much more of the same.
I expect coding with BDD will be at least as fast as TDD but I suspect I will still write some unit tests to complement the BDD specs.
BDD with SpecFlow, NUnit and MVC3–View a List Of Products
In the last post I set up an MVC project and Specs project and added various assembly references to the Specs project. In this post I’ll write my first feature which is to view a list of products.
The First Feature
Add a features folder to the Specs project then add a new item.
If you have installed SpecFlow correctly you should be able to add a SpecFlow feature. I will call mine ViewAListOfProducts.feature. I am not sure if this is the right level of granularity for a feature but ManageProducts sounds a bit too wide. I can always refactor later if this proves wrong. I am also unsure how to best organize my features in folders but will start with one folder for features and reorganise later if necessary.
Next I need to detail the feature, this is usually in the format:
Feature: feature
In order gain a benefit
As a user
I want feature
In my case it seems reasonable to do:
Feature: View a list of products
In order to see what products there are
As a user
I want to view a list of products
The First Scenario
Then comes the first scenario, ‘Display the default products view’, and where I run into naming trouble. I decided that I would start testing at the controller rather than the interface so, as much as I would like to write out the scenario as:
Scenario: Display the default products view
When I navigate to /product/
Then the products page should be displayed
And the page should contain a list of products
That sounds misleading as I am not navigating to a URL or displaying a page. I’ll use a more honest description:
Scenario: Display the default products view
When the product controller is told to display the default view
Then the controller should return a view
And the view title should be products
And the view should contain a list of products
This better describes what I want to do but is less easily understood by any non technical stakeholder (which is one of the benefits of BDD). I think it is probably better than the alternatives in my case which are either testing the GUI or a misleading scenario.
Compile and Run
Specflow creates a set of tests when the project is compiled which can then be run in the normal test runner. I use Resharper and the results are:
The tests are ignored but the output of the tests provides the code you need to write the steps file.
The Steps
Add a Steps directory to the Specs project and add a new item. again if Specs is installed correctly you should be able to add a SpecFlow Step Definition. I’ll call mine ViewAListOfProducts.cs.
Adding the code output from the test run gives results in this:
[Binding]
public class ViewAListOfProducts {
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
ScenarioContext.Current.Pending();
}
[Then(@"the controller should return a view")]
public void ThenTheControllerShouldReturnAView() {
ScenarioContext.Current.Pending();
}
[Then(@"the view title should be products")]
public void ThenTheViewTitleShouldBeProducts() {
ScenarioContext.Current.Pending();
}
[Then(@"the view should contain a list of products")]
public void ThenTheViewShouldContainAListOfProducts() {
ScenarioContext.Current.Pending();
}
}
I can then code WhenTheProductControllerIsToldToDisplayTheDefaultView to the simplest thing that works:
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
ProductController controller = new ProductController();
ActionResult result = controller.Index();
}
Of course now I need to create the controller which I will do with the MVC scaffolding. Although I don’t need it yet, I know I’ll eventually need the CRUD methods so will scaffold them all.
Running the tests after scaffolding shows the first method as done and the rest as pending. The second method, ThenTheControllerShouldReturnAView, is easy enough. Moving the result declaration out to the class allows:
[Then(@"the controller should return a view")]
public void ThenTheControllerShouldReturnAView() {
_result.ShouldBeType(typeof (ViewResult));
}
This is the first time I have used the Should framework and I really like the readability it gives the tests. Running the tests now shows the first two methods as done. I guess I could force a failure on the type test to get a red light first but that would seem like moving backwards to go forwards.
The remaining methods are as easy to complete and the final class looks like this.
[Binding]
public class ViewAListOfProducts {
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
var controller = new ProductController();
_result = controller.Index();
}
[Then(@"the controller should return a view")]
public void ThenTheControllerShouldReturnAView() {
_result.ShouldBeType(typeof (ViewResult));
}
[Then(@"the view title should be products")]
public void ThenTheViewTitleShouldBeProducts() {
ViewResult.ViewData["Title"].ShouldEqual("Products");
}
[Then(@"the view should contain a list of products")]
public void ThenTheViewShouldContainAListOfProducts() {
ViewResult.ViewData.Model.ShouldBeType(typeof (List<Product>));
}
private ActionResult _result;
private ViewResult ViewResult {
get { return (ViewResult) _result; }
}
}
Getting these tests to pass does not seem to drive out much code:
public ActionResult Index() {
ViewData["Title"] = "Products";
return View(new List<Product>());
}
Plus, of course an empty Product class. It is the simplest code to make the tests pass but at this stage it doesn’t seem the most efficient way to write code.
The next stage will be to expand our specs and code and hopefully it will drive the design down to the data layer and prove a bit more efficient.
BDD with SpecFlow, NUnit and MVC3–Getting Started
Up to now I have been using Test Driven Development (and occasionally just Unit Testing after the fact) but I want to start using Behaviour Driven Development as it seems a more natural way to flesh out an application.
When using TDD, code is tested one unit (class or method) at a time, any dependencies are mocked, faked, or stubbed out, so any errors are within the unit being tested. With BDD testing is done one feature at a time from the outside in, starting at or near the interface and progressing through further tests down toward the database.
With MVC there is a choice of starting with the interface using a framework such as Watin or just starting with the controller. I intend to start with the controller, if I need to test routing I will do so separately. At the other end I will test down as far as the repository, faking the database context.
Getting Set Up
I’m using Visual Studio 2010 so will take advantage of the Nuget package manager. I have also decided to use SpecFlow as my BDD framework of choice, I am sure other frameworks are equally as good. Whilst there is a package for SpecFlow, it doesn’t seem to add all it should at the moment (VS templates), so it is worth downloading and running the installer from http://specflow.org/.
Now create a new MVC3 razor project in Visual Studio (no test project), then create a second, class assembly, project in the solution named Specs. Right click the Specs project and select Add Library Package Reference. From the Nuget dialog install:
- SpecFlow
- NUnit
- Moq
- Should
- ShouldFluent
SpecFlow can use most of the testing frameworks, my preference is for NUnit. I have not used Should before but it looks a great way to write more readable (therefore more maintainable) tests.
In the next post I’ll start my first feature (view a list of products).