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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +24 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE.txt +23 -0
  9. data/README.md +227 -0
  10. data/Rakefile +13 -0
  11. data/bin/function_override.sh.erb +7 -0
  12. data/bin/function_override_wrapper.sh.erb +16 -0
  13. data/bin/stub +62 -0
  14. data/lib/rspec/bash.rb +5 -0
  15. data/lib/rspec/bash/call_configuration.rb +37 -0
  16. data/lib/rspec/bash/call_log.rb +77 -0
  17. data/lib/rspec/bash/matchers.rb +2 -0
  18. data/lib/rspec/bash/matchers/called_with_arguments.rb +14 -0
  19. data/lib/rspec/bash/matchers/called_with_no_arguments.rb +5 -0
  20. data/lib/rspec/bash/stubbed_command.rb +76 -0
  21. data/lib/rspec/bash/stubbed_env.rb +99 -0
  22. data/rspec-bash.gemspec +25 -0
  23. data/spec/classes/call_configuration_spec.rb +21 -0
  24. data/spec/classes/call_log_spec.rb +309 -0
  25. data/spec/classes/stubbed_command_spec.rb +134 -0
  26. data/spec/classes/stubbed_env_spec.rb +306 -0
  27. data/spec/integration/assert_called_spec.rb +48 -0
  28. data/spec/integration/assert_stdin_spec.rb +39 -0
  29. data/spec/integration/chain_args_spec.rb +65 -0
  30. data/spec/integration/change_exitstatus_spec.rb +53 -0
  31. data/spec/integration/provide_env_vars_spec.rb +31 -0
  32. data/spec/integration/replace_shell_commands_spec.rb +48 -0
  33. data/spec/integration/stub_output_spec.rb +110 -0
  34. data/spec/matchers/be_called_with_arguments_spec.rb +55 -0
  35. data/spec/matchers/be_called_with_no_arguments_spec.rb +32 -0
  36. data/spec/scripts/function_library.sh +9 -0
  37. metadata +129 -0
@@ -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
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ *.swp
15
+ *.gem
16
+ mkmf.log
17
+ .idea/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1 @@
1
+ 2.1.2
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - ruby-head
@@ -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
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+ gem 'rubocop'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'simplecov'
@@ -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.
@@ -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
@@ -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,7 @@
1
+ #!/usr/bin/env bash
2
+
3
+ function <%=function_command_binding_for_template%> {
4
+ <%=function_command_path_binding_for_template%> "${@}"
5
+ }
6
+
7
+ readonly -f <%=function_command_binding_for_template%> &> /dev/null
@@ -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}'
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'rspec/bash/stubbed_command'
2
+ require 'rspec/bash/call_configuration'
3
+ require 'rspec/bash/call_log'
4
+ require 'rspec/bash/stubbed_env'
5
+ require 'rspec/bash/matchers'
@@ -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