musts 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --backtrace
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Ryan Bates
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,98 @@
1
+ # Musts
2
+
3
+ An expectation library that adds "must" and "must_not" which can have matchers called on them. Comes with a default set of matchers, and additional matchers can be easily added. Works with RSpec, MiniTest, and Test::Unit. Requires Ruby 1.9 or greater.
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile and run the `bundle` command.
9
+
10
+ ```ruby
11
+ gem 'musts', group: :test
12
+ ```
13
+
14
+
15
+ ## Usage
16
+
17
+ Inside of a test or spec, call `must` or `must_not` on any object followed by a matcher. Some matchers have aliases.
18
+
19
+ ```ruby
20
+ 5.must.equal 5
21
+ 5.must_not.eq 4
22
+
23
+ 5.must.be_greater_than 4
24
+ 5.must.be_lt 6
25
+
26
+ [].must.be :empty? # calls the method to see if it's true
27
+ record.must.be :valid?
28
+ 5.must.be :between?, 6, 7
29
+ # raises Musts::Failure: expected 5 to be between 6 and 7
30
+
31
+ -> { 5.bad_call }.must.raise_exception(NoMethodError)
32
+ ```
33
+
34
+ See the source code for a complete list of matchers and their behavior.
35
+
36
+
37
+ ### Adding Matchers
38
+
39
+ Matchers are very easy to add. If a block is passed, it will be executed in the context of the subject.
40
+
41
+ ```ruby
42
+ Musts.matcher(:be_empty) { empty? }
43
+ [].must.be_empty?
44
+ [1].must.be_empty? # fail: expected [1] to be empty
45
+ ```
46
+
47
+ Alternatively, you can pass a class to fully customize the behavior.
48
+
49
+ ```ruby
50
+ class BetweenMatcher
51
+ # Subject is always passed, any extra arguments will be added after
52
+ def initialize(subject, min, max)
53
+ @subject = subject
54
+ @min = min
55
+ @max = max
56
+ end
57
+
58
+ def match?
59
+ @subject.between? @min, @max
60
+ end
61
+
62
+ def failure_message
63
+ "expected #{@subject.inspect} to be between #{@min.inspect} and #{@max.inspect}."
64
+ end
65
+
66
+ def negative_failure_message
67
+ "expected #{@subject.inspect} to not be between #{@min.inspect} and #{@max.inspect}."
68
+ end
69
+ end
70
+
71
+ Musts.matcher(:be_between, BetweenMatcher)
72
+ 5.must.be_between(5, 7)
73
+ ```
74
+
75
+
76
+ ### Disabling Other Matchers
77
+
78
+ **For RSpec,** add this line to your `spec_helper.rb` if you want to disable other matchers.
79
+
80
+ ```ruby
81
+ config.expect_with Musts
82
+ ```
83
+
84
+ **For MiniTest::Spec,** add this line to your `test_helper.rb` if you want to disable existing matchers.
85
+
86
+ ```ruby
87
+ ENV["MT_NO_EXPECTATIONS"] = "true"
88
+ ```
89
+
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Make changes and ensure they pass with `rspec .`
96
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
97
+ 5. Push to the branch (`git push origin my-new-feature`)
98
+ 6. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,52 @@
1
+ require "musts/version"
2
+ require "musts/object_extension"
3
+ require "musts/failure"
4
+ require "musts/must"
5
+ require "musts/matchers/default_matcher"
6
+ require "musts/matchers/be_matcher"
7
+ require "musts/matchers/close_matcher"
8
+
9
+ module Musts
10
+ def self.matcher(*args, &block)
11
+ Must.matcher(*args, &block)
12
+ end
13
+
14
+ def self.fail(message = nil)
15
+ if defined?(RSpec::Musts::Failure)
16
+ raise RSpec::Musts::Failure.new(message)
17
+ else
18
+ raise Failure.new(message)
19
+ end
20
+ end
21
+
22
+ def fail(*args)
23
+ Musts.fail(*args)
24
+ end
25
+
26
+ matcher(:equal, :eq) { |other| self == other }
27
+ matcher(:match) { |other| self =~ other }
28
+ matcher(:be_true) { self }
29
+ matcher(:be_false) { !self }
30
+ matcher(:be_nil) { nil? }
31
+ matcher(:include) { |*args| include?(*args) }
32
+ matcher(:respond_to) { |*args| respond_to?(*args) }
33
+
34
+ matcher(:be_greater_than, :be_gt) { |other| self > other }
35
+ matcher(:be_greater_than_or_equal_to, :be_gte) { |other| self >= other }
36
+ matcher(:be_less_than, :be_lt) { |other| self < other }
37
+ matcher(:be_less_than_or_equal_to, :be_lte) { |other| self <= other }
38
+
39
+ matcher(:be, BeMatcher)
40
+ matcher(:be_close_to, CloseMatcher)
41
+
42
+ matcher(:raise_exception) do |*args|
43
+ raised = false
44
+ exception = args.first || StandardError
45
+ begin
46
+ call
47
+ rescue exception
48
+ raised = true
49
+ end
50
+ raised
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ module Musts
2
+ if defined?(MiniTest::Assertion)
3
+ class Failure < MiniTest::Assertion; end
4
+ elsif defined?(Test::Unit::AssertionFailedError)
5
+ class Failure < Test::Unit::AssertionFailedError; end
6
+ else
7
+ class Failure < StandardError; end
8
+ end
9
+ end
10
+
11
+ # Kind of silly but the failure class must start with "RSpec"
12
+ # for it to not show up in RSpec output.
13
+ if defined?(RSpec)
14
+ module RSpec
15
+ module Musts
16
+ class Failure < ::Musts::Failure; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Musts
2
+ class BeMatcher
3
+ def initialize(subject, method, *args)
4
+ @subject = subject
5
+ @method = method
6
+ @args = args
7
+ end
8
+
9
+ def match?
10
+ @subject.send(@method, *@args)
11
+ end
12
+
13
+ def failure_message
14
+ "expected #{@subject.inspect} to be #{message_action}"
15
+ end
16
+
17
+ def negative_failure_message
18
+ "expected #{@subject.inspect} to not be #{message_action}"
19
+ end
20
+
21
+ def message_action
22
+ message_action = @method.to_s.sub(/\?$/, "")
23
+ message_action += " " + @args.map(&:inspect).join(" and ") unless @args.empty?
24
+ message_action
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ module Musts
2
+ class CloseMatcher
3
+ def initialize(subject, expected, delta = 0.001)
4
+ @subject = subject
5
+ @expected = expected
6
+ @delta = delta
7
+ end
8
+
9
+ def match?
10
+ @delta >= (@subject - @expected).abs
11
+ end
12
+
13
+ def failure_message
14
+ "expected #{@subject.inspect} to #{message_expected}"
15
+ end
16
+
17
+ def negative_failure_message
18
+ "expected #{@subject.inspect} to not #{message_expected}"
19
+ end
20
+
21
+ def message_expected
22
+ "be within #{@delta.inspect} of #{@expected.inspect}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ module Musts
2
+ class DefaultMatcher
3
+ # This will genereate a subclass of DefaultMatcher to
4
+ # act as the matcher class. This way we can persist the
5
+ # name of the matcher and the block of behavior.
6
+ def self.subclass_for(name, behavior)
7
+ klass = Class.new(self)
8
+ class << klass
9
+ attr_accessor :name, :behavior
10
+ end
11
+ klass.name = name
12
+ klass.behavior = behavior
13
+ klass
14
+ end
15
+
16
+ def initialize(subject, *args)
17
+ @subject = subject
18
+ @args = *args
19
+ end
20
+
21
+ def match?
22
+ @subject.instance_exec(*@args, &self.class.behavior)
23
+ end
24
+
25
+ def failure_message
26
+ "expected #{@subject.inspect} to #{message_action}"
27
+ end
28
+
29
+ def negative_failure_message
30
+ "expected #{@subject.inspect} to not #{message_action}"
31
+ end
32
+
33
+ def message_action
34
+ message_action = self.class.name.to_s.tr("_", " ")
35
+ message_action += " " + @args.map(&:inspect).join(", ") unless @args.empty?
36
+ message_action
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ module Musts
2
+ class Must
3
+ def self.matcher(name, *other_names, &block)
4
+ if block
5
+ matcher_class = DefaultMatcher.subclass_for(name, block)
6
+ else
7
+ matcher_class = other_names.pop
8
+ end
9
+ define_method(name) do |*args|
10
+ _assert matcher_class.new(@subject, *args)
11
+ end
12
+ other_names.each do |other_name|
13
+ alias_method other_name, name
14
+ end
15
+ end
16
+
17
+ def initialize(subject, negate = false)
18
+ @subject = subject
19
+ @negate = negate
20
+ end
21
+
22
+ private
23
+
24
+ def _assert(matcher)
25
+ if @negate == !!matcher.match?
26
+ message = @negate ? matcher.negative_failure_message : matcher.failure_message
27
+ Musts.fail(message)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ module Musts
2
+ module ObjectExtension
3
+ def must
4
+ Must.new(self)
5
+ end
6
+
7
+ def must_not
8
+ Must.new(self, true)
9
+ end
10
+ end
11
+ end
12
+
13
+ Object.send(:include, Musts::ObjectExtension)
@@ -0,0 +1,3 @@
1
+ module Musts
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,9 @@
1
+ require "rubygems"
2
+ require "minitest/autorun"
3
+ require "musts"
4
+
5
+ describe Musts::Failure do
6
+ it "inherits from MiniTest::Assertion" do
7
+ (Musts::Failure < MiniTest::Assertion).must.be_true
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'musts/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "musts"
8
+ gem.version = Musts::VERSION
9
+ gem.authors = ["Ryan Bates"]
10
+ gem.email = ["ryan@railscasts.com"]
11
+ gem.description = "Simple \"must\" matchers for specs."
12
+ gem.summary = "Adds \"must\" and \"must_not\" methods which can have matchers called upon them. Matchers can be added easily."
13
+ gem.homepage = "https://github.com/ryanb/musts"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = gem.files.grep(%r{^spec/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_development_dependency 'rspec', '~> 2.12.0'
21
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ describe Musts do
4
+ it "fails when calling fail with message" do
5
+ failure { fail }
6
+ failure("something went wrong") { fail "something went wrong" }
7
+ end
8
+
9
+ it "raises an exception when match fails" do
10
+ failure { 5.must.equal 4 }
11
+ end
12
+
13
+ it "raises an RSpec::Musts::Failure exception in RSpec" do
14
+ -> { 5.must.eq 4 }.must.raise_exception(RSpec::Musts::Failure)
15
+ end
16
+
17
+ it "does not raise an exception when match succeeds" do
18
+ 5.must.equal 5
19
+ end
20
+
21
+ it "negates with must_not" do
22
+ failure { 5.must_not.equal 5 }
23
+ 5.must_not.equal 4
24
+ end
25
+
26
+ it "has a message when failing" do
27
+ failure("expected 5 to equal 4") { 5.must.equal 4 }
28
+ failure("expected 5 to not equal 5") { 5.must_not.equal 5 }
29
+ failure('expected "5" to equal "4"') { "5".must.equal "4" }
30
+ end
31
+
32
+ it "adds custom matcher" do
33
+ Musts.matcher(:be_empty) { empty? }
34
+ [].must.be_empty
35
+ failure('expected [1] to be empty') { [1].must.be_empty }
36
+ end
37
+
38
+ it "adds custom matcher with alias" do
39
+ Musts.matcher(:be_empty, :be_vacant) { empty? }
40
+ failure('expected [1] to be empty') { [1].must.be_vacant }
41
+ end
42
+
43
+ it "has eq matcher" do
44
+ 5.must.eq 5
45
+ failure("expected 5 to equal 4") { 5.must.eq 4 }
46
+ end
47
+
48
+ it "has be_greater_than matcher" do
49
+ 5.must.be_greater_than 4
50
+ failure("expected 5 to be greater than 5") do
51
+ 5.must.be_greater_than 5
52
+ end
53
+ failure("expected 5 to be greater than 5") do
54
+ 5.must.be_gt 5
55
+ end
56
+ end
57
+
58
+ it "has be_greater_than_or_equal_to matcher" do
59
+ 5.must.be_greater_than_or_equal_to 5
60
+ failure("expected 5 to be greater than or equal to 6") do
61
+ 5.must.be_greater_than_or_equal_to 6
62
+ end
63
+ failure("expected 5 to be greater than or equal to 6") do
64
+ 5.must.be_gte 6
65
+ end
66
+ end
67
+
68
+ it "has be_less_than matcher" do
69
+ 5.must.be_less_than 6
70
+ failure("expected 5 to be less than 5") do
71
+ 5.must.be_less_than 5
72
+ end
73
+ failure("expected 5 to be less than 5") do
74
+ 5.must.be_lt 5
75
+ end
76
+ end
77
+
78
+ it "has be_less_than_or_equal_to matcher" do
79
+ 5.must.be_less_than_or_equal_to 5
80
+ failure("expected 5 to be less than or equal to 4") do
81
+ 5.must.be_less_than_or_equal_to 4
82
+ end
83
+ failure("expected 5 to be less than or equal to 4") do
84
+ 5.must.be_lte 4
85
+ end
86
+ end
87
+
88
+ it "has be matcher" do
89
+ [].must.be :empty?
90
+ failure("expected [1] to be empty") do
91
+ [1].must.be :empty?
92
+ end
93
+ failure("expected 3 to be between 4 and 6") do
94
+ 3.must.be :between?, 4, 6
95
+ end
96
+ end
97
+
98
+ it "has be_true/false/nil matchers" do
99
+ true.must.be_true
100
+ false.must.be_false
101
+ 1.must.be_true
102
+ nil.must.be_false
103
+ nil.must.be_nil
104
+ failure("expected false to be true") do
105
+ false.must.be_true
106
+ end
107
+ failure("expected true to be false") do
108
+ true.must.be_false
109
+ end
110
+ failure("expected false to be nil") do
111
+ false.must.be_nil
112
+ end
113
+ end
114
+
115
+ it "has include matcher" do
116
+ [1, 2, 3].must.include 3
117
+ failure("expected [1, 2, 3] to include 4") do
118
+ [1, 2, 3].must.include 4
119
+ end
120
+ end
121
+
122
+ it "has match matcher" do
123
+ "foobar".must.match /foo/
124
+ failure("expected \"foo\" to match /bar/") do
125
+ "foo".must.match /bar/
126
+ end
127
+ end
128
+
129
+ it "has respond_to matcher" do
130
+ "foobar".must.respond_to :length
131
+ failure("expected \"foo\" to respond to :invalid_method") do
132
+ "foo".must.respond_to :invalid_method
133
+ end
134
+ end
135
+
136
+ it "has be_close_to matcher" do
137
+ 5.00001.must.be_close_to 5.0
138
+ failure("expected 5.01 to be within 0.001 of 5.0") do
139
+ 5.01.must.be_close_to 5.0
140
+ end
141
+ failure("expected 6.0 to be within 0.1 of 5.0") do
142
+ 6.0.must.be_close_to 5.0, 0.1
143
+ end
144
+ end
145
+
146
+ it "has raise_exception matcher" do
147
+ -> { raise "foo" }.must.raise_exception
148
+ -> { raise Musts::Failure }.must.raise_exception(Musts::Failure)
149
+ failure { -> { nil }.must.raise_exception }
150
+ end
151
+
152
+ it "allows custom class for matcher" do
153
+ matcher_class = Struct.new(:subject, :other) do
154
+ def match?
155
+ subject == other
156
+ end
157
+
158
+ def failure_message
159
+ "failure message"
160
+ end
161
+
162
+ def negative_failure_message
163
+ "negative failure message"
164
+ end
165
+ end
166
+
167
+ Musts.matcher(:be_equal, matcher_class)
168
+ 5.must.be_equal 5
169
+ failure("failure message") { 5.must.be_equal 4 }
170
+ failure("negative failure message") { 5.must_not.be_equal 5 }
171
+ end
172
+
173
+ def failure(message = nil)
174
+ failed = false
175
+ begin
176
+ yield
177
+ rescue Musts::Failure => exception
178
+ failed = true
179
+ exception.message.must.equal message if message
180
+ end
181
+ raise "match did not fail" unless failed
182
+ end
183
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'musts'
5
+
6
+ RSpec.configure do |config|
7
+ config.expect_with Musts
8
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: musts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ryan Bates
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.12.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.12.0
46
+ description: Simple "must" matchers for specs.
47
+ email:
48
+ - ryan@railscasts.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - Gemfile
56
+ - LICENSE.txt
57
+ - README.md
58
+ - Rakefile
59
+ - lib/musts.rb
60
+ - lib/musts/failure.rb
61
+ - lib/musts/matchers/be_matcher.rb
62
+ - lib/musts/matchers/close_matcher.rb
63
+ - lib/musts/matchers/default_matcher.rb
64
+ - lib/musts/must.rb
65
+ - lib/musts/object_extension.rb
66
+ - lib/musts/version.rb
67
+ - minitest_test.rb
68
+ - musts.gemspec
69
+ - spec/musts_spec.rb
70
+ - spec/spec_helper.rb
71
+ homepage: https://github.com/ryanb/musts
72
+ licenses: []
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ segments:
84
+ - 0
85
+ hash: 310933228392488255
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ segments:
93
+ - 0
94
+ hash: 310933228392488255
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.23
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Adds "must" and "must_not" methods which can have matchers called upon them.
101
+ Matchers can be added easily.
102
+ test_files:
103
+ - spec/musts_spec.rb
104
+ - spec/spec_helper.rb