test_construct 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -0
- data/README.md +73 -0
- data/examples/foobar_spec.rb +34 -0
- data/examples/foobar_test.rb +38 -0
- data/lib/test_construct/helpers.rb +50 -11
- data/lib/test_construct/pathname_extensions.rb +35 -3
- data/lib/test_construct/rspec_integration.rb +53 -0
- data/lib/test_construct/version.rb +1 -1
- data/test/test_construct_test.rb +67 -1
- data/test_construct.gemspec +1 -0
- metadata +22 -2
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# TestConstruct Changelog
|
2
|
+
|
3
|
+
## v2.0.0
|
4
|
+
|
5
|
+
Enhancements:
|
6
|
+
|
7
|
+
* Add RSpec 2 support (@avdi)
|
8
|
+
* Option to persist directories after test run or after failure (@avdi)
|
9
|
+
* Add naming of created directories (@avdi)
|
10
|
+
|
11
|
+
Other:
|
12
|
+
|
13
|
+
* Non-backwards-compatible change to `TestConstruct::Helpers#create_construct` method signature (takes option hash now)
|
data/README.md
CHANGED
@@ -142,6 +142,79 @@ within_construct(:chdir => false) do |construct|
|
|
142
142
|
end
|
143
143
|
```
|
144
144
|
|
145
|
+
### Keeping directories around
|
146
|
+
|
147
|
+
You may find it convenient to keep the created directory around after a test has completed, in order to manually inspect its contents.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
within_construct do |construct|
|
151
|
+
# ...
|
152
|
+
construct.keep
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
Most likely you only want the directory to stick around if something goes wrong. To do this, use the `:keep_on_error` option.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
within_construct(keep_on_error: true) do |construct|
|
160
|
+
# ...
|
161
|
+
raise "some error"
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
TestConstruct will also annotate the exception error message to tell you where the generated files can be found.
|
166
|
+
|
167
|
+
### Setting the base directory
|
168
|
+
|
169
|
+
By default, TestConstruct puts its temporary container directories in your system temp dir. You can change this with the `:base_dir` option:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
tmp_dir = File.expand_path("../../tmp", __FILE__)
|
173
|
+
within_construct(base_dir: tmp_dir) do |construct|
|
174
|
+
construct.file("foo.txt")
|
175
|
+
# Passes
|
176
|
+
assert File.exists?(tmp_dir+"/foo.txt")
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
### Naming the created directories
|
181
|
+
|
182
|
+
Normally TestConstruct names the container directories it creates using a combination of a `test-construct-` prefix, the current process ID, and a random number. This ensures that the name is unlikely to clash with directories left over from previous runs. However, it isn't very meaningful. You can optionally make the directory names more recognizable by specifying a `:name` option. TestConstruct will take the string passed, turn it into a normalized "slug" without any funny characters, and append it to the end of the generated dirname.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
within_construct(name: "My best test ever!") do |construct|
|
186
|
+
# will generate something like:
|
187
|
+
# /tmp/construct-container-1234-5678-my-best-test-ever
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
### RSpec Integration
|
192
|
+
|
193
|
+
TestConstruct comes with RSpec integration. Just require the `test_construct/rspec_integration` file in your `spec_helper.rb` or in your spec file. Then tag the tests you want to execute in the context of a construct container with `test_construct: true` using RSpec metadata:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
require "test_construct/rspec_integration"
|
197
|
+
|
198
|
+
describe Foo, test_construct: true do
|
199
|
+
it "should do stuff" do
|
200
|
+
example.metadata[:construct].file "somefile"
|
201
|
+
example.metadata[:construct].directory "somedir"
|
202
|
+
# ...
|
203
|
+
end
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
By default, the current working directory will be switched to the construct container within tests; the container name will be derived from the name of the current example; and if a test fails, the container will be kept around. Information about where to find it will be added to the test failure message.
|
208
|
+
|
209
|
+
You can tweak any TestConstruct options by passing a hash as the value of the `:test_construct` metadata key.
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
require "test_construct/rspec_integration"
|
213
|
+
|
214
|
+
describe Foo, test_construct: {keep_on_error: false} do
|
215
|
+
# ...
|
216
|
+
end
|
217
|
+
```
|
145
218
|
|
146
219
|
## Contributing
|
147
220
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Run with:
|
2
|
+
# rspec -Ilib examples/foobar_spec.rb
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'test_construct/rspec_integration'
|
6
|
+
|
7
|
+
describe "Foobar", test_construct: true do
|
8
|
+
|
9
|
+
it "creates file" do
|
10
|
+
example.metadata[:construct].directory "alice/rabbithole" do |d|
|
11
|
+
d.file "white_rabbit.txt", "I'm late!"
|
12
|
+
File.read("white_rabbit.txt").should == "I'm late!"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "leaves file on error" do
|
17
|
+
example.metadata[:construct].directory "alice/rabbithole" do |d|
|
18
|
+
d.file "white_rabbit.txt", "I'm late!"
|
19
|
+
raise "Whoops"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "Foobar", test_construct: { keep_on_error: false} do
|
26
|
+
|
27
|
+
it "doesn't leave file on error" do
|
28
|
+
example.metadata[:construct].directory "alice/rabbithole" do |d|
|
29
|
+
d.file "white_rabbit.txt", "I'm late!"
|
30
|
+
raise "Whoops"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Run with:
|
2
|
+
# ruby -Ilib examples/foobar_test.rb
|
3
|
+
|
4
|
+
require 'test_construct'
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
class FoobarTest < Test::Unit::TestCase
|
8
|
+
include TestConstruct::Helpers
|
9
|
+
|
10
|
+
def test_directory_and_files
|
11
|
+
within_construct do |c|
|
12
|
+
c.directory 'alice/rabbithole' do |d|
|
13
|
+
d.file 'white_rabbit.txt', "I'm late!"
|
14
|
+
|
15
|
+
assert_equal "I'm late!", File.read('white_rabbit.txt')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_keeping_directory_on_error
|
21
|
+
within_construct(keep_on_error: true) do |c|
|
22
|
+
c.directory 'd' do |d|
|
23
|
+
d.file 'doughnut.txt'
|
24
|
+
raise "whoops"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_deleting_directory_on_error
|
30
|
+
within_construct(keep_on_error: false) do |c|
|
31
|
+
c.directory 'd' do |d|
|
32
|
+
d.file 'doughnut.txt'
|
33
|
+
raise "whoops"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -1,27 +1,66 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'pathname'
|
3
|
+
|
1
4
|
module TestConstruct
|
2
5
|
|
3
6
|
module Helpers
|
4
7
|
extend self
|
5
8
|
|
6
9
|
def within_construct(opts = {})
|
7
|
-
|
8
|
-
container
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
container = setup_construct(opts)
|
11
|
+
yield(container)
|
12
|
+
rescue Exception => error
|
13
|
+
raise unless container
|
14
|
+
teardown_construct(container, error, opts)
|
15
|
+
raise error
|
16
|
+
else
|
17
|
+
teardown_construct(container, nil, opts)
|
14
18
|
end
|
15
19
|
|
16
|
-
def create_construct(
|
17
|
-
|
18
|
-
|
20
|
+
def create_construct(opts = {})
|
21
|
+
chdir_default = opts.delete(:chdir) { true }
|
22
|
+
base_path = Pathname(opts.delete(:base_dir) { TestConstruct.tmpdir })
|
23
|
+
name = opts.delete(:name) { "" }
|
24
|
+
slug = name.downcase.tr_s("^a-z0-9", "-")[0..63]
|
25
|
+
if opts.any?
|
26
|
+
raise "[TestConstruct] Unrecognized options: #{opts.keys}"
|
27
|
+
end
|
28
|
+
dir = "#{CONTAINER_PREFIX}-#{$PROCESS_ID}-#{rand(1_000_000_000)}"
|
29
|
+
dir << "-" << slug unless slug.empty?
|
30
|
+
path = base_path + dir
|
19
31
|
path.mkpath
|
20
32
|
path.extend(PathnameExtensions)
|
21
|
-
path.construct__chdir_default =
|
33
|
+
path.construct__chdir_default = chdir_default
|
22
34
|
path
|
23
35
|
end
|
24
36
|
|
37
|
+
# THIS METHOD MAY HAVE EXTERNAL SIDE-EFFECTS, including:
|
38
|
+
# - creating the container directory tree
|
39
|
+
# - changing the current working directory
|
40
|
+
#
|
41
|
+
# It is intended to be paired with #teardown_construct
|
42
|
+
def setup_construct(opts = {})
|
43
|
+
opts = opts.dup
|
44
|
+
chdir = opts.fetch(:chdir, true)
|
45
|
+
opts.delete(:keep_on_error) { false } # not used in setup
|
46
|
+
container = create_construct(opts)
|
47
|
+
container.maybe_change_dir(chdir)
|
48
|
+
container
|
49
|
+
end
|
50
|
+
|
51
|
+
# THIS METHOD MAY HAVE EXTERNAL SIDE-EFFECTS, including:
|
52
|
+
# - removing the container directory tree
|
53
|
+
# - changing the current working directory
|
54
|
+
# - modifying any exception passed as `error`
|
55
|
+
#
|
56
|
+
# It is intended to be paired with #setup_construct
|
57
|
+
def teardown_construct(container, error = nil, opts = {})
|
58
|
+
if error && opts[:keep_on_error]
|
59
|
+
container.keep
|
60
|
+
container.annotate_exception!(error)
|
61
|
+
end
|
62
|
+
container.finalize
|
63
|
+
end
|
25
64
|
end
|
26
65
|
|
27
66
|
extend Helpers
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module TestConstruct
|
2
2
|
module PathnameExtensions
|
3
3
|
|
4
|
-
attr_accessor :construct__chdir_default
|
5
|
-
|
4
|
+
attr_accessor :construct__chdir_default, :construct__root, :construct__orig_dir
|
6
5
|
def directory(path, opts = {})
|
7
6
|
chdir = opts.fetch(:chdir, construct__chdir_default)
|
8
7
|
subdir = (self + path)
|
9
8
|
subdir.mkpath
|
10
9
|
subdir.extend(PathnameExtensions)
|
10
|
+
subdir.construct__root = construct__root || self
|
11
11
|
subdir.maybe_change_dir(chdir) do
|
12
12
|
yield(subdir) if block_given?
|
13
13
|
end
|
@@ -33,9 +33,16 @@ module TestConstruct
|
|
33
33
|
|
34
34
|
def maybe_change_dir(chdir, &block)
|
35
35
|
if(chdir)
|
36
|
+
self.construct__orig_dir ||= Pathname.pwd
|
36
37
|
self.chdir(&block)
|
37
38
|
else
|
38
|
-
block.call
|
39
|
+
block.call if block
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def revert_cwd
|
44
|
+
if construct__orig_dir
|
45
|
+
Dir.chdir(construct__orig_dir)
|
39
46
|
end
|
40
47
|
end
|
41
48
|
|
@@ -49,5 +56,30 @@ module TestConstruct
|
|
49
56
|
rmtree
|
50
57
|
end
|
51
58
|
|
59
|
+
def finalize
|
60
|
+
revert_cwd
|
61
|
+
destroy! unless keep?
|
62
|
+
end
|
63
|
+
|
64
|
+
def keep
|
65
|
+
if construct__root
|
66
|
+
construct__root.keep
|
67
|
+
else
|
68
|
+
@keep = true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def keep?
|
73
|
+
defined?(@keep) && @keep
|
74
|
+
end
|
75
|
+
|
76
|
+
def annotate_exception!(error)
|
77
|
+
error.message << exception_message_annotation
|
78
|
+
error
|
79
|
+
end
|
80
|
+
|
81
|
+
def exception_message_annotation
|
82
|
+
"\nTestConstruct files kept at: #{self}"
|
83
|
+
end
|
52
84
|
end
|
53
85
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "test_construct"
|
2
|
+
require "rspec"
|
3
|
+
module TestConstruct
|
4
|
+
module RSpecIntegration
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# the :test_construct metadata key can be either:
|
8
|
+
# - true (for all defaults)
|
9
|
+
# - a Hash of options
|
10
|
+
# - false/missing (disable the construct for this test)
|
11
|
+
def test_construct_options(example)
|
12
|
+
options = test_construct_default_options
|
13
|
+
options[:name] = example.full_description
|
14
|
+
metadata_options = example.metadata[:test_construct]
|
15
|
+
if metadata_options.is_a?(Hash)
|
16
|
+
options.merge!(metadata_options)
|
17
|
+
end
|
18
|
+
options
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_construct_enabled?(example)
|
22
|
+
!!example.metadata[:test_construct]
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_construct_default_options
|
26
|
+
{
|
27
|
+
base_dir: TestConstruct.tmpdir,
|
28
|
+
chdir: true,
|
29
|
+
keep_on_error: true,
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec.configure do |config|
|
36
|
+
config.include TestConstruct::Helpers
|
37
|
+
config.include TestConstruct::RSpecIntegration
|
38
|
+
|
39
|
+
config.before :each do
|
40
|
+
next unless test_construct_enabled?(example)
|
41
|
+
options = test_construct_options(example)
|
42
|
+
example.metadata[:construct] = setup_construct(options)
|
43
|
+
end
|
44
|
+
|
45
|
+
config.after :each do
|
46
|
+
next unless test_construct_enabled?(example)
|
47
|
+
options = test_construct_options(example)
|
48
|
+
teardown_construct(
|
49
|
+
example.metadata[:construct],
|
50
|
+
example.exception,
|
51
|
+
options)
|
52
|
+
end
|
53
|
+
end
|
data/test/test_construct_test.rb
CHANGED
@@ -4,6 +4,7 @@ class TestConstructTest < Minitest::Test
|
|
4
4
|
include TestConstruct::Helpers
|
5
5
|
|
6
6
|
def teardown
|
7
|
+
Dir.chdir File.expand_path("../..", __FILE__)
|
7
8
|
TestConstruct.destroy_all!
|
8
9
|
end
|
9
10
|
|
@@ -464,7 +465,6 @@ Contents
|
|
464
465
|
end
|
465
466
|
|
466
467
|
testing "#destroy!" do
|
467
|
-
|
468
468
|
test "removes the construct container" do
|
469
469
|
it = create_construct
|
470
470
|
it.destroy!
|
@@ -472,4 +472,70 @@ Contents
|
|
472
472
|
end
|
473
473
|
end
|
474
474
|
|
475
|
+
testing "#finalize" do
|
476
|
+
test "removes the construct container" do
|
477
|
+
it = create_construct
|
478
|
+
it.finalize
|
479
|
+
assert !File.exist?(it.to_s)
|
480
|
+
end
|
481
|
+
|
482
|
+
test "leaves the container if keep is flagged" do
|
483
|
+
it = create_construct
|
484
|
+
it.keep
|
485
|
+
it.finalize
|
486
|
+
assert File.exist?(it.to_s)
|
487
|
+
end
|
488
|
+
|
489
|
+
test "leaves the container if keep is flagged in a subdir" do
|
490
|
+
it = create_construct
|
491
|
+
subdir = it.directory "subdir"
|
492
|
+
subdir.keep
|
493
|
+
it.finalize
|
494
|
+
assert File.exist?(it.to_s)
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
testing "keep_on_error = true" do
|
499
|
+
test 'keeps dir when block raises exception' do
|
500
|
+
path = nil
|
501
|
+
begin
|
502
|
+
within_construct(keep_on_error: true) do |container_path|
|
503
|
+
path = container_path
|
504
|
+
raise 'something bad happens here'
|
505
|
+
end
|
506
|
+
rescue
|
507
|
+
end
|
508
|
+
assert path.exist?
|
509
|
+
end
|
510
|
+
|
511
|
+
test 'updates exception message to include location of files' do
|
512
|
+
path = nil
|
513
|
+
begin
|
514
|
+
within_construct(keep_on_error: true) do |container_path|
|
515
|
+
path = container_path
|
516
|
+
raise 'bad stuff'
|
517
|
+
end
|
518
|
+
rescue => e
|
519
|
+
error = e
|
520
|
+
end
|
521
|
+
assert_equal "bad stuff\nTestConstruct files kept at: #{path}", e.message
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
testing 'base_dir option' do
|
526
|
+
test 'determines the location of construct dirs' do
|
527
|
+
base_dir = File.expand_path("../temp", __FILE__)
|
528
|
+
within_construct(base_dir: base_dir) do |container|
|
529
|
+
assert_equal base_dir, container.dirname.to_s
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
testing 'name option' do
|
535
|
+
test 'used in generation of the directory name' do
|
536
|
+
within_construct(name: "My best test ever!") do |container|
|
537
|
+
assert_match /my-best-test-ever-$/, container.basename.to_s
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
475
541
|
end
|
data/test_construct.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test_construct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-12-
|
13
|
+
date: 2013-12-30 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -92,6 +92,22 @@ dependencies:
|
|
92
92
|
- - ! '>='
|
93
93
|
- !ruby/object:Gem::Version
|
94
94
|
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rspec
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
95
111
|
description: Creates temporary files and directories for testing.
|
96
112
|
email:
|
97
113
|
- ben@bbrinck.com
|
@@ -101,13 +117,17 @@ extensions: []
|
|
101
117
|
extra_rdoc_files: []
|
102
118
|
files:
|
103
119
|
- .gitignore
|
120
|
+
- CHANGELOG.md
|
104
121
|
- Gemfile
|
105
122
|
- LICENSE.txt
|
106
123
|
- README.md
|
107
124
|
- Rakefile
|
125
|
+
- examples/foobar_spec.rb
|
126
|
+
- examples/foobar_test.rb
|
108
127
|
- lib/test_construct.rb
|
109
128
|
- lib/test_construct/helpers.rb
|
110
129
|
- lib/test_construct/pathname_extensions.rb
|
130
|
+
- lib/test_construct/rspec_integration.rb
|
111
131
|
- lib/test_construct/version.rb
|
112
132
|
- test/test_construct_test.rb
|
113
133
|
- test/test_helper.rb
|