In preparation for my upcoming
Code Camp session on IronRuby I've been hacking around on the
IronRuby source, as well as on Ruby programs that can run on IronRuby. One of the core areas of interest for me concerning IronRuby is in writing specifications for my .NET applications using Ruby. While I've been learning Ruby (the real kind) I've become very fond of the testing libraries they have available, most notably
RSpec and
mocha.
Over the last two nights I've been writing some ruby code to test .NET classes written in C#. With a few of the hacks I have locally, I've had some pretty good success. The following set of specifications verify the behavior of an Account class I've written.
require "../../trunk/tests/ironruby/Util/simple_test.rb"
require "IronRubySamples"
include IronRubySamples
describe "Account" do
describe "When depositing money into my account" do
it "should increase my balance by the amount deposited" do
account = Account.new(10)
account.Deposit(10)
account.Balance.should == 20
end
end
describe "When withdrawing money from my account" do
it "should decrease my balance by the amount withdrawn" do
account = Account.new(100)
account.Withdraw(50)
account.Balance.should == 50
end
end
describe "When withdrawing money from an account with insufficient funds" do
it "should tell me I have insufficient funds" do
account = Account.new(30)
should_raise(InsufficentFundsException) { account.Withdraw(50) }
end
end
describe "When making a withdraw that drops my balance below the minimum" do
it "should reduce my account by the amount withdrawn + the low funds charge amount" do
account = Account.new(55)
account.Withdraw(50)
account.Balance.should == 4
end
end
end
The next step in my quest to test .NET code with IronRuby required me to figure out how to mock out dependent objects. I considered several different options. My first thought was to try and use "simple mock", however, I quickly realized
via Scott Bellware that while it worked well on IronRuby libraries it wouldn't fit my needs. My next thought was to give
Moq a try. After downloading Moq, I attempted to run a spec that referenced Moq and created a Mock<T> instance as shown below:
require "../../trunk/tests/ironruby/Util/simple_test.rb"
require "IronRubySamples"
require "Moq"
include Moq
include IronRubySamples
describe "LoginController" do
describe "When a user logs in" do
it "should vallidate credentials with login service" do
mock = Mock.of(ILoginService).new
#do expects
controller = LoginController.new(mock.Object)
controller.Login("steve", "****")
end
end
end
When running this via ir.exe I got an error that "Moq.Mock is not a generic type". After poking around a bit I discovered this was due to a
bug in IronRuby. Currently IronRuby has problems when there is a generic and non generic type of the same name within a referenced assembly. In order to work around this, I first tried to figure out what needed to be modified in IronRuby, however, that wasn't very fruitful so I decided to modify the source for Moq to get around my problem. After renaming the static Mock class in Moq to MockRetriever, and hacking around
another bug in IronRuby related to creating generic types where the type argument is an interface, I was finally able to get IronRuby to create the Mock<ILoginService> type:
mock = Mock.of(ILoginService).new
Unfortunately, this led to me to another roadblock. When setting up expectations in Moq you do so with lambda expressions such as:
// C#
var mock = new Mock<ILoginService>();
mock.Expect(s => s.Login("steve", "****"));
While IronRuby has blocks and lambda's it doesn't have a way to express the above (at least that I know). I'm going to dig around in the IronRuby source a bit to see if any ideas pop into my head, but at this point I'm not very hopeful.