.NET Interop

IronRuby 및 RSpec 입문, 1부

Ben Hall

이 기사는 IronRuby 시험판 버전을 기준으로 합니다. 여기에 포함된 모든 정보는 변경될 수 있습니다.

이 기사에서는 다음 내용에 대해 설명합니다.

  • Ruby와 덕 타이핑(Duck Typing)
  • Ruby와 Microsoft .NET Framework
  • IronRuby 및 RSpec 사용
이 기사에서 사용하는 기술:
IronRuby

코드는 MSDN 코드 갤러리에서 다운로드할 수 있습니다.
온라인으로 코드 찾아보기

목차

코드에서 요구 사항 및 예제 정의
Ruby와 덕 타이핑(Duck Typing)
Ruby와 메타 프로그래밍
Ruby와 .NET Framework
Ruby와 CLR
IronRuby 살펴보기
IronRuby를 사용한 C# App 테스트
앞으로의 방향

"우리가 원한 건 이런 게 아닙니다!" 개발자라면 최신 빌드를 납품하자마자 고객으로부터 이런 말을 한 번쯤 들어보았을 겁니다. 고객이 이런 반응을 보이는 이유는 다양합니다. 개발자가 요구 사항을 제대로 이해하지 못했거나 시스템의 일부가 작동하지 않을 수도 있습니다.

고객도 자신의 요구 사항을 정확히 알지 못하는 경우가 많기 때문에 시스템에 필요한 모든 기능을 100페이지에 이르는 장황한 기술 문서로 만들어 전달할 때가 많습니다. 반면 개발자는 응용 프로그램의 작동 방식을 이해하여 기능을 해치지 않으면서 새로운 요구 사항을 구현하기 위해 문서화되지 않은 기존 코드와 씨름해야 합니다.

시대가 변하면서 소프트웨어 개발 방식도 이러한 문제를 해결하여 프로세스 도중에 오류를 발생시키지 않고 최초 시도에서 고객 요구 사항을 구현할 수 있도록 하는 데 중점을 두도록 바뀌었습니다. 이러한 방식에서는 Ruby와 같은 언어의 이점을 활용함으로써, 쉽게 읽고 관리할 수 있는 코드를 짧은 반복 프로세스로 작성할 수 있도록 합니다.

이 기사에서는 Ruby와 IronRuby를 소개하고 Ruby를 Microsoft .NET Framework 기반 코드와 상호 운용하는 것과 관련한 몇 가지 기본 사항을 설명합니다. 또한 RSpec과 같은 프레임워크를 사용하여 개체 동작을 보여 주는 예제를 만들고 시스템이 올바르게 구축되었는지를 확인하는 설명서와 유효성 검사를 제공하는 방법도 설명하겠습니다. 이 기사에서는 이후 승인 테스트에 대해 자세히 설명하고 IronRuby를 사용하여 승인 테스트를 작성하는 방법을 보여 주는 기사를 이해하기 위한 기초 지식을 제공합니다.

코드에서 요구 사항 및 예제 정의

편리하게 예제를 작성하고 요구 사항을 정의하려면 프레임워크가 필요합니다. 자동화된 승인 테스트 또는 실행 파일 사양을 작성하는 방법은 다양합니다. 표준 xUnit 테스트 프레임워크를 아무 지장없이 사용하는 사람도 있고 Fit 프레임워크와 Fitness 프레임워크를 사용하는 사람도 있습니다. 개인적으로는 BDD(Behavior-Driven Development)를 사용하는 것이 가장 효과적이었습니다. Dan North는 팀원의 기술 숙련도에 관계없이 전체 팀이 이해할 수 있도록 응용 프로그램의 동작을 설명하는 시나리오를 정의하는 한 가지 방법으로 JBehave라는 BDD 프레임워크를 고안해냈습니다.

