xspec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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