orangutan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,59 @@
1
+ Orangutan is a mocking library.
2
+
3
+ It allows you to create stub objects and to setup methods with return, yield and raise behaviours.
4
+
5
+ It can be used in testing .net code using ironruby.
6
+
7
+ At this stage only the simplest of clr interfaces can be stubbed.
8
+
9
+ Note that only stub instances of objects are created. It can not create mocks of class methods.
10
+
11
+ It works by recording all method calls so that assertions can be made about them after the fact.
12
+
13
+ This means that orangutan can be used with any testing framework and in combination with other mock frameworks.
14
+
15
+ Installation:
16
+
17
+ gem install markryall-orangutan
18
+
19
+ Usage:
20
+
21
+ require 'orangutan'
22
+ @o = Orangutan::Chantek.new
23
+
24
+ # Creating a pure ruby stub
25
+ @stub = @orangutan.stub :stub
26
+
27
+ # Creating a stub that implements a clr interface
28
+ @clr_stub = @orangutan.stub :clr_stub, :clr_interface => System::IDisposable
29
+
30
+ # Setting up stub methods that return values
31
+ @o.when(:stub).receives(:execute).with(7).return('baz')
32
+
33
+ # Setting up stub methods that yield values
34
+ @o.when(:stub).receives(:execute).with(7).yield('baz')
35
+
36
+ # Setting up stub methods that raise errors
37
+ @o.when(:stub).receives(:execute).with(7).raise('baz')
38
+
39
+ # Checking recorded method calls
40
+ # at this stage you need to scan the array of call structs recorded in @o.calls
41
+ @o.calls[0].should == Orangutan::Call.new(:stub, :execute, [7])
42
+
43
+ Questions:
44
+
45
+ * Why Orangutan?
46
+
47
+ My daughter likes orangutans and I couldn't think of a better name. Both Sumatran and Bornean orangutans are endangered species so even if you intensely dislike this project or its implementation, at least you can be made aware of the plight of these spectacular creatures.
48
+
49
+ * Did it have anything to do with Clyde in "Every Which Way But Loose"
50
+
51
+ Definately not. What a ridiculous question. I'm appalled.
52
+
53
+ * What's Chantek?
54
+
55
+ Chantek is a famous orangutan that can solve sudokus and the rubik's cube - http://en.wikipedia.org/wiki/Chantek
56
+
57
+ * Why do I not need to register this as a mock framework?
58
+
59
+ Most frameworks such as rspec's mocks, mocha and stubba replace methods on existing classes. Because this can't be done with this library - you can only create pure stub objects and setup method behaviours, there's nothing that needs to be undone at the end of a test/example.
@@ -0,0 +1,34 @@
1
+ desc 'build necessary assemblies for tests'
2
+ task 'spec/ClassLibrary.dll' => FileList["spec/**/*.cs"] do
3
+ system "csc /target:library /out:spec\\ClassLibrary.dll spec\\*.cs"
4
+ end
5
+
6
+ task :compile => 'spec/ClassLibrary.dll'
7
+
8
+ desc 'run specs with bacon on ironruby'
9
+ task :bacon => :compile do
10
+ system "ibacon -Ispec -a"
11
+ end
12
+
13
+ (1..4).each do |i|
14
+ desc "run spike #{i}"
15
+ task "spike#{i}" do
16
+ system "ir -I spec -I spikes spikes\\experiment#{i}.rb"
17
+ end
18
+ end
19
+
20
+ begin
21
+ require 'jeweler'
22
+ Jeweler::Tasks.new do |gemspec|
23
+ gemspec.name = "orangutan"
24
+ gemspec.summary = "A mock objects library"
25
+ gemspec.email = "mark@ryall.name"
26
+ gemspec.homepage = "http://github.com/markryall/orangutan"
27
+ gemspec.description = "A mocking library that supports creation of ironruby mock objects (in addition to pure ruby ones)"
28
+ gemspec.files = FileList["[A-Z]*", "{lib,spec}/**/*.{rb,cs}"]
29
+ gemspec.authors = ["Mark Ryall"]
30
+ gemspec.rubyforge_project = 'orangutan'
31
+ end
32
+ rescue LoadError
33
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
34
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,3 @@
1
+ module Orangutan
2
+ end
3
+ require 'orangutan/chantek'
@@ -0,0 +1 @@
1
+ Orangutan::Call = Struct.new(:name, :method, :args)
@@ -0,0 +1,68 @@
1
+ require 'orangutan/clean_slate'
2
+ require 'orangutan/expectation'
3
+ require 'orangutan/call'
4
+
5
+ class Orangutan::Chantek
6
+ attr_reader :calls
7
+
8
+ def initialize
9
+ @calls = []
10
+ @expectations = {}
11
+ end
12
+
13
+ def stub name, params={}
14
+ c = Class.new(Orangutan::CleanSlate) do
15
+ if params[:clr_interface]
16
+ include params[:clr_interface]
17
+ params[:clr_interface].to_clr_type.get_methods.each do |m_info|
18
+ snake = m_info.name.scan(/[A-Z][a-z0-9]*/).map {|a|a.downcase}.join('_').to_sym
19
+ define_method snake do |*args|
20
+ yield_container, return_value, raiser = __react__ snake, args
21
+ raiser.execute if raiser
22
+ yield yield_container.value if yield_container && block_given?
23
+ return_value
24
+ end
25
+ end
26
+ end
27
+
28
+ def initialize name, parent
29
+ @name, @parent = name, parent
30
+ end
31
+
32
+ def method_missing method, *args
33
+ yield_container, return_value, raiser = __react__ method, args
34
+ raiser.execute if raiser
35
+ yield yield_container.value if yield_container && block_given?
36
+ return_value
37
+ end
38
+
39
+ private
40
+
41
+ def __react__ method, args
42
+ yield_container, return_value, raiser = nil, nil, nil
43
+ @parent.calls << Orangutan::Call.new(@name, method, args)
44
+ first_match = @parent.first_match(@name, method, args)
45
+ return first_match.yield_container, first_match.return_value, first_match.raiser if first_match
46
+ return yield_container, return_value, raiser
47
+ end
48
+ end
49
+ c.new name, self
50
+ end
51
+
52
+ def when name
53
+ expectations_for_name = @expectations[name]
54
+ @expectations[name] = expectations_for_name = [] unless expectations_for_name
55
+ expectation = Orangutan::Expectation.new
56
+ expectations_for_name << expectation
57
+ expectation
58
+ end
59
+
60
+ def first_match name, method, args
61
+ expectations_for_name = @expectations[name]
62
+ if expectations_for_name
63
+ expectations_for_name.each do |expectation|
64
+ return expectation if expectation.matches?(method, *args)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ class Orangutan::CleanSlate
2
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
3
+ end
@@ -0,0 +1 @@
1
+ Orangutan::Container = Struct.new(:value)
@@ -0,0 +1,37 @@
1
+ require 'orangutan/raiser'
2
+ require 'orangutan/container'
3
+
4
+ class Orangutan::Expectation
5
+ attr_reader :return_value, :yield_container, :raiser
6
+
7
+ def receives method
8
+ @method = method
9
+ self
10
+ end
11
+
12
+ def with *args
13
+ @args = args
14
+ self
15
+ end
16
+
17
+ def return value
18
+ @return_value = value
19
+ self
20
+ end
21
+
22
+ def yield value
23
+ @yield_container = Orangutan::Container.new value
24
+ self
25
+ end
26
+
27
+ def raise *args
28
+ @raiser = Orangutan::Raiser.new args
29
+ self
30
+ end
31
+
32
+ def matches? method, *args
33
+ return false unless method == @method
34
+ return true unless @args
35
+ @args == args
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ class Orangutan::Raiser
2
+ def initialize args
3
+ @args = args
4
+ end
5
+
6
+ def execute
7
+ puts 'RAISING ' + @args.inspect
8
+ raise *@args
9
+ end
10
+ end
@@ -0,0 +1,62 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{orangutan}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Mark Ryall"]
9
+ s.date = %q{2009-05-09}
10
+ s.description = %q{A mocking library that supports creation of ironruby mock objects (in addition to pure ruby ones)}
11
+ s.email = %q{mark@ryall.name}
12
+ s.extra_rdoc_files = [
13
+ "README"
14
+ ]
15
+ s.files = [
16
+ "README",
17
+ "Rakefile",
18
+ "VERSION",
19
+ "lib/orangutan.rb",
20
+ "lib/orangutan/call.rb",
21
+ "lib/orangutan/chantek.rb",
22
+ "lib/orangutan/clean_slate.rb",
23
+ "lib/orangutan/container.rb",
24
+ "lib/orangutan/expectation.rb",
25
+ "lib/orangutan/raiser.rb",
26
+ "orangutan.gemspec",
27
+ "prepare.cmd",
28
+ "spec/ClassWithANonVirtualMethod.cs",
29
+ "spec/ClassWithANonVirtualProperty.cs",
30
+ "spec/ClassWithAVirtualMethod.cs",
31
+ "spec/ClassWithAVirtualProperty.cs",
32
+ "spec/Consumer.cs",
33
+ "spec/IHaveAMethod.cs",
34
+ "spec/IHaveAProperty.cs",
35
+ "spec/IHaveAnEvent.cs",
36
+ "spec/spec_chantek.rb",
37
+ "spec/spec_chantek_clr.rb",
38
+ "spec/spec_expectation.rb"
39
+ ]
40
+ s.has_rdoc = true
41
+ s.homepage = %q{http://github.com/markryall/orangutan}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubyforge_project = %q{orangutan}
45
+ s.rubygems_version = %q{1.3.1}
46
+ s.summary = %q{A mock objects library}
47
+ s.test_files = [
48
+ "spec/spec_chantek.rb",
49
+ "spec/spec_chantek_clr.rb",
50
+ "spec/spec_expectation.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 2
56
+
57
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
+ else
59
+ end
60
+ else
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ call path_git
2
+ call path_bacon
3
+ call path_ironruby
4
+ call path_msbuild
5
+ pushd %BACON_HOME%
6
+ git pull
7
+ popd
8
+ pushd %IRONRUBY_HOME%
9
+ git pull
10
+ msbuild Merlin\Main\Languages\Ruby\Ruby.sln
11
+ popd
@@ -0,0 +1,13 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public class ClassWithANonVirtualMethod : IHaveAMethod
6
+ {
7
+ public bool MyMethod(string parameter)
8
+ {
9
+ Console.WriteLine("clr method was called with " + parameter);
10
+ return true;
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public class ClassWithANonVirtualProperty : IHaveAProperty
6
+ {
7
+ private string _MyProperty;
8
+ public string MyProperty
9
+ {
10
+ get
11
+ {
12
+ Console.WriteLine("clr getter called");
13
+ return _MyProperty;
14
+ }
15
+ set
16
+ {
17
+ Console.WriteLine("clr setter called");
18
+ _MyProperty = value;
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public class ClassWithAnVirtualMethod : IHaveAMethod
6
+ {
7
+ public virtual bool MyMethod(string parameter)
8
+ {
9
+ Console.WriteLine("clr method was called with " + parameter);
10
+ return true;
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public class ClassWithAVirtualProperty : IHaveAProperty
6
+ {
7
+ private string _MyProperty;
8
+ public virtual string MyProperty
9
+ {
10
+ get
11
+ {
12
+ Console.WriteLine("clr getter called");
13
+ return _MyProperty;
14
+ }
15
+ set
16
+ {
17
+ Console.WriteLine("clr setter called");
18
+ _MyProperty = value;
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,30 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public class Consumer
6
+ {
7
+ private int count = 0;
8
+
9
+ public void CallMethod(IHaveAMethod consumable)
10
+ {
11
+ consumable.MyMethod("thing");
12
+ }
13
+
14
+ public void RegisterEvent(IHaveAnEvent e)
15
+ {
16
+ e.MyEvent += (s,ev) => Console.WriteLine(s);
17
+ }
18
+
19
+ public void CallSetter(IHaveAProperty p)
20
+ {
21
+ p.MyProperty = ""+count;
22
+ count++;
23
+ }
24
+
25
+ public void CallGetter(IHaveAProperty p)
26
+ {
27
+ Console.WriteLine(p.MyProperty);
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,9 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public interface IHaveAMethod
6
+ {
7
+ bool MyMethod(string parameter);
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public interface IHaveAProperty
6
+ {
7
+ String MyProperty { get; set; }
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ using System;
2
+
3
+ namespace ClassLibrary
4
+ {
5
+ public interface IHaveAnEvent
6
+ {
7
+ event EventHandler<EventArgs> MyEvent;
8
+ }
9
+ }
@@ -0,0 +1,61 @@
1
+ require 'orangutan'
2
+
3
+ describe Orangutan::Chantek, 'creating ruby stubs' do
4
+ before do
5
+ @o = Orangutan::Chantek.new
6
+ @foo = @o.stub :foo
7
+ end
8
+
9
+ it 'should record method calls and their parameters on a stub' do
10
+ @foo.method1(1,2,3)
11
+ @o.calls[0].should == Orangutan::Call.new(:foo, :method1, [1,2,3])
12
+ end
13
+
14
+ it 'should record all ordinary object methods calls (except __send__ and __id__)' do
15
+ methods = Object.new.methods
16
+ methods.each { |m| @foo.__send__(m.to_sym) unless m =~ /^__/ }
17
+ @o.calls.length.should == (methods.length-2)
18
+ end
19
+
20
+ it 'should allow method return values to be stubbed' do
21
+ @o.when(:foo).receives(:bar).return('baz')
22
+ @foo.bar.should == 'baz'
23
+ end
24
+
25
+ it 'should allow method return values to be stubbed for method invocations with specific arguments' do
26
+ @o.when(:foo).receives(:bar).with(7).return('baz')
27
+ @foo.bar(7).should == 'baz'
28
+ end
29
+
30
+ it 'should allow stubbed methods to yield' do
31
+ @o.when(:foo).receives(:bar).with(7).yield('baz')
32
+ a = nil
33
+ @foo.bar(7) {|s| a = s}
34
+ a.should == 'baz'
35
+ end
36
+
37
+ it 'should allow stubbed methods to yield nils' do
38
+ @o.when(:foo).receives(:bar).yield(nil)
39
+ called = false
40
+ @foo.bar {|s| called = true}
41
+ called.should == true
42
+ end
43
+
44
+ it 'should allow stubbed methods to raise error from string' do
45
+ @o.when(:foo).receives(:bar).raise("you can't be serious")
46
+ begin
47
+ @foo.bar
48
+ rescue RuntimeError => e
49
+ e.message.should == "you can't be serious"
50
+ end
51
+ end
52
+
53
+ it 'should allow stubbed methods to raise error from string' do
54
+ @o.when(:foo).receives(:bar).raise(RuntimeError, "you can't be serious")
55
+ begin
56
+ @foo.bar
57
+ rescue RuntimeError => e
58
+ e.message.should == "you can't be serious"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,12 @@
1
+ require 'orangutan'
2
+ require 'ClassLibrary.dll'
3
+
4
+ describe Orangutan::Chantek, 'creating clr stubs' do
5
+ it 'should create stub implementations of a clr interface with a method' do
6
+ o = Orangutan::Chantek.new
7
+ s = o.stub :foo, :clr_interface => ClassLibrary::IHaveAMethod
8
+ c = ClassLibrary::Consumer.new
9
+ c.call_method(s)
10
+ o.calls[0].should == Orangutan::Call.new(:foo, :my_method, ['thing'])
11
+ end
12
+ end
@@ -0,0 +1,51 @@
1
+ require 'orangutan'
2
+
3
+ describe Orangutan::Expectation do
4
+ before do
5
+ @e = Orangutan::Expectation.new.receives(:foo)
6
+ end
7
+
8
+ it 'should not match when method is different' do
9
+ @e.matches?(:bar).should == false
10
+ end
11
+
12
+ it 'should match when method matches and args are unspecified' do
13
+ @e.matches?(:foo).should == true
14
+ end
15
+
16
+ it 'should match when method matches and args match' do
17
+ @e.with(7).matches?(:foo, 7).should == true
18
+ end
19
+
20
+ it 'should not match when method matches but args do not match' do
21
+ @e.with(7).matches?(:foo, 8).should == false
22
+ end
23
+
24
+ it 'should not yield by default' do
25
+ @e.yield_container.should == nil
26
+ end
27
+
28
+ it 'should yield when given a value to yield' do
29
+ @e.yield(1).yield_container.should.not == nil
30
+ end
31
+
32
+ it 'should yield when given a nil value to yield' do
33
+ @e.yield(nil).yield_container.should.not == nil
34
+ end
35
+
36
+ it 'should store yield_value' do
37
+ @e.yield(1).yield_container.value.should == 1
38
+ end
39
+
40
+ it 'should store return value' do
41
+ @e.return(1).return_value.should == 1
42
+ end
43
+
44
+ it 'should not raise by default' do
45
+ @e.raiser.should == nil
46
+ end
47
+
48
+ it 'should store raiser' do
49
+ @e.raise('description').raiser.should.not == nil
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: orangutan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mark Ryall
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-09 00:00:00 +10:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A mocking library that supports creation of ironruby mock objects (in addition to pure ruby ones)
17
+ email: mark@ryall.name
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - VERSION
28
+ - lib/orangutan.rb
29
+ - lib/orangutan/call.rb
30
+ - lib/orangutan/chantek.rb
31
+ - lib/orangutan/clean_slate.rb
32
+ - lib/orangutan/container.rb
33
+ - lib/orangutan/expectation.rb
34
+ - lib/orangutan/raiser.rb
35
+ - orangutan.gemspec
36
+ - prepare.cmd
37
+ - spec/ClassWithANonVirtualMethod.cs
38
+ - spec/ClassWithANonVirtualProperty.cs
39
+ - spec/ClassWithAVirtualMethod.cs
40
+ - spec/ClassWithAVirtualProperty.cs
41
+ - spec/Consumer.cs
42
+ - spec/IHaveAMethod.cs
43
+ - spec/IHaveAProperty.cs
44
+ - spec/IHaveAnEvent.cs
45
+ - spec/spec_chantek.rb
46
+ - spec/spec_chantek_clr.rb
47
+ - spec/spec_expectation.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/markryall/orangutan
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --charset=UTF-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project: orangutan
72
+ rubygems_version: 1.3.3
73
+ signing_key:
74
+ specification_version: 2
75
+ summary: A mock objects library
76
+ test_files:
77
+ - spec/spec_chantek.rb
78
+ - spec/spec_chantek_clr.rb
79
+ - spec/spec_expectation.rb