JBehave는 North가 TDD(Test-Driven Development)를 사용하면서 겪었던 문제를 해결하기 위해 Java 언어용으로 구현한 프레임워크입니다. 나중에 North는 유명한 Ruby용 프레임워크인 RSpec에 통합된 RBehave도 개발했습니다. RSpec은 두 가지 BDD 방식을 제공합니다. 사례 및 시나리오를 기반으로 응용 프로그램의 동작을 설명하는 Dan North의 방식과 개체 수준에서 예제를 만드는 데 보다 중점을 둔 Dave Astels의 방식이 그것입니다.

C#에는 NSpec, NBehave 등 몇 가지 BDD 프레임워크가 포함되어 있습니다. C#로 테스트를 작성할 경우 가장 큰 문제점은 괄호나 public, private 같은 추가적인 구조적 요소와 메타데이터 때문에 테스트의 궁극적인 목적이 잘 드러나지 않는다는 점입니다. 전반적으로 C#와 NSpec/NBehave를 통해 제공되는 프레임워크는 IronRuby와 RSpec를 통해 제공되는 프레임워크에 비해 기능이 떨어진다고 생각합니다. 이전에는 Ruby 기반 RSpec을 사용하여 C# 응용 프로그램을 테스트할 수 없었기 때문에 C# 개발자들이 불편을 겪었지만 IronRuby가 개발되면서 더 이상 문제가 되지 않습니다.

아직 개발 초기이기는 하지만 IronRuby는 DLR(동적 언어 런타임)을 이용해 CLR 기반에 Ruby 언어를 구현합니다. IronRuby를 사용하면 기존 Ruby 응용 프로그램과 프레임워크를 .NET Framework 및 .NET 호환 언어와 함께 활용할 수 있습니다. 결과적으로 Ruby 언어와 RSpec을 사용하여 C# 응용 프로그램을 테스트할 수 있게 되는 것입니다.

언어로서 Ruby는 간결하기 때문에 작성해야 할 코드 양이 적고 자연스러운 방식으로 표현할 수 있어 코드를 관리하기가 용이합니다. 예를 들어 파일에서 모든 텍스트 줄을 읽고 콘솔에 쓰려면 다음과 같은 코드를 작성합니다.

File.readlines('AboutMicrosoft.txt').map {|line| puts line}

이 코드는 AboutMicrosoft.txt라는 파일을 열어 모든 줄을 읽고 각 줄을 매개 변수로 블록에 전달하여 콘솔에 씁니다. Ruby에서 블록은 괄호 안에 있는 명령문 모음으로, yield 문을 사용하여 호출하는 메서드에 제어권을 반환하는 C#의 메서드 호출과 유사합니다.

Ruby가 테스트에 적합한 이유 중 하나는 바로 자연스러운 언어적 표현 방식입니다. 이러한 특징 때문에 테스트나 시나리오를 이해하기가 쉽습니다.

Ruby와 덕 타이핑(Duck Typing)

Ruby와 동적 언어가 이해하기 쉬운 이유 중 하나로 형식 지정을 처리하는 방식을 들 수 있습니다. 즉, 사용자가 형식을 정의하는 대신 Ruby 코드가 중단될 때 변수의 형식이 추론됩니다. 이들 언어도 강력한 형식의 언어이긴 하지만 변수의 형식을 동적으로 결정한다는 차이가 있습니다.

C#에서는 인터페이스가 개체의 분리를 고려하면서도 구현을 위해 계약을 정의합니다. 반면 Ruby에서는 계약과 인터페이스가 필요 없습니다. 대신 개체에 호출하는 메서드의 구현이 있으면 호출하고 없으면 오류를 반환합니다. 따라서 개체의 계약이 코드의 다른 부분과의 관계가 아니라 구현에 의해 정의됩니다.

이를 보여 주기 위해 런타임에 형식이 추론되는 값을 받는 메서드를 만들었습니다. 그리고 값과 함께 Hello라는 문자열을 출력합니다.

   def SayHello(val)
     puts "Hello #{val}"
   end

