Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/usr/local/bin/ruby

# MidiSpec
# A minispec like example framework
# that provides more rspec like functionality.
#
#  Features:
#
#  * fast
#  * nested example groups with 'chained' before and after blocks
#  * 'obj.should matcher' syntax
#  * an easy way to define matchers
#  * 1.9 compatible
#
# There are several examples coming after the framework definition
#
# Based on Ryan Davis' MiniTest/Spec

require "rubygems"
require "minitest/unit"
require "minitest/mock"
MiniTest::Unit.autorun

module MidiSpec
  class ExampleGroup < MiniTest::Unit::TestCase

    def self.desc=(desc)
      @desc = desc
    end

    def self.desc
      @desc || ""
    end

    def self.setup_proc=(proc)
      @setup_proc = proc
    end

    def self.setup_proc
      @setup_proc || lambda {}
    end

    def self.teardown_proc=(proc)
      @teardown_proc = proc
    end

    def self.teardown_proc
      @teardown_proc || lambda {}
    end

    def self.before(type = :each, &block)
      raise "unsupported before type: #{type}" unless type == :each
      passed_through_setup = self.setup_proc
      self.setup_proc = lambda { instance_eval(&passed_through_setup);instance_eval(&block) }
      define_method :setup, &self.setup_proc
    end

    def self.after(type = :each, &block)
      raise "unsupported after type: #{type}" unless type == :each
      passed_through_teardown = self.teardown_proc
      self.teardown_proc = lambda {instance_eval(&block);instance_eval(&passed_through_teardown) }
      define_method :teardown, &self.teardown_proc
    end

    def self.describe desc, &block
      cls = Class.new(ExampleGroup)
      Object.const_set self.name + desc.to_s.split(/\W+/).map { |s| s.capitalize }.join, cls
      cls.setup_proc = self.setup_proc
      cls.teardown_proc = self.teardown_proc
      cls.desc = self.desc + " " + desc
      cls.class_eval(&block)
    end

    def self.it desc, &block
      self.before {}
      define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &block
      self.after {}
    end

    def initialize name
      super
      $current_spec = self
    end

    def mock
      MiniTest::Mock.new
    end
  end

  # Redefine MiniTest::Unit's way of formatting failuremessages
  MiniTest::Unit.class_eval do 
    def puke klass, meth, e
      e = case e
          when MiniTest::Skip then
            @skips += 1
            "Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
          when MiniTest::Assertion then
            @failures += 1
            "Failure:\n #{klass.desc} #{meth.gsub(/^test_/,"").split("_").join(" ")} \n#{e.message}\n"
          else
            @errors += 1
            bt = MiniTest::filter_backtrace(e.backtrace).join("\n    ")
            "Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n    #{bt}\n"
          end
      @report << e
      e[0, 1]
    end
  end

  class OperatorMatcherProxy
    def self.create given, loc, type = true
      body = lambda do |klass|
        define_method(:initialize) do |given|
          @given = given
        end

        ['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
          define_method(operator.to_sym) do |actual|
            print_given  = (@given == nil) ? "nil" : @given
            print_actual = (actual == nil) ? "nil" : actual
            if type 
              msg = """
                    Expected: #{print_actual}
                         got: #{print_given} 
             comparison with: #{operator}
                    """ 
            else
              msg = """
                    Expected not: #{print_actual}
                         but got: #{print_given} 
                 comparison with: #{operator}
                    """
            end
            comparison = type == @given.send(operator.to_sym,actual)
            $current_spec.assert(comparison, msg  + "\n" + "(" + loc + ")")
          end
        end
      end

      return Class.new(&body).new(given)
    end
  end

  Object.class_eval do
    def should matcher = nil
      if matcher
        $current_spec.assert(matcher.matches?(self),matcher.positive_msg + "\n" + "(" + caller[0] + ")")
      else
        OperatorMatcherProxy.create(self,caller[0])
      end
    end

    def should_not matcher = nil
      if matcher 
        $current_spec.assert(!matcher.matches?(self),matcher.negative_msg + "\n" + "(" + caller[0] + ")")
      else 
        OperatorMatcherProxy.create(self,caller[0], false)
      end
    end
  end

  module Extension
    def describe desc, &block
      cls = Class.new(MidiSpec::ExampleGroup)
      Object.const_set desc.to_s.split(/\W+/).map { |s| s.capitalize }.join, cls
      cls.desc = desc
      cls.class_eval(&block)
    end
    private :describe

    def def_matcher(matcher_name, &block)
      self.class.send :define_method, matcher_name do |*args|
        match_block = lambda do |actual, matcher|
          block.call(actual, matcher, args)
        end
        body = lambda do |klass|
          attr_accessor :positive_msg, :negative_msg
          def initialize(match_block)
            @match_block = match_block
            @positive_msg = "Expected "
            @negative_msg = "Unexpected "
          end

          def matches?(given)
            @match_block.call(given, self)
          end
        end
        Class.new(&body).new(match_block)
      end
    end
  end # Extension
end # 
include MidiSpec::Extension

#
### Some examples:
#

# Minimal:
describe "ExampleGroup" do
  it "Example" do
    1.should == 1
  end
end


# With a minimal matcher:
def_matcher :be_nil do |given, matcher, args|
  matcher.positive_msg = "expected #{given} to be nil"
  matcher.negative_msg = "expected not nil"
  given == nil
end

describe "Be nil matcher" do
  it "should pass if given is nil" do
    nil.should be_nil
  end
end


# Nested ExampleGroups
describe "first level" do

  before do
    $first_level_before ||= 0
    $first_level_before += 1
  end

  after do
    $first_level_after ||= 0
    $first_level_after += 1
  end

  it "example in first level" do
    $first_level_after.should be_nil
    $first_level_before.should == 1
  end

  describe "second level" do

    before do
      $second_level_before ||= 0
      $second_level_before += 1
    end

    after do
      $second_level_after ||= 0
      $second_level_after += 1
    end

    it "example in second level" do
      $second_level_after.should == nil
      $first_level_after.should == 1
      $first_level_before.should == 2
      $second_level_before.should == 1
    end

    describe "third level" do
      it "second_level_after should have been called now" do
        $second_level_after.should == 1
      end
    end
  end
end


# A change alike matcher
def_matcher :change do |given, matcher, args|
  raise "callseq: change(lambda {'something here'})" unless Proc === (prok = args[0])
  before = prok.call
  given.call
  before != prok.call
end

describe "Change matcher" do
  before do
    @var = 1
  end

  it "should pass if actual was actually changed by given" do
    lambda {@var = 2}.should change(lambda {@var})
  end

  it "should pass if actual was expectedly not changed by given" do
    lambda { }.should_not change(lambda {@var})
  end
end

# With MiniTest/Mock
describe "with mock" do
  it "should work with minitest/mock" do
    mock_obj = mock.expect(:hello, "Hello", ["there"])
    mock_obj.hello("there").should == "Hello"
  end
end