xspec 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ files = if ARGV.any? {|x| x.length > 0 }
2
+ ARGV
3
+ else
4
+ Dir['spec/**/*_spec.rb']
5
+ end
6
+
7
+ files.each do |f|
8
+ load f
9
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ require 'xspec/assertion_contexts'
4
+
5
+ it 'integrates with rspec' do
6
+ opts = {
7
+ assertion_context: XSpec::AssertionContext.stack {
8
+ include XSpec::AssertionContext::RSpecExpectations
9
+ }
10
+ }
11
+
12
+ context = with_dsl(opts) do
13
+ it 'supports expect an eq' do
14
+ expect(1 + 1).to eq(3)
15
+ end
16
+ end
17
+
18
+ assert_errors_from_run context, [
19
+ "\nexpected: 3\n got: 2\n\n(compared using ==)\n"
20
+ ]
21
+ end
@@ -0,0 +1,39 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'xspec'
5
+
6
+ extend XSpec.dsl(
7
+ notifier: XSpec::Notifier::ColoredDocumentation.new +
8
+ XSpec::Notifier::TimingsAtEnd.new +
9
+ XSpec::Notifier::FailuresAtEnd.new
10
+ )
11
+
12
+ autorun!
13
+
14
+ def assert_errors_from_run(context, expected_error_messages)
15
+ context.run!
16
+
17
+ notifier = context.__xspec_opts.fetch(:notifier)
18
+ assert_equal expected_error_messages, notifier.errors.flatten.map(&:message)
19
+ end
20
+
21
+ def with_dsl(opts, &block)
22
+ c = Class.new do
23
+ notifier = Class.new(XSpec::Notifier::Null) do
24
+ attr_reader :errors
25
+
26
+ def initialize
27
+ @errors = []
28
+ end
29
+
30
+ def evaluate_finish(result)
31
+ @errors << result.errors
32
+ end
33
+ end.new
34
+
35
+ extend XSpec.dsl(opts.merge(notifier: notifier))
36
+
37
+ instance_exec(&block)
38
+ end
39
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'simple assertion context' do
4
+ let(:subject) { Class.new { include XSpec::AssertionContext.stack {
5
+ include XSpec::AssertionContext::Simple
6
+ }}.new }
7
+
8
+ describe 'assert' do
9
+ it 'succeeds if parameter is true' do
10
+ subject.assert(true)
11
+ end
12
+
13
+ it 'fails with default message if parameter is false' do
14
+ begin
15
+ subject.assert(false)
16
+ fail "Assertion did not fail"
17
+ rescue XSpec::AssertionContext::Simple::AssertionFailed => e
18
+ assert_equal "assertion failed", e.message
19
+ end
20
+ end
21
+
22
+ it 'fails with custom message if parameter is false' do
23
+ begin
24
+ subject.assert(false, "nope")
25
+ fail "Assertion did not fail"
26
+ rescue XSpec::AssertionContext::Simple::AssertionFailed => e
27
+ assert_equal "nope", e.message
28
+ end
29
+ end
30
+ end
31
+
32
+ describe 'assert_equal' do
33
+ it 'succeeds if parameters are equal' do
34
+ subject.assert_equal("a", "a")
35
+ end
36
+
37
+ it 'fails if parameters are not equal' do
38
+ begin
39
+ subject.assert_equal("a", "b")
40
+ fail "Assertion did not fail"
41
+ rescue XSpec::AssertionContext::Simple::AssertionFailed => e
42
+ assert_include 'want: "a"', e.message
43
+ assert_include 'got: "b"', e.message
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'fail' do
49
+ it 'always fails' do
50
+ begin
51
+ subject.fail
52
+ assert false, "fail did not fail"
53
+ rescue XSpec::AssertionContext::Simple::AssertionFailed => e
54
+ assert_equal "failed", e.message
55
+ end
56
+ end
57
+
58
+ it 'can fail with a custom message' do
59
+ begin
60
+ subject.fail ":("
61
+ assert false, "fail did not fail"
62
+ rescue XSpec::AssertionContext::Simple::AssertionFailed => e
63
+ assert_equal ":(", e.message
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+
3
+ class LoadedClass
4
+ def instance_method; end
5
+ def self.class_method; end
6
+ end
7
+
8
+ describe 'doubles assertion context' do
9
+ let(:subject) { Class.new { include XSpec::AssertionContext.stack {
10
+ include XSpec::AssertionContext::Doubles
11
+ }}.new }
12
+
13
+ it 'converts double exceptions to failures' do
14
+ result = subject.call(XSpec::UnitOfWork.new(nil, ->{
15
+ raise XSpec::AssertionContext::Doubles::DoubleFailure, "nope"
16
+ }))
17
+ assert_equal "nope", result[0].message
18
+ end
19
+
20
+ describe 'doubles of unloaded classes' do
21
+ it 'allows any method to be expected' do
22
+ assert_equal nil, subject.instance_eval {
23
+ double = instance_double('Bogus')
24
+ expect(double).foo
25
+ double.foo
26
+ }
27
+ end
28
+
29
+ it 'allows any return value to be specified' do
30
+ assert_equal 1, subject.instance_eval {
31
+ double = instance_double('Bogus')
32
+ expect(double).foo("a") { 2 }
33
+ expect(double).foo("b") { 3 }
34
+ double.foo("b") - double.foo("a")
35
+ }
36
+ end
37
+
38
+ it 'requires matching method name' do
39
+ begin
40
+ subject.instance_eval {
41
+ double = instance_double('Bogus')
42
+ expect(double).foo("a")
43
+ double.bar("a")
44
+ }
45
+ fail "no error raised"
46
+ rescue XSpec::AssertionContext::Doubles::DoubleFailure => e
47
+ assert_include "Unexpectedly received", e.message
48
+ assert_include 'bar("a")', e.message
49
+ end
50
+ end
51
+
52
+ it 'requires exact arguments' do
53
+ begin
54
+ subject.instance_eval {
55
+ double = instance_double('Bogus')
56
+ expect(double).foo("a")
57
+ double.foo("b")
58
+ }
59
+ fail "no error raised"
60
+ rescue XSpec::AssertionContext::Doubles::DoubleFailure => e
61
+ assert_include "Unexpectedly received", e.message
62
+ assert_include 'foo("b")', e.message
63
+ end
64
+ end
65
+ end
66
+
67
+ describe 'assert_exhausted' do
68
+ it 'passes if all expectations have been called' do
69
+ assert subject.instance_eval {
70
+ double = instance_double('Bogus')
71
+ expect(double).foo
72
+ double.foo
73
+ assert_exhausted double
74
+ true
75
+ }
76
+ end
77
+
78
+ it 'raises when not all expectations have been called' do
79
+ begin
80
+ subject.instance_eval {
81
+ double = instance_double('Bogus')
82
+ expect(double).foo(1, "abc")
83
+ assert_exhausted double
84
+ }
85
+ fail "no error raised"
86
+ rescue XSpec::AssertionContext::Doubles::DoubleFailure => e
87
+ assert_include "did not receive", e.message
88
+ assert_include 'foo(1, "abc")', e.message
89
+ end
90
+ end
91
+ end
92
+
93
+ describe 'instance_double' do
94
+ describe 'when doubled class is loaded' do
95
+ it 'allows instance methods to be expected' do
96
+ assert subject.instance_eval {
97
+ double = instance_double('LoadedClass')
98
+ expect(double).instance_method { 123 }
99
+ }
100
+ end
101
+
102
+ it 'does not allow non-existing methods to be expected' do
103
+ begin
104
+ assert subject.instance_eval {
105
+ double = instance_double('LoadedClass')
106
+ expect(double).bogus_method { 123 }
107
+ }
108
+ fail "no error raised"
109
+ rescue XSpec::AssertionContext::Doubles::DoubleFailure => e
110
+ assert_include "LoadedClass#bogus_method", e.message
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'class_double' do
117
+ describe 'when doubled class is loaded' do
118
+ it 'allows instance methods to be expected' do
119
+ assert subject.instance_eval {
120
+ double = class_double('LoadedClass')
121
+ expect(double).class_method { 123 }
122
+ }
123
+ end
124
+
125
+ it 'does not allow non-existing methods to be expected' do
126
+ begin
127
+ assert subject.instance_eval {
128
+ double = class_double('LoadedClass')
129
+ expect(double).bogus_method { 123 }
130
+ }
131
+ fail "no error raised"
132
+ rescue XSpec::AssertionContext::Doubles::DoubleFailure => e
133
+ assert_include "LoadedClass.bogus_method", e.message
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ describe 'strict doubles assertion context' do
141
+ let(:subject) { Class.new { include XSpec::AssertionContext.stack {
142
+ include XSpec::AssertionContext::Doubles.with(:strict)
143
+ }}.new }
144
+
145
+ it 'allows doubling of loaded classes' do
146
+ assert subject.instance_double("LoadedClass")
147
+ end
148
+
149
+ it 'prevents doubling of non-existing classes' do
150
+ begin
151
+ subject.instance_double("Bogus")
152
+ fail "no error raised"
153
+ rescue XSpec::AssertionContext::Doubles::DoubleFailure => e
154
+ assert_include "Bogus", e.message
155
+ end
156
+ end
157
+ end
158
+
159
+ describe 'auto-verifying doubles assertion context' do
160
+ let(:subject) { Class.new { include XSpec::AssertionContext.stack {
161
+ include XSpec::AssertionContext::Doubles.with(:auto_verify)
162
+ }}.new }
163
+
164
+ it 'verifies all used instance doubles on successful result' do
165
+ result = subject.call(XSpec::UnitOfWork.new(nil, ->{
166
+ double = instance_double('Bogus')
167
+ expect(double).foo
168
+ }))
169
+
170
+ assert_equal 1, result.length
171
+ assert_include "did not receive", result[0].message
172
+ assert_include "foo()", result[0].message
173
+ end
174
+
175
+ it 'verifies all used class doubles on successful result' do
176
+ result = subject.call(XSpec::UnitOfWork.new(nil, ->{
177
+ double = class_double('Bogus')
178
+ expect(double).foo
179
+ }))
180
+
181
+ assert_equal 1, result.length
182
+ assert_include "did not receive", result[0].message
183
+ assert_include "foo()", result[0].message
184
+ end
185
+
186
+ it 'does not verify doubles if errors occurred' do
187
+ result = subject.call(XSpec::UnitOfWork.new(nil, ->{
188
+ double = instance_double('Bogus')
189
+ expect(double).foo
190
+ fail "nope"
191
+ }))
192
+ assert_equal 1, result.length
193
+ assert_include "nope", result[0].message
194
+ end
195
+
196
+ it 'returns successful result if all doubles are valid' do
197
+ assert_equal [], subject.call(XSpec::UnitOfWork.new(nil, ->{}))
198
+ end
199
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ let(:root_value) { 3 }
4
+
5
+ describe 'let' do
6
+ let(:parent_value) { 1 }
7
+ let(:array) { [] }
8
+
9
+ it('creates an instance method') { assert parent_value == 1 }
10
+
11
+ describe do
12
+ it("inherits from parent") { assert parent_value == 1 }
13
+ end
14
+
15
+ describe do
16
+ let(:parent_value) { 2 }
17
+
18
+ it('allows overriding of parent') { assert parent_value == 2 }
19
+ end
20
+
21
+ describe do
22
+ def parent_value; 2; end
23
+
24
+ it('allows overriding of parent by method') { assert parent_value == 2 }
25
+ end
26
+
27
+ describe do
28
+ it('can be defined after an it') { assert my_value == 2 }
29
+
30
+ let(:my_value) { 2 }
31
+ end
32
+
33
+ it 'works with root context' do
34
+ assert root_value == 3
35
+ end
36
+
37
+ it 'memorizes the value' do
38
+ array << 1
39
+ assert array == [1]
40
+ end
41
+
42
+ it 'resets after each spec' do
43
+ assert array == []
44
+ end
45
+ end
46
+
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ ComposableNotifier = shared_context do
5
+ it 'can be composed with itself' do
6
+ assert (notifier + notifier).run_finish
7
+ end
8
+ end
9
+
10
+ describe 'failures at end notifier' do
11
+ let(:out) { StringIO.new }
12
+ let(:notifier) { XSpec::Notifier::FailuresAtEnd.new(out) }
13
+
14
+ it 'returns true when no errors are observed' do
15
+ assert notifier.run_finish
16
+ end
17
+
18
+ it 'includes full name in failure' do
19
+ failure = XSpec::Failure.new(
20
+ make_nested_test([nil, 'a', nil, 'b'], 'c'),
21
+ "failed",
22
+ []
23
+ )
24
+ notifier.evaluate_finish(make_executed_test errors: [failure])
25
+
26
+ assert !notifier.run_finish
27
+ assert_include "a b c:\n failed", out.string
28
+ end
29
+
30
+ it 'cleans lib entries out of backtrace' do
31
+ failure = XSpec::Failure.new(
32
+ make_nested_test([nil, 'a', nil, 'b'], 'c'),
33
+ "failed",
34
+ [File.expand_path('../../../lib', __FILE__) + '/bogus.rb']
35
+ )
36
+ notifier.evaluate_finish(make_executed_test errors: [failure])
37
+
38
+ assert !notifier.run_finish
39
+ assert !out.string.include?('bogus.rb')
40
+ end
41
+
42
+ it_behaves_like_a ComposableNotifier
43
+ end
44
+
45
+ describe 'character notifier' do
46
+ let(:notifier) { XSpec::Notifier::Character.new(out) }
47
+ let(:out) { StringIO.new }
48
+
49
+ it 'outputs a . for every successful test' do
50
+ notifier.evaluate_finish(make_executed_test)
51
+ notifier.evaluate_finish(make_executed_test)
52
+
53
+ assert notifier.run_finish
54
+ assert out.string == "..\n"
55
+ end
56
+
57
+ it 'outputs a F for every failed test' do
58
+ notifier.evaluate_finish(make_executed_test errors: [make_failure])
59
+
60
+ assert !notifier.run_finish
61
+ assert out.string == "F\n"
62
+ end
63
+
64
+ it 'outputs an E for every errored test' do
65
+ notifier.evaluate_finish(make_executed_test errors: [make_error])
66
+
67
+ assert !notifier.run_finish
68
+ assert out.string == "E\n"
69
+ end
70
+
71
+ it_behaves_like_a ComposableNotifier
72
+ end
73
+
74
+ describe 'documentation notifier' do
75
+ let(:notifier) { XSpec::Notifier::Documentation.new(out) }
76
+ let(:out) { StringIO.new }
77
+
78
+ def evaluate_finish(args)
79
+ notifier.evaluate_finish(make_executed_test args)
80
+ out.string
81
+ end
82
+
83
+ it 'outputs each context with a header and individual tests' do
84
+ assert_equal "\na\n 0.001s b\n",
85
+ evaluate_finish(parents: ['a'], name: 'b')
86
+ end
87
+
88
+ it 'adds an indent for each nested context' do
89
+ assert_equal "\na\n b\n 0.001s c\n",
90
+ evaluate_finish(parents: ['a', 'b'], name: 'c')
91
+ end
92
+
93
+ it 'does not repeat top level parents for multiple nested contexts' do
94
+ evaluate_finish(parents: ['a', 'b'], name: 'c')
95
+ evaluate_finish(parents: ['a', 'd'], name: 'e')
96
+
97
+ assert_equal "\na\n b\n 0.001s c\n\n d\n 0.001s e\n", out.string
98
+ end
99
+
100
+ it 'ignores contexts with no name' do
101
+ assert_equal "\na\n 0.001s b\n",
102
+ evaluate_finish(parents: [nil, 'a', nil], name: 'b')
103
+ end
104
+
105
+ it 'suffixes FAILED to tests when they fail' do
106
+
107
+ assert_include "a - FAILED",
108
+ evaluate_finish(errors: [make_failure], name: 'a')
109
+ end
110
+
111
+ it 'outputs FAILED for unnamed tests when they fail' do
112
+ assert_include "FAILED", evaluate_finish(name: nil, errors: [make_failure])
113
+ end
114
+
115
+ it 'outputs FAILED for unnamed tests when they error' do
116
+ assert_include "FAILED", evaluate_finish(errors: [make_error])
117
+ end
118
+
119
+ it_behaves_like_a ComposableNotifier
120
+ end
121
+
122
+ describe 'colored documentation notifier' do
123
+ let(:notifier) { XSpec::Notifier::ColoredDocumentation.new(out) }
124
+ let(:out) { StringIO.new }
125
+
126
+ it 'colors successful tests green' do
127
+ notifier.evaluate_finish(make_executed_test errors: [])
128
+
129
+ assert_include "\e[32m\e[0m\n", out.string
130
+ end
131
+
132
+ it 'colors failed and errored tests red' do
133
+ notifier.evaluate_finish(make_executed_test errors: [make_failure])
134
+
135
+ assert_include "\e[31mFAILED\e[0m", out.string
136
+ end
137
+
138
+ it_behaves_like_a ComposableNotifier
139
+ end
140
+
141
+ describe 'composable notifier' do
142
+ let(:notifier) { XSpec::Notifier::Composite.new }
143
+
144
+ it_behaves_like_a ComposableNotifier
145
+ end
146
+
147
+ describe 'null notifier' do
148
+ let(:notifier) { XSpec::Notifier::Null.new }
149
+
150
+ it 'always returns true' do
151
+ assert notifier.run_finish
152
+ end
153
+
154
+ it_behaves_like_a ComposableNotifier
155
+ end
156
+
157
+ def make_nested_test(parent_names = [], work_name = nil)
158
+ XSpec::NestedUnitOfWork.new(
159
+ parent_names.map {|name| XSpec::Context.make(name, Module.new) },
160
+ XSpec::UnitOfWork.new(work_name, ->{})
161
+ )
162
+ end
163
+
164
+ def make_executed_test(parents: [], errors: [], duration: 0.001, name: nil)
165
+ XSpec::ExecutedUnitOfWork.new(
166
+ XSpec::NestedUnitOfWork.new(
167
+ parents.map {|name| XSpec::Context.make(name, Module.new) },
168
+ XSpec::UnitOfWork.new(name, ->{})
169
+ ),
170
+ errors,
171
+ duration
172
+ )
173
+ end
174
+
175
+ def make_failure
176
+ failure = XSpec::Failure.new(
177
+ make_nested_test([], 'failure'),
178
+ "failed",
179
+ []
180
+ )
181
+ end
182
+
183
+ def make_error
184
+ failure = XSpec::CodeException.new(
185
+ make_nested_test([], 'failure'),
186
+ "failed",
187
+ []
188
+ )
189
+ end