형식을 처리하는 이러한 방식 덕분에 코드를 변경하지 않고도 문자열과 정수에 같은 메서드를 재사용할 수 있습니다.

   SayHello("Test") => "Hello Test"
   SayHello(5) => "Hello 5"

Ruby에서 괄호는 선택 사항이기 때문에 코드를 읽기 쉽도록 괄호 없이 메서드를 호출할 수도 있습니다.

   SayHello "Test" => "Hello Test"

C#에서는 개체를 사용하여 이러한 코드를 작성할 수 있지만 복잡한 개체의 경우에 어떻게 되는지 한번 살펴보겠습니다. GetName에 대한 호출 결과를 출력하는 새 메서드를 정의했습니다. 매개 변수 n의 값이 GetName이라는 메서드를 구현한다면 코드는 문제 없이 작동합니다.

def outputName(n)
  puts n.GetName()
end 

그런데 다음에는 서로 관련 없는 클래스가 두 개 있습니다.

class Person
  attr_accessor :name
  def GetName()
    @name
  end
end
class Product
  def GetName()
    "The product with no name"
  end
end

Person에는 name 변수를 설정할 수 있도록 하는 접근자가 있는 반면 Product는 하드코드된 값을 반환합니다. Ruby에서는 메서드의 마지막 코드 줄의 마지막 결과가 기본적으로 반환되므로 return 문을 작성할 필요가 없습니다.

outputName 메서드를 호출하면 GetName 메서드가 호출되어 단일 형식으로 고정되지 않는 Ruby의 형식 지정 방식이 코드 재사용성을 어떻게 높이는지를 보여 줍니다.

outputName(Product.new) => "The product with no name"
$x = Person.new
$x.name = "Ben Hall"
outputName($x) => "Ben Hall"

GetName을 구현하지 않는 인수를 사용하여 메서드를 호출하면 NoMethodError가 발생합니다.

outputName("MyName") => :1:in 'outputName': \
  undefined method 'GetName' \
  for MyName:String (NoMethodError)

이 개념에 대해 우려하는 사람도 있지만, 특히 C# 응용 프로그램을 테스트하는 경우에 테스트 용이성과 응용 프로그램 성능을 향상시켜 줍니다. 이에 대해서는 다음 섹션에서 살펴보겠습니다.

Ruby와 메타 프로그래밍

다른 동적 언어와 마찬가지로 Ruby도 메타 프로그래밍이라는 개념을 차용하고 있습니다. 따라서 런타임에 어떤 클래스 또는 개체라도 확장하고 메서드와 동작을 정의할 수 있어 자가 수정 응용 프로그램을 개발할 수 있습니다. 이러한 기능은 필요에 맞게 개체와 메서드의 동작을 사용자 지정할 수 있도록 해주므로 동적 언어에서 단위 테스트에 특히 유용합니다.

마찬가지로, 기본 제공 개발자가 원하는 고유한 기능을 제공하도록 Ruby 클래스를 확장할 수도 있습니다. 이는 C# 3.0의 확장 메서드와 유사합니다. C#의 경우 생성 가능한 메서드와 관련하여 여러 가지 제약이 따릅니다. 예를 들어 반드시 정적 메서드로 각각 별도의 클래스에 만들어야 합니다. 이러한 모든 제한 조건은 가독성을 해칩니다. 그러나 Ruby에서는 기존 클래스에 일반 메서드를 만들기만 하면 됩니다. 예를 들어 Integer는 정수를 처리하는 클래스로, 일련의 메서드를 포함하지만 숫자가 짝수인지를 나타내는 메서드는 없습니다.

그러한 메서드를 만들려면 이름(Integer)이 같은 클래스를 새로 만들고 새 메서드를 정의하면 됩니다. 메서드의 끝에 있는 물음표는 부울 값을 읽기 쉽도록 반환한다는 것을 나타냅니다.

