Almost two years ago I blogged about Project
White, an open source project available on CodePlex which provides
an abstraction on top of the UI Automation library built into WPF and
Silverlight.
Fast forwarding and there is a new project available called WiPFlash which allows you
to drive WPF UI’s for testing purposes created by Liz Keogh who is an
agile\lean coach here in the UK. When I saw this framework, one of the first
advantages such stood out was how clean the API is to work with but also how
lightweight and focused the overal framework was.
This got me thinking… I’ve been presenting on how you can
test ASP.net web applications using Ruby, most recently at QCon London which
was an amazing experience. This got me thinking, if Ruby is a great language
for testing web applications then surely the same applies to desktop
applications. Thankfully, by using WiPFlash together with IronRuby you can have
the same advantage!
One of the questions I’m commonly asked is why would you use
Ruby when testing C# applications? For me, one of the major advantages of Ruby
is it’s readable natural language – this is perfect for tests. As Ruby is a
dynamic language you can also adapt your tests and frameworks to fit your test
domain more effectively. As a result, your tests become more readable and more
maintainable which means they stand much better chance of supporting the
development instead of hindering it. Given the amount of Ruby you need to learn
to get started, there is very little overhead for a project as to get started
you use a very focused subject and grow\learn, as such team members should be
able to pick-up everything required in a hour or so. When combined with
frameworks such as Cucumber you can create really powerful acceptance tests which
can cover large parts of your application with very little implementation.
WiPFlash – Start application and find control
After downloading IronRuby
(currently RC3) and WiPFlash,
you can use the REPL (ir.exe) to interact with your WPF application. For
example:
require ‘lib\WiPFlash.dll’
include WiPFlash
include WiPFlash::Components
a = ApplicationLauncher.new
app = a.launch ‘D:/SourceControl/wipflash/ExampleUIs/bin/Debug/ExampleUIs.exe’
The first line adds a reference to the assembly. We can now
start interacting with the objects. The second\third line includes the namespaces
so we can access the object without including the full namespace. Notice how we
use the Ruby syntax of :: for navigating the namespace – IronRuby will take
care of the translation for us.
The fourth line is where the action starts, we first create
the ApplicationLauncher object, this is part of WiPFlash.dll and is used to
launch the application which is what we do on the fifth line. We now have a
running WPF application along with a reference to the app in the variable. The
application is this example looks like this:
The next stage is to gain access the controls and start
interacting with them as a user might. First, obtain the window where the
controls are. This is very similar to how you would using C#.
window = app.find_window ‘petShopWindow’
The next stage is to obtain a reference to the actual
control. This is where C# and IronRuby
go in a slight different direction. The
C# API uses generics, with the syntax such as:
TextBox name = window.Find<TextBox>(“petNameInput”)
With IronRuby, we have a slightly different syntax, the most
important part is the of() which defines the generic type:
name =
window.method(:find).of(TextBox).call(“petNameInput”)
petNameInput refers to the WPF control name, which if you are
unsure of, you can use applications like Snoop
to find the name.
Once we have the control, we can set\get the text property.
name.Text = “Bob”
Fundamentally, we can now use the API in exactly the same way
we could via C# – but why is that interesting?
Extending WiPFlash via IronRuby
But before we get into the interesting Cucumber + WiPFlash,
we have a problem with the API and the generic syntax. Personally, I think it
is too wordy with the impact facts being lost. Thankfully Ruby is a dynamic
language, and we can extend the framework to meet our requirements resulting in
a syntax of:
text = window.find_TextBox “petNameInput”
Amazingly, this is easy to achieve thanks to the special
method_missing hook in Ruby. This method is called when a message is sent to an
object which doesn’t match a pre-defined method. This means you can to take
action and is how ActiveRecord allows for a flexible query syntax.
The code below makes the syntax above possible. GetType takes
in the method name we called, and returns the part relating to a WiP component.
This is converted to a class name within get_class before being used within
find_ui_element together with the args originally passed to the method. Simple,
but effective.
class WiPFlash::Components::Container
def get_type(name)
name.to_s.split(‘_’)[1]
end
def get_class(name)
Kernel.const_get(name)
end
def
find_ui_element(class_name, *args)
self.method(:find).of(class_name).call(args[0][0].to_sym)
end
def method_missing(m,
*args, &block)
ui_type =
get_type m
ui_class =
get_class ui_type
find_ui_element(ui_class,
args)
end
end
With our nice clean API allowing us to obtain controls via
window.find_TextBox(“petNameInput”) we can start testing.
WiPFlash + Cucumber
With this in place, we can now plug everything together and
use Cucumber together with IronRuby to test WPF applications. The complete
example can be found at http://github.com/BenHall/wipflash_cucumber_driven_example
To install Cucumber, run the command (ir being the IronRuby
interpreter):
ir -S gem install cucumber
For example, let’s say we wanted to test the following:
Scenario: Saving a product by name only
Given the application has started
And I enter the name “Product 1″
When I save the product
Then “Product 1″ should be available to purchase
Above is a cucumber scenario. Using the Given, When, Then
(GWT) syntax we define the ‘given’ steps to setup our test context and data,
‘when’ to perform a particular action and ‘then’ to verify it worked
successfully. The most important aspect is the language used. When defining
your scenario, you should use business oriented language to describe the
behaviour of the application instead of
the actual implementation or UI. There are two (main) reasons for this,
one is that you can use the scenarios as a communication base with customers
and the business to form acceptance criteria about how the application should
work. The second reason is that if your application’s implementation changes
you only need to change the implementation of the test instead of the wording
and aim. This will save you a huge amount of effort during the project.
The implementation of the test is as follows. Each cucumber
step relates to a ruby code block:
Given /^the application has started$/ do
host = ApplicationLauncher.new
@app = host.launch Dir.pwd + ‘/src/ExampleUIs/bin/Debug/ExampleUIs.exe’
@main_window = @app.find_window ‘petShopWindow’
end
Given /^I enter the name “([^\”]*)”$/ do |product_name|
textbox = @main_window.find_TextBox “petNameInput”
textbox.text = product_name
end
When /^I save the product$/ do
button = @main_window.find_Button “petSaveButton”
button.click
sleep(1)
end
Then /^”([^\”]*)” should be available to purchase$/ do |product_name|
combo = @main_window.find_ComboBox “basketInput”
combo.Items.should include(“Pet[#{product_name}]”)
end
After do |scenario|
@app.process.kill
end
The main different to what we discussed previous is the
‘After’ step which is a hook within Cucumber called after the scenario has
finished. This means we can kill the application launched in the first step and
ensure we have a clean system with no left over processes.
To execute the tests you would run the command:
ir -S cucumber features
We can now test our WPF applications using Cucumber, taking
advantage of the flexible nature and
re-useable steps together with an appropriate language for driving
acceptance tests.