For a very long time, .NET developers have envied the simplicity and the beauty of the Ruby language. The dynamic behavior, duck typing and compact code are some of the main features of the Ruby language. Now, .NET developers can enjoy the same benefits using the IronRuby framework. This article explores the possibilities of using IronRuby in the CLR world. The main focus will revolve around the sphere of unit testing CLR assemblies using the IronRuby framework.
IronRuby allows you to use the simplicity of the Ruby language and the power of the .NET Framework to write concise and simple tests.
IronRuby is the .NET implementation of the Ruby language. IronRuby uses the dynamic language runtime (DLR) framework to execute. The IronRuby team went to great lengths to make sure that the Ruby specifications line up properly with the Iron Ruby specifications. IronRuby brings the best of both worlds to.NET developers. They can enjoy the simplicity of the Ruby language and the richness of the .NET API. This article explores IronRuby through the lens of unit testing. This includes the built-in testing framework, RSpec framework, WatiN and Cucumber framework.
Interacting IronRuby with CLR Assembly
One of the advantages of using IronRuby is that it can leverage the rich APIs provided by the .NET Framework. One common scenario where this will be applicable is when a particular API is only available in the form of .NET DLL. IronRuby can easily load the library and use the features provided by the API. Figure 1 shows how IronRuby is used to invoke the Windows Forms API and display a form.
The keyword “require” loads the specified assembly and the keyword “include” is used to include the namespace. Finally, the form.show_dialog is called which displays the form to the user. Apart from loading and calling methods of the .NET API you are free to load your custom library and use it with IronRuby. The next section explains how to perform unit testing of CLR assembly by using IronRuby.
Invoking IronRuby from a .NET Application
A .NET application can easily invoke IronRuby code. This method can be utilized in scenarios where an already implemented IronRuby API needs to be invoked from within the CLR application. The CLR application requires a reference to the following libraries.
IronRuby.dll
Microsoft.Dynamic
Microsoft.Scripting
After you’ve established the references you could use the following code to invoke the DLR framework from the CLR framework.
static void Main(string[] args)
{
var runtime = IronRuby.Ruby.CreateRuntime();
var engine = runtime.GetEngine("rb");
engine.Execute("puts 'Hello World from Ruby
code'");
}
The above code initializes the IronRuby runtime environment which is used to return the IronRuby engine. Your code can use the engine to execute Ruby statements. The above code then runs and prints “Hello World” on the console. The “Hello World” text is displayed by the IronRuby “puts” function which is executed using the IronRuby engine.
You can also interact with the classes defined in the IronRuby file from the CLR application. A simple account class is implemented below:
class Account
def deposit(amount)
puts amount
end
end
You can invoke the above class from the CLR environment as shown in the snippet below:
static void Main(string[] args)
{
var runtime = IronRuby.Ruby.CreateRuntime();
var engine = runtime.GetEngine("rb");
engine.ExecuteFile("../../account.rb");
var accountClass =
engine.Runtime.Globals.GetVariable("Account");
var account =
engine.Operations.CreateInstance(accountClass);
engine.Operations.InvokeMember(account,
"deposit", 100);
}
If you are using .NET 4.0 then you can leverage the use of the new “dynamic” keyword as shown below:
static void Main(string[] args)
{
var runtime = IronRuby.Ruby.CreateRuntime();
var engine = runtime.GetEngine("rb");
engine.ExecuteFile("../../account.rb");
var accountClass =
engine.Runtime.Globals.GetVariable("Account");
dynamic account =
engine.Operations.CreateInstance(accountClass);
account.deposit(100);
}
The new dynamic keyword transforms the IronRuby account instance auto magically into an object compatible in the CLR world. Your code can use the dynamic object in the CLR environment just like any other CLR object. Because of the dynamic behavior of the object, Visual Studio does not provide IntelliSense support for it.
Testing CLR Assembly Using IronRuby
Dynamic languages are more mature in unit testing than the static languages. Dynamic behavior makes it mandatory to perform unit testing. A statically typed language can catch an error at compile time but since code written in a dynamic language is not compiled, it must be covered by the unit tests. Another reason for the maturity of dynamic languages is the availability of unit testing tools. The Ruby language has unit testing built into the framework. The RSpec framework allows developers to write behavior-driven design style unit tests. WatiR and WebRat allow developers to perform automated user interface testing for web applications. Cucumber allows developers to write BDD style integration tests using plain English language. Most of these have been ported to the CLR world but the simplicity of the Ruby language created a vacuum which is now filled by the IronRuby framework.
The scenario in this article revolves around the bank account domain. Customer should be able to deposit, withdraw and transfer balance from one account to another. The following code sets up the CLR assembly to be tested.
require 'test/unit'
require File.dirname(__FILE__) +
'/bin/Debug/BusinessObjects.dll'
include BusinessObjects
include Test::Unit
class Testing_Account_Deposit < TestCase
def test_account_deposit
end
end
The CLR assembly “BusinessObjects.dll” is loaded by the IronRuby framework using the require keyword. The assembly is picked up from the Debug folder but it can reside anywhere on the system. The class “Testing_Account_Deposit” inherits from the TestCase class which makes it a test fixture. The test fixture will be run by the IronRuby interpreter from the command line. The test method is called “test_account_deposit” which is responsible for testing the deposit functionality of the Account class. The test method is prefixed with the word “test” which is mandatory. Currently the method does not contain any code but can be run from the command line. Figure 2 shows how a unit test written in IronRuby can be executed from the command line.
The test does not result in a success or failure since there is no assert statements to fail or pass the test. Let me add some code to the test.
class Test_Account_Deposit < TestCase
def setup
@account = Account.new
end
def test_account_deposit
@account.deposit(100)
assert_equal(100, @account.get_balance)
end
end
A new account instance is created inside the setup method. The setup is fired once at the start of each test. Inside the test_account_deposit unit test the “deposit” method is invoked with 100 as the input parameter. Finally, an assert statement makes sure that the amount 100 is deposited correctly in the account. When the test is run it fails since the class Account does not exist. Now I’ll add the following Account class in the project.
public class Account
{
}
Rerunning the unit test will again result in failure because the Deposit and GetBalance method are not defined in the Account class. After following the principles of red green and refactor, the Account class is implemented as follows:
public class Account
{
private double _balance;
public void Deposit(double amount)
{
_balance += amount;
}
public double GetBalance()
{
return _balance;
}
}
Run the test and now the test will finally pass successfully.
One advantage of using TestUnit class to perform unit testing is that it is built directly into the IronRuby framework. The TestUnit class provides basic functionality for unit testing but if you want more meaningful ways to unit test then RSpec is the gem to use.
Testing CLR Assembly Using RSpec
RSpec is a specification framework introduced in the Ruby language. RSpec allows writing behavior-driven test that are more explanatory than the regular unit tests. RSpec framework can be installed as a gem.
igem install rspec
This installs the RSpec framework on the local machine. The same unit test as before is implemented using the RSpec framework as shown below:
describe Account, "when amount 100 is deposit" do
before do
@account = Account.new
end
it "should deposit 100 successfully" do
@account.deposit(100)
@account.get_balance().should == 100
end
end
The keyword “describe” is used to specify a particular specification. The specification acts on the Account class. A string message is used to specify the specification text. The keyword “before” acts like a “setup” method and is executed before running each specification. The “it” keyword specifies the specification to run. As you may notice, the specification is written in plain English which improves the readability of the unit test. Instead of using asserts in a unit test the “should” method is used to check the validity of the specification.
You can run the specifications from the command line using the following code:
ir -S spec testaccountspec.rb
The RSpec framework help can be displayed by running “spec” from the command line.
You can use one of the arguments called format to format the test result on the command line screen as shown in Figure 6.
The next specification demonstrates how to throw an exception when an invalid input is passed to the function.
it "should throw exception when negative number is
passed" do
lambda { @account.deposit(-100) }.should
raise_error
end
The deposit method is wrapped into a lambda expression which compares the result of the expression to a raise_error. The raise_error makes sure that an error is thrown. There are several parameters to the raise_error method which allows it to validate against different error types.
The above section shows the basic use of RSpec framework. The RSpec framework also allows shared groups, which enables the specifications to be shared between each other. For more details on using the RSpec framework, please visit the official website of RSpec using the link below:
Performing Automated User Interface Testing Using IronRuby and WatiR
User interface testing is a time-consuming operation when performed manually. The developer must correctly mimic all the real-world scenarios through the interface and acknowledges the correct result. This process, when performed manually, can eat away hundreds of hours. The better approach is to automate the user interface testing. Ruby uses WatiR (Web Application Testing in Ruby) to automate the browser. Unfortunately, IronRuby does not support WatiR since it is implemented in the C language. Fortunately, the .NET implementation of WatiR, called WatiN (Web Application Testing in .NET), can be used by the IronRuby framework to automate the user interface testing.
Download the latest version of the WatiN framework using the following URL:
Add the following WatiN assemblies to the Debug folder of your application. You can choose any other folder if you desire. The following code loads the assemblies and includes the namespaces associated with the assemblies.
$:.unshift( File.dirname(__FILE__) + "/bin/Debug")
require 'BusinessObjects.dll'
require 'Interop.SHDocVw.dll'
require 'WatiN.Core.dll'
include BusinessObjects
include WatiN
You can write the unit test using the built-in testing framework available in the IronRuby framework or you can use a separate gem like RSpec. Since I covered RSpec in the last section, I will use that to run the automated tests.
As explained earlier, you invoke before prior to running the specification. I’ve defined the before method in this code snippet:
before do
IE = WatiN::Core::IE
@ie = IE.new()
end
The before method initializes the IE instance. The IE instance is part of the WatiN assembly and is used to launch the Internet Explorer browser. The IE constructor takes in the URL to load but when invoked using IronRuby, uses the more generic constructor, which takes object as the parameter. This bad design of the IE class constructor results in an error on the IronRuby side as shown in Figure 7.
Instead of relying on the constructor parameter we will utilize the Goto method of the WatiN IE class instance. The Goto method sends requests to the specified URL and downloads the content of the web page. The Goto method is used inside the specification which checks that the amount is deposited successfully.
it "should deposit the amount successfully" do
@ie.go_to('http://localhost:1055/Default.aspx')
@ie.text_field("txtAmount").type_text("100")
@ie.button("btn_Deposit").click();
# use a persisting storage
DataAccess.get_balance().should == 100
end
The specification above makes sure that the amount deposited is correct. The go_to method loads the page which is under test. The text field “txtAmount” is populated with an amount of 100 and the button “btn_Deposit” is clicked. All of this is performed automatically without the user manually interacting with the web application. This greatly reduces the amount of work and proves to be a time saver in much more complicated user interface environments. Automated tests are usually not part of every build but are run in a separated build which can be scheduled at a later time of the day. The next section introduces Cucumber which you can use to perform integrated testing using a behavior-driven development approach.
Testing Using Cucumber
Cucumber is a very interesting tool in the Ruby world. Cucumber allows you to write behavior-driven integration tests using plain English language. The test is comprised of two files: the feature file and the Ruby file. The feature file is a simple text file with “.feature” extension. It contains the current feature in plain English that the developer is testing. In this next example (Listing 1) I’ll show you how to implement a feature to transfer funds between two bank accounts.
The feature simply represents the story being tested, which in this case is the account balance transfer service. The feature file can contain regex expressions which filters the variables in the file. Cucumber uses the Gherkin language to process the feature document. The keywords Feature, Scenario, Scenario Outline, Given, When, Then are all part of the Gherkin language. The most common Gherkin keywords are explained in Table 1.
Try following the URL below which explains other keywords for the Gherkin language:
http://wiki.github.com/aslakhellesoy/cucumber/gherkin
The feature file is placed under a folder called features. The Cucumber framework recognizes the features folder and executes all the integration tests contained in that folder. The actual test is called step definition and is contained inside the step_definations folder under the features folder. The step defination is a Ruby file which is used to satisfy the conditions in the feature file. Listing 2 shows the complete implementation of the step file:
** NOTE: ** The latest version of Cucumber 0.8.2 has some problems with the Gherkin language. The Cucumber 0.6.3 is stable and can be used to run Cucumber tests. You must explicitly specify the Cucumber version when installing using RubyGems.
igem install cucumber -v 0.6.3 (DLR)
gem install cucumber -v 0.6.3 (MRI)
The step file uses the same text from the feature file and adds Ruby code to it. The Ruby code is responsible for adding the logic to pass or fail the test. The cucumber can be run using the following statement from the command line.
ir -S cucumber
You can see the output of the test in Figure 7.
Cucumber provides the functionality to express complicated scenarios in plain language and then to invoke actions on the language and run the tests.
Conclusion
IronRuby allows developers to use the simplicity of the Ruby language and the power and richness of the .NET Framework. The mature tools provided by the Ruby language can now be used in .NET applications through IronRuby.
The journey of IronRuby has just started as the next step is to integrate it into the Visual Studio tools.
Listing 1: Cucumber feature file for balance transfer
Feature: Balance Transfer
Scenario Outline: Transfer amount from one bank account to another
bank account
Given I have wamu account with <wamu> dollars and boa account with
<boa> dollars
When I transfer the <amount> from wamu to boa
Then the balance in wamu is <wamu_balance> and in boa is
<boa_balance>
Examples:
| amount | wamu | boa | wamu_balance | boa_balance |
|100 | 100 | 100 | 0 | 200 |
|10 | 100 | 10 | 90 | 20 |
|20 | 30 | 20 | 10 | 40 |
Listing 2: Cucumber step file
Before do
@account_service = AccountService.new
@wamu = Account.new
@boa = Account.new
end
Given "I have wamu account with $wamu dollars and
boa account with $boa dollars" do |wamu,boa|
@wamu.deposit(wamu)
@boa.deposit(boa)
end
When "I transfer the $amount from wamu to boa" do
|amount|
@amount_to_transfer = amount
@account_service.transfer(@amount_to_transfer,
@wamu, @boa)
end
Then "the balance in wamu is $wamu_balance and in
boa is $boa_balance" do |wamu_balance,
boa_balance|
@wamu.get_balance.should == wamu_balance.to_i
@boa.get_balance.should == boa_balance.to_i
end
Keyword | Definition |
---|---|
Feature | The name of the feature under test. |
Scenario | The summary of the scenario under test. |
Given | The known variables of the scenario. |
When | The event that will invoke the functionality under test. |
Then | The expected result of the feature. |