class Integer
   def even?()
       self.abs % 2 == 0
   end
end
puts 2.even? => true
puts 1.even? => false

Ruby와 .NET Framework

IronRuby는 정적 언어와 동적 언어 중 어떤 것을 사용할지에 대해 케케묵은 논쟁을 벌일 필요 없이 작업에 좀 더 적합한 언어를 사용할 수 있도록 해줍니다. 즉, C#을 사용하는 편이 효과적이라면 C#을 사용하면 되고, 보다 동적인 방식이 필요하면 손쉽게 IronRuby를 사용하여 .NET과의 상호 운용성과 Ruby 언어의 동적 특성을 활용할 수 있습니다. 많은 사람들이 여러 가지 언어와 기술을 혼용하지 않을까 생각합니다. 예를 들어 기본 응용 프로그램에는 C#을 사용하고 테스트에는 Ruby를 사용하는 식이죠.

DLR은 .NET interop을 사용할 수 있는 기반을 제공합니다. 이러한 기반이 마련되면 Windows Forms, WPF(Windows Presentation Foundation), Silverlight 등의 UI 기술을 이용하면서 응용 프로그램 코드는 Ruby로 작성할 수 있습니다.

IronRuby에서 WPF를 활용하는 것은 어렵지 않습니다. 다음 코드를 실행하면 완벽하게 작동하는 WPF 창이 IronRuby에서 만들어집니다. 단, C#에서 필요한 명령문을 사용하여 참조하듯이 .NET Framework 3.0에서 제공되는 mscorlib와 두 가지 WPF 어셈블리를 참조해야 합니다.

require 'mscorlib'
require 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
require 'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

이제 WPF 개체를 만들어 상호 작용할 수 있습니다. 먼저 사용할 모든 개체의 별칭을 만듭니다. 별칭을 사용하면 네임스페이스를 통해 개체에 액세스하지 않아도 되므로 코드가 간결해집니다.

Window = System::Windows::Window
Application = System::Windows::Application
Button = System::Windows::Controls::Button

다음으로 WPF 창을 만들어 제목을 지정합니다.

win = Window.new
win.title = 'IronRuby and WPF Interop'

같은 방법으로 단추를 만듭니다.

mainButton = Button.new
mainButton.content = 'I'm a WPF button — press me'

코드에서 보듯이 두 인스턴스 모두에서 별칭을 사용하여 개체의 전체 이름에 액세스하고 있습니다. 예에서는 단추를 클릭했을 때 사용자에게 MessageBox가 표시되도록 했습니다. 그러려면 C#의 경우와 마찬가지로 이벤트를 구독하고 click 이벤트가 호출될 때 호출되는 블록을 제공하면 됩니다.

mainButton.click do |sender, args|
  System::Windows::MessageBox.Show("Created using IronRuby!")
end

마지막으로 창의 내용이 단추가 되도록 설정하고 새 Application 개체를 만들고 창을 실행합니다.

win.content = mainButton
my_app = Application.new
my_app.run win

fig01.gif

그림 1 IronRuby를 사용하여 WPF 응용 프로그램 만들기

이제 그림 1과 같이 click 이벤트가 제대로 연결된 완벽한 대화형 WPF 창이 표시됩니다. Ruby 라이브러리와 같은 방법으로 .NET Framework와 상호 작용했습니다. John Lam과 그의 팀에서 정한 핵심 원칙 중 하나가 Ruby 언어에도 그대로 적용되는 것입니다. 이는 IronRuby로 전환한다고 해서 Ruby 응용 프로그램을 개발할 때 사용하던 방식을 변경할 생각은 없다고 말하는 현재 Ruby 개발자들이 IronRuby를 도입하기 위해 반드시 알아야 할 원칙입니다. 대신 같은 방법으로 기능이 풍부한 모든 .NET 코드에 액세스할 수 있게 됩니다.

