rspec-bash 0.0.3
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +23 -0
- data/README.md +227 -0
- data/Rakefile +13 -0
- data/bin/function_override.sh.erb +7 -0
- data/bin/function_override_wrapper.sh.erb +16 -0
- data/bin/stub +62 -0
- data/lib/rspec/bash.rb +5 -0
- data/lib/rspec/bash/call_configuration.rb +37 -0
- data/lib/rspec/bash/call_log.rb +77 -0
- data/lib/rspec/bash/matchers.rb +2 -0
- data/lib/rspec/bash/matchers/called_with_arguments.rb +14 -0
- data/lib/rspec/bash/matchers/called_with_no_arguments.rb +5 -0
- data/lib/rspec/bash/stubbed_command.rb +76 -0
- data/lib/rspec/bash/stubbed_env.rb +99 -0
- data/rspec-bash.gemspec +25 -0
- data/spec/classes/call_configuration_spec.rb +21 -0
- data/spec/classes/call_log_spec.rb +309 -0
- data/spec/classes/stubbed_command_spec.rb +134 -0
- data/spec/classes/stubbed_env_spec.rb +306 -0
- data/spec/integration/assert_called_spec.rb +48 -0
- data/spec/integration/assert_stdin_spec.rb +39 -0
- data/spec/integration/chain_args_spec.rb +65 -0
- data/spec/integration/change_exitstatus_spec.rb +53 -0
- data/spec/integration/provide_env_vars_spec.rb +31 -0
- data/spec/integration/replace_shell_commands_spec.rb +48 -0
- data/spec/integration/stub_output_spec.rb +110 -0
- data/spec/matchers/be_called_with_arguments_spec.rb +55 -0
- data/spec/matchers/be_called_with_no_arguments_spec.rb +32 -0
- data/spec/scripts/function_library.sh +9 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea2520732451e6815750cf97022a407cfb0f19ef
|
4
|
+
data.tar.gz: 5d11657c4897d0ffcaff2e137b8da47c8bc8ac17
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 922d168641fd597e4c98555f365ea51f4ee426ac685a0a86b8980287752b00163e59975015f4e1b134908b6f95e270a0d811b61902740a12ac52da5e46251ccf
|
7
|
+
data.tar.gz: 97cade40565fd8fec2662a192308db8e56f07d4b70824338cdbc7621671d03e61c2354e52b890f90f7e805ccddd47d31c4c34f7f00514840f470e06d8a4a8d99
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.2
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# 1.3.0
|
2
|
+
|
3
|
+
* Improved assertion message
|
4
|
+
|
5
|
+
# 1.2.0
|
6
|
+
|
7
|
+
* Support for output filenames based on input arguments
|
8
|
+
(`.outputs('something', to: [:arg2, '.png'])`
|
9
|
+
* Updates local `ENV['PATH']` to easy test execution from private code
|
10
|
+
* Add `stubbed_env.cleanup` to cleanup `ENV['PATH']` manually
|
11
|
+
|
12
|
+
# 1.1.0
|
13
|
+
|
14
|
+
* Support chaining of arguments in multiple steps
|
15
|
+
(`.with_args(...).with_args(...)`
|
16
|
+
|
17
|
+
# 1.0.0
|
18
|
+
|
19
|
+
* Initial release
|
20
|
+
* Support for
|
21
|
+
* `create_stubbed_env`
|
22
|
+
* Execute script with env-vars
|
23
|
+
* Stubbing commands, output and exitstatus
|
24
|
+
* Asserting calls, arguments and stdin
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2014 Matthijs Groen
|
2
|
+
Modified (c) 2016 Ben Brewer, Mike Urban
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
# Rspec::Bash
|
2
|
+
|
3
|
+
Run your shell script in a mocked environment to test its behavior using RSpec.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
- Test bash functions, entire scripts and inline scripts
|
7
|
+
- Stub shell commands and their exitstatus and outputs
|
8
|
+
- Partial mocks of functions
|
9
|
+
- Control exit status codes
|
10
|
+
- Control multiple outputs (through STDOUT, STDERR or files)
|
11
|
+
- Verify STDIN, STDOUT, STDERR
|
12
|
+
- Verify if command is called
|
13
|
+
- Verify command is called with specific argument sequence
|
14
|
+
- Verify command was called correct number of times
|
15
|
+
- Supports RSpec "anything" wildcard matchers
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'rspec-bash'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install rspec-bash
|
32
|
+
|
33
|
+
|
34
|
+
You can setup rspec-bash globally for your spec suite:
|
35
|
+
|
36
|
+
in `spec_helper.rb`:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'rspec/bash'
|
40
|
+
|
41
|
+
RSpec.configure do |c|
|
42
|
+
c.include Rspec::Bash
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
see specs in *spec/integration* folder:
|
49
|
+
|
50
|
+
### Running script through stubbed env:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'rspec/bash'
|
54
|
+
|
55
|
+
describe 'my shell script' do
|
56
|
+
include Rspec::Bash
|
57
|
+
|
58
|
+
let(:stubbed_env) { create_stubbed_env }
|
59
|
+
|
60
|
+
it 'runs the script' do
|
61
|
+
stdout, stderr, status = stubbed_env.execute(
|
62
|
+
'my-shell-script.sh',
|
63
|
+
{ 'SOME_OPTIONAL' => 'env vars' }
|
64
|
+
)
|
65
|
+
expect(status.exitstatus).to eq 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
### Stubbing commands:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
let(:stubbed_env) { create_stubbed_env }
|
74
|
+
let!(:bundle) { stubbed_env.stub_command('bundle') }
|
75
|
+
|
76
|
+
it 'is stubbed' do
|
77
|
+
stubbed_env.execute 'my-script.sh'
|
78
|
+
expect(bundle).to be_called_with_arguments('install)
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### Changing exitstatus of stubs:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
let(:stubbed_env) { create_stubbed_env }
|
86
|
+
before do
|
87
|
+
stubbed_env.stub_command('rake').returns_exitstatus(5)
|
88
|
+
stubbed_env.stub_command('rake').with_args('spec').returns_exitstatus(3)
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
### Stubbing output:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
let(:stubbed_env) { create_stubbed_env }
|
96
|
+
let(:rake_stub) { stubbed_env.stub_command 'rake' }
|
97
|
+
before do
|
98
|
+
rake_stub.outputs('informative message', to: :stdout)
|
99
|
+
.outputs('error message', to: :stderr)
|
100
|
+
.outputs('log contents', to: 'logfile.log')
|
101
|
+
.outputs('converted result', to: ['prefix-', :arg2, '.txt'])
|
102
|
+
# last one creates 'prefix-foo.txt' when called as 'rake convert foo'
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
### Verifying stdin:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
let(:stubbed_env) { create_stubbed_env }
|
110
|
+
let(:cat_stub) { stubbed_env.stub_command 'cat' }
|
111
|
+
let(:mail_stub) { stubbed_env.stub_command 'mail' }
|
112
|
+
it 'verifies stdin' do
|
113
|
+
stubbed_env.execute_script 'script.sh'
|
114
|
+
expect(cat_stub.stdin).to eql 'hello'
|
115
|
+
expect(mail_stub.with_args('-s', 'hello').stdin).to eql 'world'
|
116
|
+
end
|
117
|
+
```
|
118
|
+
### Test entire script
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
let(:stubbed_env) { Rspec::Bash::StubbedEnv.new }
|
122
|
+
stubbed_env.execute('./path/to/script.sh')
|
123
|
+
```
|
124
|
+
|
125
|
+
### Test specific function
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
let(:stubbed_env) { Rspec::Bash::StubbedEnv.new }
|
129
|
+
stubbed_env.stub_command('overridden_function')
|
130
|
+
stubbed_env.execute_function(
|
131
|
+
'./path/to/script.sh',
|
132
|
+
'overridden_function'
|
133
|
+
)
|
134
|
+
|
135
|
+
```
|
136
|
+
|
137
|
+
### Test inline script
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
let(:stubbed_env) { create_stubbed_env }
|
141
|
+
stubbed_env.stub_command('stubbed_command')
|
142
|
+
stubbed_env.execute_inline(<<-multiline_script
|
143
|
+
stubbed_command first_argument second_argument
|
144
|
+
multiline_script
|
145
|
+
)
|
146
|
+
|
147
|
+
```
|
148
|
+
### Check that mock was called with specific arguments
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
describe 'be_called_with_arguments' do
|
152
|
+
include Rspec::Bash
|
153
|
+
let(:stubbed_env) { create_stubbed_env }
|
154
|
+
|
155
|
+
context 'with a command' do
|
156
|
+
context 'and no chain calls' do
|
157
|
+
before(:each) do
|
158
|
+
@command = stubbed_env.stub_command('stubbed_command')
|
159
|
+
@actual_stdout, @actual_stderr, @actual_status = stubbed_env.execute_inline(<<-multiline_script
|
160
|
+
stubbed_command first_argument second_argument
|
161
|
+
multiline_script
|
162
|
+
)
|
163
|
+
end
|
164
|
+
it 'correctly identifies the called arguments' do
|
165
|
+
expect(@command).to be_called_with_arguments('first_argument', 'second_argument')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
### Check that mock was not called with any arguments
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
@command = stubbed_env.stub_command('stubbed_command')
|
176
|
+
stubbed_env.execute_inline(<<-multiline_script
|
177
|
+
stubbed_command
|
178
|
+
multiline_script
|
179
|
+
|
180
|
+
it 'correctly identifies that no arguments were called' do
|
181
|
+
expect(@command).to be_called_with_no_arguments
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
### Check that mock was called a certain number of times
|
186
|
+
```ruby
|
187
|
+
context 'and the times chain call' do
|
188
|
+
before(:each) do
|
189
|
+
@command = stubbed_env.stub_command('stubbed_command')
|
190
|
+
@actual_stdout, @actual_stderr, @actual_status = stubbed_env.execute_inline(<<-multiline_script
|
191
|
+
stubbed_command duplicated_argument
|
192
|
+
stubbed_command duplicated_argument
|
193
|
+
stubbed_command once_called_argument
|
194
|
+
multiline_script
|
195
|
+
)
|
196
|
+
end
|
197
|
+
it 'matches when arguments are called twice' do
|
198
|
+
expect(@command).to be_called_with_arguments('duplicated_argument').times(2)
|
199
|
+
end
|
200
|
+
it 'matches when argument is called once' do
|
201
|
+
expect(@command).to be_called_with_arguments('once_called_argument').times(1)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
### Use rspec "anything" wildcards for arguments you don't need to match exactly
|
207
|
+
```ruby
|
208
|
+
it 'correctly matches when wildcard is used for arguments' do
|
209
|
+
expect(@command).to be_called_with_arguments(anything, 'second_argument', anything)
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
## More examples
|
214
|
+
|
215
|
+
see the *spec/integration* folder
|
216
|
+
|
217
|
+
## Supported ruby versions
|
218
|
+
|
219
|
+
Ruby 2+, no JRuby, due to issues with `Open3.capture3`
|
220
|
+
|
221
|
+
## Contributing
|
222
|
+
|
223
|
+
1. Fork it ( https://github.com/mdurban/rspec-bash )
|
224
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
225
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
226
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
227
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
5
|
+
t.verbose = false
|
6
|
+
t.rspec_opts = '--format documentation'
|
7
|
+
t.rspec_opts << ' --color'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rubocop/rake_task'
|
11
|
+
RuboCop::RakeTask.new
|
12
|
+
|
13
|
+
task default: [:rubocop, :spec]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/usr/bin/env bash -c '
|
2
|
+
# load in command and function overrides
|
3
|
+
for override_file in <%=function_override_path_binding_for_template%>; do
|
4
|
+
source ${override_file}
|
5
|
+
done
|
6
|
+
|
7
|
+
(
|
8
|
+
<%=execution_binding_for_template%>
|
9
|
+
) 2> <%=wrapped_error_path_binding_for_template%>
|
10
|
+
command_exit_code=$?
|
11
|
+
|
12
|
+
# filter stderr for readonly problems
|
13
|
+
grep -v "readonly function" <%=wrapped_error_path_binding_for_template%> >&2
|
14
|
+
|
15
|
+
# return original exit code
|
16
|
+
exit ${command_exit_code}'
|
data/bin/stub
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pathname'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
command = File.basename(__FILE__)
|
6
|
+
folder = File.dirname(__FILE__)
|
7
|
+
|
8
|
+
call_logs = Pathname.new(folder).join("#{command}_calls.yml")
|
9
|
+
call_logs.open('a') do |f|
|
10
|
+
f.puts '- args:'
|
11
|
+
ARGV.each { |arg| f.puts " - #{arg.inspect}" }
|
12
|
+
f.puts " stdin: #{$stdin.read.inspect}" unless STDIN.tty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def interpolate_filename(elements)
|
16
|
+
return elements if elements.is_a? String
|
17
|
+
return nil unless elements.is_a? Array
|
18
|
+
|
19
|
+
elements.map do |element|
|
20
|
+
case element
|
21
|
+
when String then element
|
22
|
+
when Symbol then interpolate_argument(element)
|
23
|
+
end
|
24
|
+
end.join
|
25
|
+
end
|
26
|
+
|
27
|
+
def interpolate_argument(name)
|
28
|
+
return unless (data = /^arg(\d+)$/.match(name.to_s))
|
29
|
+
ARGV[data[1].to_i - 1]
|
30
|
+
end
|
31
|
+
|
32
|
+
call_configurations = Pathname.new(folder).join("#{command}_stub.yml")
|
33
|
+
if call_configurations.exist?
|
34
|
+
config = YAML.load call_configurations.read
|
35
|
+
|
36
|
+
matching_calls = []
|
37
|
+
config.each do |call_config|
|
38
|
+
if !call_config[:args].empty?
|
39
|
+
next unless (call_config[:args].eql? ARGV)
|
40
|
+
end
|
41
|
+
matching_calls << {
|
42
|
+
config: call_config,
|
43
|
+
specific: call_config[:args].length
|
44
|
+
}
|
45
|
+
end
|
46
|
+
exit 0 if matching_calls.empty?
|
47
|
+
|
48
|
+
call_config = matching_calls.sort do |a, b|
|
49
|
+
b[:specific] <=> a[:specific]
|
50
|
+
end.first[:config]
|
51
|
+
(call_config[:outputs] || []).each do |data|
|
52
|
+
$stdout.print data[:content] if data[:target] == :stdout
|
53
|
+
$stderr.print data[:content] if data[:target] == :stderr
|
54
|
+
|
55
|
+
output_filename = interpolate_filename(data[:target])
|
56
|
+
next unless output_filename
|
57
|
+
Pathname.new(output_filename).open('w') do |f|
|
58
|
+
f.print data[:content]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
exit call_config[:statuscode] || 0
|
62
|
+
end
|
data/lib/rspec/bash.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Rspec
|
4
|
+
module Bash
|
5
|
+
# Configuration of a stubbed command
|
6
|
+
class CallConfiguration
|
7
|
+
attr_reader :command
|
8
|
+
|
9
|
+
def initialize(config_path, command)
|
10
|
+
@config_path = config_path
|
11
|
+
@configuration = {}
|
12
|
+
@command = command
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_exitcode(statuscode, args = [])
|
16
|
+
@configuration[args] ||= {}
|
17
|
+
@configuration[args][:statuscode] = statuscode
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_output(content, target, args = [])
|
21
|
+
@configuration[args] ||= {}
|
22
|
+
@configuration[args][:outputs] ||= []
|
23
|
+
@configuration[args][:outputs] << { target: target, content: content }
|
24
|
+
end
|
25
|
+
|
26
|
+
def write
|
27
|
+
structure = @configuration.map do |args, results|
|
28
|
+
{ args: args }.merge results
|
29
|
+
end
|
30
|
+
|
31
|
+
@config_path.open('w') do |f|
|
32
|
+
f.puts structure.to_yaml
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|