This article is based on a prerelease version of IronRuby. All information is subject to change.
This article discusses: § Ruby and Duck Typing § Ruby and the Microsoft .NET Framework § Using IronRuby and RSpec |
This article uses the following technologies: |
Code download available from the
MSDN Code Gallery
Browse the Code Online
Contents
Defining Requirements and Examples in Code
Ruby and Duck Typing
Ruby and MetaProgramming
Ruby and the .NET Framework
Ruby and the CLR
Inside IronRuby
Testing a C# App with IronRuby
Moving Forward
"That's not what we asked for!" I'm sure most developers have heard this cry from a customer shortly after delivering the latest build. The customer
could be yelling at the poor developer for various reasons—maybe the requirements were not correctly understood or part of the system simply did not work.
Because customers are often unclear as to their own requirements, they will opt for the safety of the 100-page technical document that tries to define everything the system might have to do. Meanwhile,
the developer struggles with undocumented legacy code, trying to understand how the app is intended to work while attempting to implement new requirements without breaking anything.
Times are changing. New approaches to software development are aimed at solving these problems, helping you to meet the customer requirements at the first attempt without causing failures in the process.
These approaches are taking advantage of languages such as Ruby, allowing you to create easily readable and maintainable code with much shorter development iterations.
In this article, I will introduce you to Ruby and IronRuby and demonstrate some basics of Ruby interoperating with Microsoft .NET Framework-based code. I also explain how frameworks such as RSpec can be
used to generate examples of how objects are intended to behave, providing both documentation and verification that the system is built correctly. This will set the stage for a future article in which I will explain acceptance testing in detail and demonstrate
creating acceptance tests with IronRuby.
Defining Requirements and Examples in Code
To help write examples and define requirements you need a framework. There are many different approaches to writing automated acceptance tests or executable specifications. Some people use standard xUnit
test frameworks successfully, while others use Fit and Fitness frameworks. I have found the best approach is to use Behavior-Driven Development (BDD). Dan North devised a
BDD framework called JBehave
as a way to define scenarios that describe the application's behavior in a way that can be communicated to the whole team, regardless of technical ability.
JBehave was the result of problems North faced with Test-Driven Development (TDD) and was implemented for the Java language. Later, North created RBehave, which has since been integrated into RSpec, a
popular framework for Ruby. RSpec accommodates two different approaches to BDD: Dan North's approach, based on stories and scenarios to describe application's behavior, and
Dave Astels' approach
, which is more focused on creating examples at an object level.
C# does have some BDD frameworks, such as NSpec and NBehave. The main problem with writing tests in C# is that the true intent of the test is often hidden due to fact that you have extra structural elements
and metadata such as braces, public and private. Overall, I don't think what is on offer via C# and NSpec/NBehave can match what is available via IronRuby and RSpec. Previously, this would have been a major problem for C# developers, as you could not have
used the Ruby-based RSpec to test C# apps. With IronRuby, this is no longer a problem.
While still early in development, IronRuby is taking advantage of the Dynamic Language Runtime (DLR) to create an implementation of the Ruby language on top of the CLR. With IronRuby, you can employ existing
Ruby apps and frameworks together with the .NET Framework and .NET-compliant languages. The result is that you can use the Ruby language and RSpec to test C# applications!
As a language, Ruby is concise, allowing you to write less code and express it in a much more natural manner, making your code easier to maintain. For example, to read all the lines of text from a file
and write them out to the console, you would write this:
File.readlines('AboutMicrosoft.txt').map {|line| puts line}
The code opens the file AboutMicrosoft.txt and reads all the lines, passing each line into the block, with the line as a parameter that is then written to the console. In Ruby, a block is the collection
of statements between the braces and is similar to invoking a method in C#, which uses the yield statement to return control to the calling method.
Its natural language approach is one of the reasons why Ruby is excellent to use when testing. The tests, or scenarios in this case, are much more readable.
Ruby and Duck Typing
One of the reasons why Ruby and dynamic languages are easier to read is due to how they handle typing. Instead of the developer defining a type, when the Ruby code is interrupted, the type of the variable
is inferred. The language is still strongly typed, but it determines the variable's type dynamically.
With C#, interfaces allow for decoupling of objects while still defining a contract for the implementation. With Ruby, there is no need for contracts and interfaces. Instead, if the object has an implementation
of the method you are calling, then it's called. If it doesn't, then an error is returned. As such, the contract for the object is defined by its implementation, not its relationship to other parts of the code.
To demonstrate this, I created a method that accepts a value, the type of which will be inferred at run time. It will then output Hello plus the value:
def SayHello(val)
puts "Hello #{val}"
end
Because of the way types are handled, you can reuse the same method for both strings and integers without changing any of the code:
SayHello("Test") => "Hello Test"
SayHello(5) => "Hello 5"
I could have also have called the method without brackets, as these are optional in Ruby and can make the syntax much more readable:
SayHello "Test" => "Hello Test"
This can be achieved in C# using object, but let's look at how this works with more complex objects. I defined a new method to output the result of a call to GetName. As long as the value of the parameter
n implements a method called GetName, the code will work:
def outputName(n)
puts n.GetName()
end
Here are two unrelated classes:
class Person
attr_accessor :name
def GetName()
@name
end
end
class Product
def GetName()
"The product with no name"
end
end
Person has an accessor allowing the name variable to be set, while Product returns a hardcoded value. In Ruby, there is no need to write the return statement, the last result of the last line of code of
a method is returned by default.
If you call the method outputName, the GetName method is called, demonstrating how Ruby's typing can enhance code reusability since we are not fixed to a single type:
outputName(Product.new) => "The product with no name"
$x = Person.new
$x.name = "Ben Hall"
outputName($x) => "Ben Hall"
If you call the method using an argument that doesn't implement GetName, then a NoMethodError is raised:
outputName("MyName") => :1:in 'outputName': \
undefined method 'GetName' \
for MyName:String (NoMethodError)
While this concept concerns some people, it can be useful; it allows for improved testability and application design, especially when testing C# applications, which I will discuss in the next section.
Ruby and MetaProgramming
Similar to other dynamic languages, Ruby embraces the concept of meta-programming. This allows you to extend any class or object at run time, enabling you to define methods and behavior at run time, giving
you the ability to develop a self-modifying application. This is extremely useful in dynamic languages, especially in unit testing, as it allows you to customize the behavior of objects and methods to your own requirements.
In a similar fashion, you can extend the built-in Ruby classes to provide your own functionally, much like the extension methods in C# 3.0. With C#, there are various restrictions about the methods you
can create. For example, they have to be static and in a separate class. This all harms readability. With Ruby, you just create a normal method on an existing class. For example, Integer is a class to handle whole numbers. Integer has a series of methods,
but it does not have a method to say whether the number is even.
To create such a method, you simply create a new class with the same name (Integer) and define the new method. The question mark at the end of the method denotes that a Boolean is returned with the aim
of making it easier to read:
class Integer
def even?()
self.abs % 2 == 0
end
end
puts 2.even? => true
puts 1.even? => false
Ruby and the .NET Framework
In place of the old debate about whether to use a static or dynamic language, IronRuby lets you use the right language for the job. If it makes more sense to use C#, then you can use C#. If you need a
more dynamic approach, you can easily use IronRuby, taking advantage of its interoperability with .NET and the dynamic nature of the Ruby language. I anticipate people using a mixture of different languages and technologies, such as C# for the main application
and Ruby for testing.
The DLR provides the foundation to enable .NET interop. With this in place, you can take advantage of UI technology such as Windows Forms, Windows Presentation Foundation (WPF), and Silverlight while writing
application code in Ruby.
Taking advantage of WPF from IronRuby is easy. When the following code is executed, you can have a fully functioning WPF window created from IronRuby. You still must reference mscorlib and the two WPF
assemblies shipped with the .NET Framework 3.0, as you did with C#, using the require statements:
require 'mscorlib'
require 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
require 'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
You can now create and interact with the WPF objects. First, create aliases for all the objects you want to use. This makes the code cleaner, as you won't need to access the object via its namespace:
Window = System::Windows::Window
Application = System::Windows::Application
Button = System::Windows::Controls::Button
Next, create a WPF window and give it a title:
win = Window.new
win.title = 'IronRuby and WPF Interop'
Create a button in the same fashion:
mainButton = Button.new
mainButton.content = 'I'm a WPF button — press me'
In both instances I'm using the alias to access the full name of the object. When the button is clicked, I want to display a MessageBox to the user. As you would in C#, you can subscribe to the event and
provide a block that is called when the click event is invoked:
mainButton.click do |sender, args|
System::Windows::MessageBox.Show("Created using IronRuby!")
end
Finally, set the content of the window to be the button, create a new Application object, and launch the window:
win.content = mainButton
my_app = Application.new
my_app.run win
Figure 1 Using IronRuby to Create a WPF App
At this point, a fully interactive WPF window will be displayed with the click event correctly wired up as shown in
Figure 1. I interacted with the .NET Framework in the same way I would interact with Ruby libraries. One of the core principals set out by John Lam and team was to stay true to the Ruby language. This is an important principal for adoption
by current Ruby developers, who won't want to change the way they create Ruby applications just because they're moving to IronRuby. Instead they can access all the rich .NET code in the same way.
Ruby and the CLR
Here is another example. If you attempt to access AddRange on IEnumerable, in C# the code would look like this:
ArrayList list = new ArrayList();
list.AddRange(new [] { 1,2,3,4 });
However, with Ruby, the accepted convention is for words within method names to be separated by underscores to improve readability. Creating a separate library to follow this convention would be too time-consuming
and error-prone, and it would not support additional third-party development.
Instead, for CLR objects, IronRuby translates Ruby method calls into the equivalent method name for the CLR:
$list = ArrayList.new
$list.add_range([1,2,3,4])
Here I am accessing the AddRange method following Ruby's approach, which is lower case with words separated by an underscore. If you prefer, you can keep with the CLR naming convention as the method name
still exists from Ruby:
$list.AddRange([1,2,3,4])
Both work the same; it's just a personal preference which to use.
As I mentioned, Ruby infers the type of objects at run time, and this is the same when handling C# objects. Consider a series of C# methods that return different object types, while the same object is
being returned, the method signature returns either the interface or the concrete type. Within my example, I have an interface that defines a single HelloWorld method:
public interface IHello {
string HelloWorld();
}
I created a Hello4Times class that inherits from Hello3Times, which inherits from the interface but has implemented three extra HelloWorld methods. Within the class I define a new method called HelloMethodOn4Times
that calls the base implementation:
public class Hello4Times : Hello3Times {
public string HelloMethodOn4Times () {
return base.HelloWorld();
}
}
I then defined a static class and methods that will return a new instance of the Hello4Times class but return it to the calling code as an interface. This means the calling code should only know about
HelloWorld, not any of the additional methods defined:
public static class HelloWorld {
public static IHello ReturnHello4TimesAsInterface() {
return new Hello4Times();
}
In my Ruby code, I have two method calls. The first call is on the interface-defined method, which you would expect to work without a problem. However, the second call is to the method on the concrete
class. IronRuby has determined the type of the object being returned and can dispatch the method calls:
puts InteropSample::HelloWorld.ReturnHello4TimesAsInterface.HelloWorld
puts interopSample::HelloWorld.ReturnHello4TimesAsInterface.HelloMethodOn4Times
All of this happens without you ever needing to worry about casting the object to the correct type to call the method.
Following this theme, you can extend .NET objects in exactly the same fashion as you can with Ruby objects. When displaying a MessageBox, I don't want to keep defining which icons and buttons to use. Instead,
I just want to provide a message.
Maybe you don't like the built-in functionality and want to extend the actual MessageBox class for seamless interaction. With Ruby, this is simple. You define a new class that is the same as the built
in MessageBox within WPF. You then create a new method on the class that simply calls the show method with various defaults:
class System::Windows::MessageBox
def self.ShowMessage(msg)
System::Windows::MessageBox.Show(msg, msg, \
System::Windows::MessageBoxButton.OK, \
System::Windows::MessageBoxImage.Stop)
end
end
After executing this block of code
System::Windows::MessageBox.ShowMessage( \
"I'm going to show you a message")
you can call the ShowMessage method, the result being the message box shown in Figure 2 .
Figure 2 Displaying a Custom MessageBox from Ruby
Inside IronRuby
How is interop possible? The answer is the DLR. At a high level, when you execute Ruby code using IronRuby, a lot of work takes place behind the scenes. First, the code you write is tokenized and parsed
by the IronRuby engine. The parsed code is then converted into the DLR's Abstract Syntax Tree (AST). This is a standardized AST that all language implementations, such as IronPython, need to provide to the DLR in order to execute the code.
Once the DLR has the AST, it converts the tree into Intermediate Language (IL). All .NET code is compiled down into IL to provide a generic language for the CLR. This is how IronRuby can interoperate with
the .NET Framework—behind the scenes, all the code is executed as IL instructions. After the DLR has completed the conversion, it passes the IL to the CLR for execution and the result is returned to IronRuby.
During this process there are additional steps to improve performance and reliability, such as caching. For a deeper explanation, I recommend reading Bill Chiles's CLR Inside Out column "
IronPython and the Dynamic Language Runtime
" in the October 2007 issue of MSDN Magazine .
Within a separate assembly there is a hosting API that allows you to embed IronRuby within your own applications, allowing you to execute Ruby code from C# or users to execute their own code.
For more about the IronRuby implementation, download the entire source code from
RubyForge
. IronRuby is an open-source project implemented in C# and is an excellent example of a dynamic language implementation.
IronPython
is available as an open source project hosted at CodePlex, and it includes a sample language called ToyScript if you want an introduction into how the DLR works.
To ensure continued compatibility with the core Ruby platform, the IronRuby team is using RubySpecs. This is a shared set of examples based around how the Ruby language should be implemented. The aim of
RubySpecs is to ensure different implementations including Matz's Ruby Interpreter (MRI), JRuby, MacRuby, and IronRuby have the same behavior. RubySpecs uses a syntax-compatible version of RSpec called MSpec. These are viewed as the acceptance tests for the
IronRuby implementation.
Testing a C# App with IronRuby
For many years now, the awareness and practice of TDD has increased as a way to develop software, resulting in higher quality code, in terms of design and maintainability, along with fewer defects along
the way. With IronRuby, I can use the RSpec specification framework and runner to provide examples of how my C# objects work, following a BDD approach instead of TDD.
While the scenario framework, which I'll cover in a future article, is more aligned with acceptance testing at the app level, the specification framework is more aligned to developers writing their own
specifications about the code they are just about to implement and its expected behavior. The specification framework is based around providing examples describing the behavior at the object level. These examples can be run to verify that the implementation
of the system is still working as the developer expects while providing documentation about how the objects are expected to behave.
This is an important distinction compared to unit-testing frameworks such as NUnit and MbUnit. Both of those use test attributes to indicate a method is a test for the system. RSpec takes a different approach.
RSpec says that each method is an example of how the code is intended to work. While the distinction is subtle, it changes the way you write the examples, including the terminology you use, the way you organize the methods, and how easily the concept can be
understood compared to TDD. With BDD and RSpec, together with the close integration of IronRuby and the .NET Framework, you can start using IronRuby to test C# applications. In terms of an example, the classic RSpec example is the Bowling game.
First, you need to access the bowling game's implementation in a C# assembly:
require File.dirname(__FILE__) + \
'/InteropSamples/Bowling/Bowling/bin/Debug/bowling.dll'
You then need to access RSpec:
require 'rubygems'
require 'spec'
Now you can begin writing examples. This is an example showing how bowling implementation should work. RSpec has a Domain Specific Language (DSL) that you must follow for the examples to be executable.
The first part of the DSL is the describe block. Here you simply state the object you are "describing" together with an optional description. In this case, I define the Bowling object, which will be implemented in C#:
describe Bowling, " defines the bowling game" do
To improve readability, any setup will be performed within a before block:
before(:each) do
@bowling = Bowling.new
End
I am now in a position to interact with the object, create the example and verify that it works correctly. I use the "it" block, providing a string stating the context of the example and what it is demonstrating.
I then write the section of code that interacts with the system and verifies the correct action has happened:
it "should score 0 for a gutter game" do
20.times { @bowling.hit(0) }
@bowling.score.should == 0
end
end
The C# implementation simply looks like this:
public class Bowling {
public int Score { get; set; }
public void Hit(int pins)
{ Score += pins; }
}
Finally, execute this to verify that the bowling example works as expected with RSpec testing C# objects:
>ir bowling_spec.rb
.
Finished in 1.0458315 seconds
1 example, 0 failures
If I wanted a more detailed report, I could select the format to be specdoc. This outputs the object description together with all the examples, stating if they have passed or not:
>ir bowling_spec.rb --format specdoc
Bowling defines the bowling game
- should score 0 for a gutter game
Finished in 1.43728 seconds
1 example, 0 failures
At the time of writing, the IronRuby team is not regularly shipping binaries. The team has said it is waiting until it is happy with the implementation, compatibility, and performance before releasing
official binaries. However, because the source code is freely available, you can download the code and build it yourself.
To download the source code, you will need to have a Git source control client, such as
msysgit
, installed. The
IronRuby source control is available online. If you would like more information then I recommend you visit the
IronRuby project site
or my blog post "
Downloading IronRuby from GitHub ." Once you have downloaded the source code, you can compile the assembly either with Visual Studio using the IronRuby.sln solution file or, if you have MRI installed, then you can use
the command:
rake compile
Once you have IronRuby compiled, you must download various libraries, such as RSpec, to gain the extra functionality. Ruby has a concept of RubyGems where the gems are packages you can download to gain
the functionality and the additional dependencies. To download RSpec, type the following at a command prompt:
gem install rspec
You would now be able to access the RSpec library from within your Ruby code. However, at the time of writing, RSpec does not work with IronRuby due to one or two bugs. Hopefully by the time this article
is published RSpec should be working with IronRuby. To find out the status of the RSpec support, see the
RSpec Web site
or the
IronRuby mailing list .
If the bug hasn't been fixed, I have made a
binary with the bug fixed, together with the RSpec library in place
. (Note: don't use this version once the team has fixed the problem.)
Once you have the correct binaries in place, ir.exe is the IronRuby interpreter that can execute your code. If you simply launch the app from the command line, you'll enter the interactive console. In
Ruby, code is executed on a line-by-line basis, excellent for learning and debugging, but also for running short commands to solve daily problems. If you provide a file name as a parameter, then the file is executed with the results output to the console.
Moving Forward
In my next article, I will introduce the concept of acceptance testing and how it can improve communication between customer and developer. I will demonstrate how acceptance testing can be automated using
IronRuby and RSpec to verify .NET applications and create an executable specification for the system.
Ben Hall is a C# developer/tester with a strong passion for software development and loves writing code. Ben works at Red Gate Software in the U.K. as
a Test Engineer and enjoys exploring different ways of testing software, including both manual and automated testing, focusing on the best ways to test different types of applications. Ben is a C# MVP and maintains a blog at
Blog.BenHall.me.uk
.
本文讨论:
|
本文涉及以下技术: IronRuby |
struggles。
RSpec 框架生成的方式对象为了行为,示例提供文档和系统生成正确的验证。这将设置为将来的项目中我将介绍验收测试详细信息,并演示 IronRuby 创建验收测试阶段。
xUnit 测试框架。我已发现最好的方法是使用行为驱动开发 (BDD)。Dan North 设计了一个BDD
Framework 调用 JBehave作为一种定义方案,介绍一种方式可以将传递到整个团队无论技术的功能的应用程序的行为。
的拼音、 流行框架的 RBehave。RSpec 适合 BDD 两种不同方法: 根据文章和方案描述应用程序的行为的 Dan North 方法和Dave
Astels 方法的更多集中在对象级别创建的示例。
(如大括号公钥和私钥的事实。总体,我不认为通过 C# 的提供程序是,和 NSpec / NBehave 可以匹配是通过 IronRuby 和 RSpec 可用。以前,这将已主要问题的 C# 开发,您可能不具有使用拼音、
基于 RSpec 测试 C# 应用程序。与 IronRuby,这不是一个问题。
Framework 和.NET 兼容语言的框架。结果是可以使用拼写的语言和 RSpec 测试 C# 应用程序 !
C# 中,使用 yield 语句将控制返回给调用方法的方法。
n 的值实现方法调用 GetName,将工作代码:
class Person attr_accessor :name def GetName() @name end end class Product def GetName() "The product with no name" end end
outputName(Product.new) => "The product with no name" $x = Person.new $x.name = "Ben Hall" outputName($x) => "Ben Hall"
outputName("MyName") => :1:in 'outputName': \ undefined method 'GetName' \ for MyName:String (NoMethodError)
应用程序。这是非常有用,尤其是在单元测试,之类的动态语言中它允许您自定义对象和您自己的要求的方法的行为。
IronRuby,利用与.NET 的互操作性和拼写的语言的动态特征。我预计使用多种不同语言和等不是针对主应用程序和用于测试的拼音、 C# 的技术的人。
(WPF) 和 Silverlight 在拼音、 写入应用程序代码时。
mscorlib 和两个 WPF 程序集提供了在 N 一样使用 C# 使用在需要语句:
require 'mscorlib' require 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' require 'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
Window = System::Windows::Window Application = System::Windows::Application Button = System::Windows::Controls::Button
C#,您可以订阅事件和提供一个调用 Click 事件时调用的块:
所示的单击事件。 我使用.NET Framework 交互我将交互拼音库方式相同。通过 John Lam,团队设置出在核心原则之一是保持 True 为拼写的语言。这是采用由当前拼写开发,那些不希望更改他们仅仅因为要转向
IronRuby 创建拼写的应用程序的方式的一个重要原则。而是它们可以访问所有丰富的.NET 代码相同的方式。
方法签名返回界面或具体的类型。我的示例中,我有定义一个 HelloWorld 方法的接口:
HelloMethodOn4Times 调用基实现的新方法:
public class Hello4Times : Hello3Times { public string HelloMethodOn4Times () { return base.HelloWorld(); } }
public static class HelloWorld { public static IHello ReturnHello4TimesAsInterface() { return new Hello4Times(); }
已确定的返回的对象类型,并可以分派方法调用:
puts InteropSample::HelloWorld.ReturnHello4TimesAsInterface.HelloWorld puts interopSample::HelloWorld.ReturnHello4TimesAsInterface.HelloMethodOn4Times
WPF 中内置的 MessageBox 相同的。然后只需调用 Show 方法使用不同的默认值类上创建的新方法:
class System::Windows::MessageBox def self.ShowMessage(msg) System::Windows::MessageBox.Show(msg, msg, \ System::Windows::MessageBoxButton.OK, \ System::Windows::MessageBoxImage.Stop) end end
.
IronRuby 的拼音代码大量工作将在后台。首先,您编写的代码是标记,并分析由 IronRuby 引擎。分析的代码然后转换为 DLR 的抽象语法树 (AST)。这是一个标准化的
AST,如 IronPython 的所有语言实现都需要向 DLR 以执行代码的。
IronRuby 可以如何使用.NET Framework 互操作,作为 IL 说明在后台执行所有代码。DLR 完成转换后,它传递给执行 CLR 的 IL,并且结果返回到 IronRuby。
和动态语言运行库"2007 年 10 月发布的MSDN 杂志 》.
IronRuby 在 C# 中实现的开源项目并且为一个动态语言实现的极好示例。IronPython是可用,因为在
CodePlex,和其托管一个开源项目包含调用 ToyScript 要介绍到 DLR 的工作原理的示例语言。
的目的是确保包括 Matz 的拼音解释器 (MRI) 的不同实现,JRuby、 MacRuby,和 IronRuby 具有相同的行为。RubySpecs 使用语法兼容版本的名为 MSpec RSpec。这些被视为验收测试
IronRuby 实现的。
RSpec 规范框架和 Runner 提供如何我的 C# 对象工作,以下 BDD 方法,而不是 TDD 的示例。
采用其他方法。RSpec 显示每个方法为如何代码是要使用的一个示例。细微区别时它将更改方式编写包括您使用,您组织在的方法和如何轻松地概念可理解为 TDD 的比较方式的术语的这些示例。使用
BDD 和 RSpec,IronRuby 关闭集成和在.NET Framework 可以启动使用 IronRuby 测试 C# 应用程序。示例,方面的经典的 RSpec 示例是球游戏。
具有域特定语言 (DSL) 则必须按照示例要执行的。第一部分在 DSL 的是在介绍块。此处只是声明您要在"描述"结合可选说明该对象。在这种情况下我将定义球对象将实现
C# 中:
it "should score 0 for a gutter game" do 20.times { @bowling.hit(0) } @bowling.score.should == 0 end end
Bowling defines the bowling game
- should score 0 for a gutter game
Finished in 1.43728 seconds
1 example, 0 failures
IronRuby 源代码管理联机才可用。如果您希望更多信息,则建议您访问在IronRuby
项目网站或我的博客张贴内容"从
GitHub 下载 IronRuby." 源代码您下载后您可以编译该程序集与使用 IronRuby.sln 解决方案文件的 Visual Studio 或者,如果 MRI 安装然后您可以使用命令:
RSpec,请在命令提示符处键入以下:
RSpec 应要与处理 IronRuby 而。要了解 RSpec 支持的状态,请参阅在RSpec
Web 站点或
IronRuby 邮寄列表.
验证.NET 应用程序,并创建为系统的可执行文件规范。
本工作在英国的红色 Gate Software为测试工程师和受浏览包括手动和自动测试侧重于测试的应用程序的不同类型的最佳方式的测试软件的不同方式。Ben
是 C# MVP,并维护在博客Blog.BenHall.Me.uk.
原文出自MSDN杂志