Ruby와 CLR

다른 예를 살펴보겠습니다. IEnumerable의 AddRange에 액세스하려는 경우 C# 코드는 다음과 같습니다.

ArrayList list = new ArrayList();
list.AddRange(new [] { 1,2,3,4 });

그러나 Ruby의 경우 메서드 이름에 있는 단어들을 밑줄로 구분하여 가독성을 높여야 한다는 일종의 합의가 이루어졌습니다. 이 규칙에 따라 별도의 라이브러리를 만들려면 시간이 너무 많이 소요되고 오류가 발생하기 쉬울 뿐만 아니라 추가적인 타사 개발도 지원할 수 없게 됩니다.

대신 CLR 개체의 경우 IronRuby는 Ruby 메서드 호출을 CLR의 해당하는 메서드 이름으로 변환합니다.

   $list = ArrayList.new
   $list.add_range([1,2,3,4])

여기서는 소문자 단어를 밑줄로 구분한다는 Ruby의 방식에 따라 AddRange 메서드에 액세스했습니다. Ruby에서도 메서드 이름이 존재하므로 원하는 경우 다음과 같이 CLR 명명 규칙을 따를 수도 있습니다.

$list.AddRange([1,2,3,4])

두 방법 모두 결과는 동일합니다. 개인적인 취향에 따라 사용할 방법을 선택하면 됩니다.

앞서 언급했듯이 Ruby에서는 개체 형식을 런타임에 추론합니다. 이는 C# 개체를 처리할 때도 마찬가지입니다. 동일한 개체를 반환할 때 반환되는 개체의 형식이 서로 다른 일련의 C# 메서드가 있다고 가정해보겠습니다. 메서드 서명은 인터페이스 또는 구체적인 형식을 반환합니다. 필자의 예제에는 단일 HelloWorld 메서드를 정의하는 인터페이스가 있습니다.

public interface IHello {
  string HelloWorld();    
}

Hello3Times에서 상속되는 Hello4Times 클래스를 만들었습니다. Hello3Times는 인터페이스에서 상속되지만 세 가지 추가 Hello­World 메서드를 구현합니다. 그리고 Hello4Times 클래스 내에 기본 구현을 호출하는 HelloMethodOn4Times라는 새 메서드를 정의했습니다.

public class Hello4Times : Hello3Times {
  public string HelloMethodOn4Times () {
    return base.HelloWorld();
  }
}

그런 다음 Hello4Times 클래스의 새 인스턴스를 호출 코드에 인터페이스로 반환하는 정적 클래스와 메서드를 정의했습니다. 이는 호출 코드가 HelloWorld만 인식하고 정의된 추가 메서드는 인식하지 못하도록 하기 위한 것입니다.

