rspec-bash 0.0.3

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