public static class HelloWorld {
  public static IHello ReturnHello4TimesAsInterface() {
    return new Hello4Times();
  }

예제 Ruby 코드에는 두 가지 메서드 호출이 있습니다. 첫 번째 호출은 인터페이스로 정의된 메서드에 대한 호출로, 문제 없이 작동합니다. 그러나 두 번째 호출은 구체적인 클래스의 메서드에 대한 호출입니다. IronRuby는 이미 반환되는 개체의 형식을 결정했으며 메서드 호출을 디스패치할 수 있습니다.

puts InteropSample::HelloWorld.ReturnHello4TimesAsInterface.HelloWorld
puts interopSample::HelloWorld.ReturnHello4TimesAsInterface.HelloMethodOn4Times

이러한 모든 프로세스는 메서드를 호출하기 위해 개체를 올바른 형식으로 캐스팅할 필요 없이 진행됩니다.

이 원리에 따라 .NET 개체를 Ruby 개체와 같은 방법으로 확장할 수 있습니다. MessageBox를 표시할 때마다 사용할 아이콘과 단추를 정의하는 대신 메시지를 제공하려고 합니다.

기본 제공 기능이 마음에 들지 않아 실제 MessageBox 클래스를 확장하여 원활한 상호 작용이 이루어지도록 하려 할 수도 있을 겁니다. Ruby에서는 어렵지 않습니다. 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

그리고 다음 코드 블록을 실행합니다.

System::Windows::MessageBox.ShowMessage( \
  "I'm going to show you a message") 

그러면 ShowMessage 메서드를 호출할 수 있습니다. 결과는 그림 2와 같은 메시지입니다.

fig02.gif

그림 2 Ruby에서 사용자 지정 MessageBox 표시

IronRuby 살펴보기

interop은 어떻게 가능한 것일까요? 대답은 DLR에 있습니다. 간단히 말하면, IronRuby를 사용하여 Ruby 코드를 실행할 때 내부적으로 많은 작업이 수행됩니다. 우선, 작성하는 코드가 IronRuby에 의해 토큰화되고 구문 분석됩니다. 그런 다음 구문 분석된 코드가 DLR의 AST(추상 구문 트리)로 변환됩니다. 이는 코드를 실행하기 위해 IronPython과 같은 모든 언어 구현에서 DLR에 제공해야 하는 표준화된 AST입니다.

AST가 제공되면 DLR은 트리를 IL(Intermediate Language)로 변환합니다. CLR에서 사용할 수 있는 일반 언어를 제공하기 위해 모든 .NET 코드는 IL로 컴파일됩니다. IronRuby는 이런 방식으로 .NET Framework와 상호 운용되는 것입니다. 즉, 내부적으로 모든 코드가 IL 명령으로 실행되는 것입니다. DLR에서 변환이 끝나면 IL이 CLR로 전달되어 실행되고 IronRuby에 결과가 반환됩니다.

이 프로세스를 진행하는 동안 성능과 가독성을 높이기 위해 캐싱과 같은 추가 단계가 실행됩니다. 자세한 내용은 2007년 10월호 MSDN Magazine에서 Bill Chiles의 CLR Inside Out 칼럼 "IronPython과 동적 언어 런타임"을 참조하시기 바랍니다.

응용 프로그램에 IronRuby를 포함할 수 있도록 하는 호스팅 API가 별도의 어셈블리 내에 있습니다. 이렇게 함으로써 C#에서 Ruby 코드를 실행하거나 사용자가 자신이 작성한 코드를 실행할 수 있게 됩니다.

IronRuby 구현에 대해 자세히 알아보려면 RubyForge에서 전체 소스 코드를 다운로드하십시오. IronRuby는 C#에서 구현된 공개 소스 프로젝트로, 동적 언어 구현을 보여 주는 좋은 예입니다. IronPython은 CodePlex에서 공개 소스 프로젝트로 제공하고 있으며, DLR의 작동 방식을 살펴볼 수 있는 ToyScript라는 샘플 언어가 포함되어 있습니다.

IronRuby 팀은 핵심 Ruby 플랫폼과의 호환성을 유지하기 위해 RubySpecs를 사용하고 있습니다. RubySpecs는 Ruby 언어의 정석적인 구현 방식을 기반으로 하는 공유 예제 집합입니다. MRI(Matz's Ruby Interpreter), JRuby, MacRuby, IronRuby 등의 서로 다른 구현에서 동일한 동작이 나타나도록 하는 것이 RubySpecs의 주된 목적입니다. RubySpecs는 구문이 호환되는 RSpec 버전인 MSpec을 사용합니다. 이들은 IronRuby 구현에 대한 승인 테스트라고 볼 수 있습니다.

IronRuby를 사용한 C# App 테스트

지난 수 년 동안 소프트웨어 개발의 한 방식으로서 TDD에 대한 인식과 실제 사용이 확대되면서 설계와 관리 용이성 측면에서 코드의 품질이 높아졌고 결함이 줄었습니다. IronRuby의 경우 TDD 대신 BDD에 따라 RSpec 사양 프레임워크와 실행기를 사용하여 C# 개체의 작동 방식에 대한 예제를 제공할 수 있습니다.

앞으로 기사에서 다룰 시나리오 프레임워크가 응용 프로그램 수준에서는 승인 테스트에 보다 적합하지만, 구현할 코드와 그 동작에 대한 사양을 직접 작성하려는 개발자에게는 사양 프레임워크가 적합합니다. 사양 프레임워크는 개체 수준에서 동작을 설명하는 예제를 제공하는 방식을 기반으로 합니다. 이러한 예제를 실행하여 시스템 구현이 개발자가 예상한 대로 작동하는지 확인하고 개체의 정상 동작에 대한 설명서를 제공할 수 있습니다.

이는 NUnit이나 MbUnit 같은 단위 테스트 프레임워크와 비교되는 중요한 차이점입니다. 이 두 가지 프레임워크는 모두 테스트 특성을 사용하여 메서드가 시스템에 대한 테스트임을 나타냅니다. 반면 RSpec은 다른 방식으로 작동합니다. RSpec은 각 메서드가 코드의 정상적인 작동 방식을 보여 주는 예제임을 알립니다. 차이가 극명하게 들어나지는 않지만 사용하는 용어, 메서드 구성 방법, TDD에 비해 이해하기 쉬운 개념 등 예제를 작성하는 방법 전반에 변화를 가져옵니다. IronRuby와 .NET Framework에 밀접하게 통합하여 BDD 및 RSpec을 사용하면 IronRuby를 사용하여 C# 응용 프로그램을 테스트할 수 있습니다. RSpec의 작동 방식을 보여 주는 전형적인 예로 볼링 경기를 들 수 있습니다.

우선, C# 어셈블리에서 볼링 경기의 구현에 액세스해야 합니다.

require File.dirname(__FILE__) + \
  '/InteropSamples/Bowling/Bowling/bin/Debug/bowling.dll'

그런 다음 RSpec에 액세스합니다.

require 'rubygems'
require 'spec'

이제 예제 작성을 시작할 수 있습니다. 다음은 볼링 구현의 정상적인 작동을 보여 주는 예제입니다. RSpec에는 실행 파일이 될 예제와 관련하여 따라야 할 DSL(Domain Specific Language)이 있습니다. DSL의 첫 부분은 describe 블록입니다. 이 블록에서는 "설명하려는" 개체를 옵션 설명과 함께 명시합니다. 예제에서는 C#에서 구현할 Bowling 개체를 정의합니다.

describe Bowling, " defines the bowling game" do

가독성을 높이기 위해 모든 설정은 before 블록 내에서 수행합니다.

  before(:each) do  
    @bowling = Bowling.new 
  End

이제 개체와 상호 작용하고 예제를 만들어 올바르게 동작하는지 확인할 수 있습니다. "it" 블록을 사용하여 예제의 컨텍스트와 설명하는 대상을 명시하는 문자열을 제공합니다. 그런 다음 시스템과 상호 작용하고 동작이 올바르게 나타나는지 확인하는 코드 섹션을 작성합니다.

  it "should score 0 for a gutter game" do
    20.times { @bowling.hit(0) }
    @bowling.score.should == 0
  end
end

C# 구현은 다음과 같습니다.

public class Bowling {
  public int Score { get; set; }
  public void Hit(int pins)
    { Score += pins; }
}

마지막으로 다음 코드를 실행하여 RSpec 테스트 C# 개체로 볼링 예제가 정상적으로 작동하는지 확인합니다.

>ir bowling_spec.rb
.
Finished in 1.0458315 seconds
1 example, 0 failures

형식을 specdoc로 선택하면 보다 자세한 보고서를 얻을 수 있습니다. 이 형식은 모든 예제와 함께 개체 설명을 출력하고 테스트 통과 여부를 알려 줍니다.

>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

이 기사를 작성할 당시 IronRuby 팀은 이진 파일을 정식으로 제공하지 않았습니다. 구현, 호환성 및 성능이 만족스러운 수준이 되면 정식 이진 파일을 공개할 예정이라고 합니다. 그러나 소스 코드는 자유롭게 사용할 수 있으므로 코드를 다운로드하여 직접 작성해볼 수 있습니다.

소스 코드를 다운로드하려면 msysgit 같은 Git 소스 제어 클라이언트가 설치되어 있어야 합니다. IronRuby 소스 제어는 온라인에서 구할 수 있습니다. 자세한 내용은 IronRuby 프로젝트 사이트나 필자의 블로그 게시물 "GitHub에서 IronRuby 다운로드하기"를 참조하시기 바랍니다. 소스 코드를 다운로드하고 나면 Visual Studio에서 IronRuby.sln 솔루션 파일을 사용하거나 명령을 사용(MRI가 설치되어 있는 경우)하여 어셈블리를 컴파일할 수 있습니다.

rake compile

IronRuby를 컴파일한 후 추가 기능을 사용하려면 RSpec과 같은 다양한 라이브러리를 다운로드해야 합니다. Ruby에는 RubyGems라는 개념이 있습니다. 여기서 gem은 다운로드하여 기능을 확장하고 추가 종속성을 얻을 수 있는 패키지를 말합니다. RSpec을 다운로드하려면 명령 프롬프트에 다음을 입력하십시오.

gem install rspec

이제 Ruby 코드에서 RSpec 라이브러리에 액세스할 수 있습니다. 그러나 이 기사를 작성할 당시에는 한두 가지 버그 때문에 RSpec이 아직 IronRuby에서 작동하지 않았습니다. 기사가 게시될 때쯤에는 RSpec이 IronRuby에서 문제 없이 작동하리라 믿습니다. RSpec 지원 상태를 확인하려면 RSpec 웹 사이트를 참조하거나 IronRuby 메일 그룹에 가입하시기 바랍니다.

버그가 아직 해결되지 않았다면 필자가 만든 버그가 수정된 이진 파일과 RSpec 라이브러리를 사용하시기 바랍니다. 참고로, 팀에서 문제를 해결한 경우에는 이 버전을 사용하지 마십시오.

올바른 이진 파일이 준비되면 ir.exe라는 IronRuby 인터프리터를 사용하여 코드를 실행할 수 있습니다. 명령줄에서 응용 프로그램을 실행하면 대화형 콘솔이 열립니다. Ruby에서 코드는 한 줄씩 실행되므로 학습이나 디버깅은 물론, 짧은 명령을 실행하여 일상적인 문제를 해결하는 데에도 적합합니다. 파일 이름을 매개 변수로 제공하면 파일 실행 결과가 콘솔에 출력됩니다.

앞으로의 방향

다음 기사에서는 승인 테스트의 개념을 소개하고 고객과 개발자 간의 의사 소통에 얼마나 유용한지를 설명하겠습니다. 또한 .NET 응용 프로그램을 확인하고 시스템의 실행 파일 사양을 작성하는 데 IronRuby 및 RSpec을 사용함으로써 승인 테스트를 자동화하는 방법도 보여 드리겠습니다.

Ben Hall은 소프트웨어 개발과 코드 작성에 대한 열정을 지닌 C# 개발자/테스터로서 영국의 Red Gate Software에서 테스트 엔지니어로 일하고 있습니다. Ben은 수동 테스트와 자동 테스트를 막론하여 다양한 소프트웨어 테스트 방법을 실험하면서 여러 종류의 응용 프로그램을 테스트하는 최적의 방법을 찾는 일을 즐깁니다. C# MVP인 Ben은 블로그 Blog.BenHall.me.uk를 통해 만나볼 수 있습니다.