aruba-doubles 0.2.3 → 1.0.0

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.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ vendor/*
6
+ tmp/*
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.2-p290
data/README.md CHANGED
@@ -1,14 +1,36 @@
1
1
  # Aruba-Doubles
2
2
 
3
- Stub and mock command line applications with Cucumber
3
+ Cucumber Steps to double Command Line Applications
4
4
 
5
5
  ## Introduction
6
6
 
7
- Developing a command line application in proper [BDD](http://en.wikipedia.org/wiki/Behavior_Driven_Development)-style with [Cucumber](http://cukes.info/) and [Aruba](https://github.com/cucumber/aruba) can be cumbersome, when your application depends on other CLI stuff (i.e. calling under certain conditions `kill_the_cat`).
8
- Mocking and stubbing with Cucumber is usually [not recommended](https://github.com/cucumber/cucumber/wiki/Mocking-and-Stubbing-with-Cucumber) and tricky because we have do do it across processes but, in some cases it could make your life easier (Your cat will thank you later!)
9
- Aruba-Doubles are some convenient Cucumber steps to fake CLI applications during your tests (by injecting temporary doubles in front of your PATH).
10
-
11
- (Note: Aruba-Doubles is not an official part of Aruba but a good companion in the same domain, hence the name.)
7
+ > Sometimes it is just plain hard to test the system under test (SUT)
8
+ > because it depends on other components that cannot be used in the test
9
+ > environment. This could be because they aren't available, they will not
10
+ > return the results needed for the test or because executing them would
11
+ > have undesirable side effects. In other cases, our test strategy
12
+ > requires us to have more control or visibility of the internal behavior
13
+ > of the SUT.
14
+ >
15
+ > When we are writing a test in which we cannot (or chose not to) use a
16
+ > real depended-on component (DOC), we can replace it with a Test Double.
17
+ > The Test Double doesn't have to behave exactly like the real DOC; it
18
+ > merely has to provide the same API as the real one so that the SUT
19
+ > thinks it is the real one!
20
+ >
21
+ > &mdash; <cite>[Gerard Meszaros, *xUnit Test Patterns: Refactoring Test
22
+ > Code*, Copyright © 2007, Addison-Wesley, ISBN
23
+ > 978-0131495050.](http://xunitpatterns.com/Test%20Double.html)</cite>
24
+
25
+ Aruba-Doubles is a [Cucumber](http://cukes.info/) extention to
26
+ temporarily "replace" selected Command Line Applications by Test
27
+ Doubles.
28
+ This allows you to simply stub them or fake their output depending on
29
+ ARGV.
30
+
31
+ (Aruba-Doubles is not an official part of
32
+ [Aruba](https://github.com/cucumber/aruba) but a good companion in the
33
+ same domain, hence the name.)
12
34
 
13
35
  ## Usage
14
36
 
@@ -16,61 +38,43 @@ If you have a `Gemfile`, add `aruba-doubles`. Otherwise, install it like this:
16
38
 
17
39
  gem install aruba-doubles
18
40
 
19
- Then, `require` the library in one of your ruby files under `features/support` (e.g. `env.rb`)
20
-
21
- require 'aruba-doubles/cucumber'
22
-
23
- ### Stubbing
41
+ Then, `require` the [step
42
+ definitions](https://github.com/bjoernalbers/aruba-doubles/blob/master/lib/aruba-doubles/cucumber.rb)
43
+ in a ruby files below `features/support` (e.g. `env.rb`):
24
44
 
25
- You can stub commands by using the `I could run` steps:
45
+ ```Ruby
46
+ require 'aruba-doubles/cucumber'
47
+ ```
26
48
 
27
- Background:
28
- Given I could run `kill_the_cat` with stdout:
29
- """
30
- R.I.P.
31
- """
49
+ This file contains all [step
50
+ definitions](https://github.com/bjoernalbers/aruba-doubles/blob/master/lib/aruba-doubles/cucumber.rb),
51
+ so you might wanna have a look at it.
52
+ Usage examples can be found in the
53
+ [features](https://github.com/bjoernalbers/aruba-doubles/tree/master/features)
54
+ directory.
32
55
 
33
- Scenario: Call stubbed command
34
- When I run `kill_the_cat`
35
- Then the exit status should be 0
36
- And the stdout should contain "R.I.P."
37
- And the stderr should be empty
56
+ ## How it works
38
57
 
39
- Scenario: Call stubbed command with unexpected argument
40
- When I run `kill_the_cat --force`
41
- Then the exit status should not be 0
42
- And the stdout should be empty
43
- And the stderr should contain "--force"
58
+ Aruba-Doubles does the following:
44
59
 
45
- Scenario: Add expected arguments to stub
46
- And I could run `kill_the_cat --gently` with exit status 255 and stderr:
47
- """
48
- ERROR: Unable to kill gently... with a chainsaw!
49
- """
50
- When I run `kill_the_cat`
51
- Then the stdout should contain "R.I.P."
52
- And the stderr should be empty
53
- When I run `kill_the_cat --gently`
54
- Then the stdout should be empty
55
- And the stderr should contain "ERROR: Unable to kill gently..."
60
+ 1. Hijack the PATH variable by injecting a temporary doubles directory
61
+ in front of it.
62
+ 2. Create Test Doubles (which are executable Ruby scripts) inside the
63
+ doubles directory.
64
+ 3. Restore the old PATH and remove all Test Doubles after each
65
+ scenario.
56
66
 
57
- Take a peek at `features/*.feature` for further examples and at `lib/aruba-doubles/cucumber.rb` for all step definitions.
58
-
59
- ### Mocking
60
-
61
- Currently you can't check if a double was called at all, but hopefully this will be a feature in the future (patches are welcome!).
67
+ This way your SUT will pick up the Test Double instead of the real
68
+ Command Line Application.
62
69
 
63
70
  ## Caveats
64
71
 
65
- Aruba-Double won't work, if your command...
72
+ Aruba-Double won't work, if your command:
66
73
 
67
74
  * calls other commands with absolute path, i.e. `/usr/local/kill_the_cat`
68
75
  * defines its own PATH
69
76
  * calls build-in commands from your shell like `echo` (but who want to stub that)
70
77
 
71
- Of course you'll only see stdout and sterr from doubles if these aren't silenced by the calling command (like redirecting to /dev/null.)
72
- Keep that in mind when you want to compare expected and acutal arguments.
73
-
74
78
  Also note that doubles will be created as scripts in temporary directories on your filesystem, which might slow down your tests.
75
79
 
76
80
  ## Note on Patches/Pull Requests
@@ -85,4 +89,4 @@ Also note that doubles will be created as scripts in temporary directories on yo
85
89
 
86
90
  ## Copyright
87
91
 
88
- Copyright (c) 2011 Björn Albers. See LICENSE for details.
92
+ Copyright (c) 2011-2012 Björn Albers. See LICENSE for details.
@@ -2,15 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'aruba-doubles'
5
- s.version = '0.2.3'
6
- s.authors = ["Björn Albers"]
7
- s.email = ["bjoernalbers@googlemail.com"]
8
- s.description = 'Stub command line applications with Cucumber'
5
+ s.version = '1.0.0'
6
+ s.authors = ['Björn Albers']
7
+ s.email = ['bjoernalbers@googlemail.com']
8
+ s.description = 'Cucumber Steps to double Command Line Applications'
9
9
  s.summary = "#{s.name}-#{s.version}"
10
10
  s.homepage = "https://github.com/bjoernalbers/#{s.name}"
11
11
 
12
12
  s.add_dependency 'cucumber', '>= 1.0.2'
13
+ s.add_dependency 'rspec', '>= 2.6.0'
13
14
 
15
+ s.add_development_dependency 'rake', '>= 0.9.2.2'
14
16
  s.add_development_dependency 'aruba', '>= 0.4.6'
15
17
  s.add_development_dependency 'guard-cucumber', '>= 0.7.3'
16
18
  s.add_development_dependency 'guard-rspec', '>= 0.5.1'
@@ -18,5 +20,5 @@ Gem::Specification.new do |s|
18
20
  s.files = `git ls-files`.split("\n")
19
21
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
22
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
- s.require_paths = ["lib"]
23
+ s.require_paths = ['lib']
22
24
  end
@@ -0,0 +1,92 @@
1
+ Feature: Define Output
2
+
3
+ In order to double a command line application
4
+ As a BDD-guy
5
+ I want to define the doubles output
6
+
7
+ Scenario: Double with default stdout, stderr and exit status
8
+ Given I double `foo --bar baz`
9
+ When I run `foo --bar baz`
10
+ Then the exit status should be 0
11
+ And the stdout should be empty
12
+ And the stderr should be empty
13
+
14
+ Scenario: Double with custom stdout (inline)
15
+ Given I double `foo` with "hello, world."
16
+ When I run `foo`
17
+ Then the exit status should be 0
18
+ And the stdout should contain "hello, world."
19
+ And the stderr should be empty
20
+
21
+ Scenario: Double with custom stdout
22
+ Given I double `foo --bar baz` with stdout:
23
+ """
24
+ hello, world.
25
+ """
26
+ When I run `foo --bar baz`
27
+ Then the exit status should be 0
28
+ And the stdout should contain exactly:
29
+ """
30
+ hello, world.
31
+
32
+ """
33
+ And the stderr should be empty
34
+
35
+ Scenario: Double with custom stdout and exit status
36
+ Given I double `foo --bar baz` with exit status 42 and stdout:
37
+ """
38
+ hello, world.
39
+ """
40
+ When I run `foo --bar baz`
41
+ Then the exit status should be 42
42
+ And the stdout should contain exactly:
43
+ """
44
+ hello, world.
45
+
46
+ """
47
+ And the stderr should be empty
48
+
49
+ Scenario: Double with custom stderr
50
+ Given I double `foo --bar baz` with stderr:
51
+ """
52
+ BOOOOOOOM!!!
53
+ """
54
+ When I run `foo --bar baz`
55
+ Then the exit status should be 0
56
+ And the stdout should be empty
57
+ And the stderr should contain exactly:
58
+ """
59
+ BOOOOOOOM!!!
60
+
61
+ """
62
+
63
+ Scenario: Double with custom stderr and exit status
64
+ Given I double `foo --bar baz` with exit status 42 and stderr:
65
+ """
66
+ BOOOOOOOM!!!
67
+ """
68
+ When I run `foo --bar baz`
69
+ Then the exit status should be 42
70
+ And the stdout should be empty
71
+ And the stderr should contain exactly:
72
+ """
73
+ BOOOOOOOM!!!
74
+
75
+ """
76
+
77
+ Scenario: Double with custom exit status
78
+ Given I double `foo --bar baz` with exit status 42
79
+ When I run `foo --bar baz`
80
+ Then the exit status should be 42
81
+ And the stdout should be empty
82
+ And the stderr should be empty
83
+
84
+ Scenario: Double called with unknown arguments
85
+ Given I double `foo --bar baz` with exit status 42 and stdout:
86
+ """
87
+ hello, world.
88
+ """
89
+ When I run `foo --unknown arguments`
90
+ Then the exit status should be 0
91
+ And the stdout should be empty
92
+ And the stderr should be empty
@@ -0,0 +1,35 @@
1
+ Feature: Double Commands
2
+
3
+ In order to avoid that my system-under-test runs certain commands
4
+ As a BDD-guy
5
+ I want to double command line applications
6
+
7
+ Background:
8
+ Given a file named "foo" with:
9
+ """
10
+ #!/usr/bin/env ruby
11
+ puts "stdout of foo"
12
+ warn "stderr of foo"
13
+ exit 5
14
+ """
15
+ And I run `chmod +x foo`
16
+ And I append the current working dir to my path
17
+
18
+ Scenario: Run the original (undoubled) command
19
+ When I run `foo`
20
+ Then the exit status should be 5
21
+ And the stdout should contain "stdout of foo"
22
+ And the stderr should contain "stderr of foo"
23
+
24
+ Scenario: Double and run the command
25
+ Given I double `foo`
26
+ When I run `foo`
27
+ Then the exit status should be 0
28
+ And the stdout should not contain "stdout of foo"
29
+ And the stderr should not contain "stderr of foo"
30
+
31
+ Scenario: Run the original (undoubled) command
32
+ When I run `foo`
33
+ Then the exit status should be 5
34
+ And the stdout should contain "stdout of foo"
35
+ And the stderr should contain "stderr of foo"
@@ -1,21 +1,5 @@
1
- Then /^the doubles directory should not exist$/ do
2
- @doubles_dir.should be_nil
3
- end
4
-
5
- Then /^the path should include (\d+) doubles directory$/ do |count|
6
- ENV['PATH'].split(File::PATH_SEPARATOR).count(@doubles_dir).should eql(count.to_i)
7
- end
8
-
9
- When /^I keep the doubles directory in mind$/ do
10
- @@previous_doubles_dir = @doubles_dir
11
- end
12
-
13
- Then /^the previous doubles directory should not exist$/ do
14
- File.should_not be_exist(@@previous_doubles_dir)
15
- end
16
-
17
- Then /^the previous doubles directory should exist$/ do
18
- File.should be_exist(@@previous_doubles_dir)
1
+ Given /^I append the current working dir to my path$/ do
2
+ ENV['PATH'] = [ENV['PATH'], '.'].join(File::PATH_SEPARATOR)
19
3
  end
20
4
 
21
5
  Then /^the (stdout|stderr) should be empty$/ do |stdout_stderr|
@@ -25,5 +9,3 @@ Then /^the (stdout|stderr) should be empty$/ do |stdout_stderr|
25
9
  """
26
10
  }
27
11
  end
28
-
29
-
@@ -1,20 +1,39 @@
1
- require 'aruba-doubles/hooks'
2
- require 'aruba-doubles/api'
1
+ require 'aruba-doubles'
3
2
 
4
- World(ArubaDoubles::Api)
3
+ World(ArubaDoubles)
5
4
 
6
- Given /^I could run `([^`]*)`$/ do |cmd|
7
- create_double_by_cmd(cmd)
5
+ Before do
6
+ ArubaDoubles::Double.setup
8
7
  end
9
8
 
10
- Given /^I could run `([^`]*)` with (stdout|stderr):$/ do |cmd, type, output|
11
- create_double_by_cmd(cmd, type.to_sym => output)
9
+ After do
10
+ ArubaDoubles::Double.teardown
12
11
  end
13
12
 
14
- Given /^I could run `([^`]*)` with exit status (\d+)$/ do |cmd, exit|
15
- create_double_by_cmd(cmd, :exit_status => exit.to_i)
13
+ Given /^I double `([^`]*)`$/ do |cmd|
14
+ double_cmd(cmd)
16
15
  end
17
16
 
18
- Given /^I could run `([^`]*)` with exit status (\d+) and (stdout|stderr):$/ do |cmd, exit, type, output|
19
- create_double_by_cmd(cmd, :exit_status => exit.to_i, type.to_sym => output)
17
+ Given /^I double `([^`]*)` with "([^"]*)"$/ do |cmd, stdout|
18
+ double_cmd(cmd, :puts => stdout)
19
+ end
20
+
21
+ Given /^I double `([^`]*)` with stdout:$/ do |cmd, stdout|
22
+ double_cmd(cmd, :puts => stdout)
23
+ end
24
+
25
+ Given /^I double `([^`]*)` with exit status (\d+) and stdout:$/ do |cmd, exit_status, stdout|
26
+ double_cmd(cmd, :puts => stdout, :exit => exit_status.to_i)
27
+ end
28
+
29
+ Given /^I double `([^`]*)` with stderr:$/ do |cmd, stderr|
30
+ double_cmd(cmd, :warn => stderr)
31
+ end
32
+
33
+ Given /^I double `([^`]*)` with exit status (\d+) and stderr:$/ do |cmd, exit_status, stderr|
34
+ double_cmd(cmd, :warn => stderr, :exit => exit_status.to_i)
35
+ end
36
+
37
+ Given /^I double `([^`]*)` with exit status (\d+)$/ do |cmd, exit_status|
38
+ double_cmd(cmd, :exit => exit_status.to_i)
20
39
  end
@@ -1,29 +1,141 @@
1
1
  module ArubaDoubles
2
2
  class Double
3
- attr_reader :stdout, :stderr, :exit_status, :expectations
4
-
5
- def self.run!(expectations = {})
6
- double = self.new(expectations)
7
- double.run
8
- puts double.stdout if double.stdout
9
- warn double.stderr if double.stderr
10
- exit(double.exit_status) if double.exit_status
3
+ class << self
4
+
5
+ # Setup the doubles environment.
6
+ def setup
7
+ patch_path
8
+ end
9
+
10
+ # Teardown the doubles environment.
11
+ def teardown
12
+ delete_all
13
+ restore_path
14
+ end
15
+
16
+ # Initialize and create a new double.
17
+ #
18
+ # It accepts an optional block to setup define the doubles output.
19
+ def create(filename, &block)
20
+ double = new(filename)
21
+ double.instance_eval(&block) if block_given?
22
+ double.create
23
+ end
24
+
25
+ # Initialize and run a new double.
26
+ #
27
+ # It accepts an optional block to setup define the doubles output.
28
+ def run(&block)
29
+ double = new(File.basename($PROGRAM_NAME))
30
+ double.instance_eval(&block) if block_given?
31
+ double.run
32
+ end
33
+
34
+ # Return the doubles directory.
35
+ # @return [String]
36
+ def bindir
37
+ @bindir ||= Dir.mktmpdir
38
+ end
39
+
40
+ # Iterate over all registered doubles.
41
+ def each
42
+ all.each { |double| yield(double) }
43
+ end
44
+
45
+ # Return all registered doubles.
46
+ # @return [Array<ArubaDoubles::Double>]
47
+ def all
48
+ @doubles ||= []
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :original_path
54
+
55
+ # Delete all registered doubles.
56
+ def delete_all
57
+ each(&:delete)
58
+ end
59
+
60
+ # Prepend doubles directory to PATH.
61
+ def patch_path
62
+ unless bindir_in_path?
63
+ @original_path = ENV['PATH']
64
+ ENV['PATH'] = [bindir, ENV['PATH']].join(File::PATH_SEPARATOR)
65
+ end
66
+ end
67
+
68
+ # Remove doubles directory from PATH.
69
+ def restore_path
70
+ ENV['PATH'] = original_path if bindir_in_path?
71
+ end
72
+
73
+ # Check if PATH is already patched.
74
+ # @return [Boolean]
75
+ def bindir_in_path?
76
+ ENV['PATH'].split(File::PATH_SEPARATOR).first == bindir
77
+ end
11
78
  end
12
-
13
- def initialize(expectations = {})
14
- @expectations = expectations
79
+
80
+ attr_reader :filename, :output, :matchers
81
+
82
+ # Instantiate and register new double.
83
+ # @return [ArubaDoubles::Double]
84
+ def initialize(cmd, default_output = {}, &block)
85
+ @filename = cmd
86
+ @output = {:puts => nil, :warn => nil, :exit => nil}.merge(default_output)
87
+ @matchers = []
88
+ self.class.all << self
89
+ self.instance_eval(&block) if block_given?
15
90
  end
16
-
17
- def could_receive(args, options = {})
18
- @expectations[args] = options
19
- self
91
+
92
+ # Add ARGV matcher with output.
93
+ def on(argv, output = nil)
94
+ @matchers << [argv, output || default_output]
20
95
  end
21
-
96
+
97
+ # Run the double.
98
+ #
99
+ # This will actually display any outputs if defined and exit.
22
100
  def run(argv = ARGV)
23
- raise "Unexpected arguments: #{argv.inspect}" unless @expectations.has_key?(argv)
24
- @stdout = @expectations[argv][:stdout]
25
- @stderr = @expectations[argv][:stderr]
26
- @exit_status = @expectations[argv][:exit_status]
101
+ @matchers.each do |m|
102
+ expected_argv, output = *m
103
+ @output = output if argv == expected_argv
104
+ end
105
+ puts @output[:puts] if @output[:puts]
106
+ warn @output[:warn] if @output[:warn]
107
+ exit @output[:exit] if @output[:exit]
108
+ end
109
+
110
+ # Create the executable double.
111
+ # @return [String] full path to the double.
112
+ def create
113
+ content = self.to_ruby
114
+ fullpath = File.join(self.class.bindir, filename)
115
+ #puts "creating double: #{fullpath} with content:\n#{content}" # debug
116
+ f = File.open(fullpath, 'w')
117
+ f.puts content
118
+ f.close
119
+ FileUtils.chmod(0755, File.join(self.class.bindir, filename))
120
+ end
121
+
122
+ # Export the double to executable Ruby code.
123
+ #
124
+ # @return [String] serialized double
125
+ def to_ruby
126
+ ruby = ['#!/usr/bin/env ruby']
127
+ ruby << "$: << '#{File.expand_path('..', File.dirname(__FILE__))}'"
128
+ ruby << 'require "aruba-doubles"'
129
+ ruby << 'ArubaDoubles::Double.run do'
130
+ @matchers.each { |argv,output| ruby << " on #{argv.inspect}, #{output.inspect}" }
131
+ ruby << 'end'
132
+ ruby.join("\n")
133
+ end
134
+
135
+ # Delete the executable double.
136
+ def delete
137
+ fullpath = File.join(self.class.bindir, filename)
138
+ FileUtils.rm(fullpath) if File.exists?(fullpath)
27
139
  end
28
140
  end
29
141
  end
data/lib/aruba-doubles.rb CHANGED
@@ -1 +1,12 @@
1
- # Load nothing - just keep the file here to keep bundler happy.
1
+ require 'tempfile'
2
+ require 'shellwords'
3
+ require 'aruba-doubles/double'
4
+
5
+ module ArubaDoubles
6
+ def double_cmd(cmd, output = {})
7
+ argv = Shellwords.split(cmd)
8
+ Double.create(argv.shift) do
9
+ on(argv, output)
10
+ end
11
+ end
12
+ end
@@ -1,89 +1,249 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ArubaDoubles::Double, '#could_receive' do
4
- before do
5
- @double = ArubaDoubles::Double.new
6
- end
3
+ describe ArubaDoubles::Double do
4
+ describe '.setup' do
5
+ before do
6
+ @original_path = ENV['PATH']
7
+ @bindir = '/tmp/foo'
8
+ ArubaDoubles::Double.stub(:bindir).and_return(@bindir)
9
+ end
10
+
11
+ after do
12
+ ENV['PATH'] = @original_path
13
+ end
7
14
 
8
- it "should set expected arguments like ARGV" do
9
- @double.could_receive(["--foo"], :stdout => "foo")
10
- @double.could_receive(["--bar"], :stdout => "bar")
11
- @double.run(["--foo"])
12
- @double.stdout.should eql("foo")
13
- @double.run(["--bar"])
14
- @double.stdout.should eql("bar")
15
+ it 'should prepend the doubles directory to PATH' do
16
+ ENV['PATH'].should_not match(%r(^#{@bindir}))
17
+ ArubaDoubles::Double.setup
18
+ ENV['PATH'].should match(%r(^#{@bindir}))
19
+ end
20
+
21
+ it 'should change the path only once' do
22
+ ArubaDoubles::Double.setup
23
+ ArubaDoubles::Double.setup
24
+ ENV['PATH'].scan(@bindir).count.should eq(1)
25
+ end
15
26
  end
16
27
 
17
- it "should return self" do
18
- @double.could_receive([]).should be(@double)
28
+ describe '.bindir' do
29
+ it 'should create and return the temporary doubles directory' do
30
+ bindir = '/tmp/foo'
31
+ Dir.should_receive(:mktmpdir).once.and_return(bindir)
32
+ ArubaDoubles::Double.bindir.should eq(bindir)
33
+ ArubaDoubles::Double.bindir.should eq(bindir)
34
+ end
19
35
  end
20
-
21
- it "should set defaults when called without options" do
22
- @double.could_receive([])
23
- @double.run([])
24
- @double.stdout.should be_nil
25
- @double.stderr.should be_nil
26
- @double.exit_status.should be_nil
36
+
37
+ describe '.teardown' do
38
+ before do
39
+ @bindir = '/tmp/foo'
40
+ end
41
+
42
+ it 'should delete all registered doubles' do
43
+ doubles = []
44
+ 3.times { |i| doubles << double(i) }
45
+ doubles.map{ |d| d.should_receive(:delete) }
46
+ ArubaDoubles::Double.should_receive(:all).and_return(doubles)
47
+ ArubaDoubles::Double.teardown
48
+ end
49
+
50
+ it 'should remove the doubles dir'
51
+ it 'should not remove a non-empty doubles dir'
52
+
53
+ it 'should remove the doubles dir from PATH' do
54
+ ArubaDoubles::Double.stub(:bindir).and_return(@bindir)
55
+ ArubaDoubles::Double.setup
56
+ ENV['PATH'].should match(%r(^#{@bindir}))
57
+ ArubaDoubles::Double.teardown
58
+ ENV['PATH'].should_not match(%r(^#{@bindir}))
59
+ end
60
+
61
+ it 'should not restore PATH when unchanged' do
62
+ original_path = '/foo:/bar:/baz'
63
+ ArubaDoubles::Double.stub(:original_path).and_return('/foo:/bar:/baz')
64
+ ArubaDoubles::Double.teardown
65
+ ENV['PATH'].should_not eq(original_path)
66
+ end
27
67
  end
28
68
 
29
- it "should set optional stdout" do
30
- @double.could_receive([], :stdout => "hi")
31
- @double.run([])
32
- @double.stdout.should eql("hi")
69
+ describe '.new' do
70
+ it 'should register the double' do
71
+ d = ArubaDoubles::Double.new('bar')
72
+ ArubaDoubles::Double.all.should include(d)
73
+ end
74
+
75
+ it 'should initialize all output attributes with nil' do
76
+ output = ArubaDoubles::Double.new('foo').output
77
+ output.should eql({:puts => nil, :warn => nil, :exit => nil})
78
+ end
79
+
80
+ it 'should execute a given block in the doubles context' do
81
+ double = ArubaDoubles::Double.new('bar') { def hi; "hi" end }
82
+ double.should respond_to(:hi)
83
+ end
84
+
85
+ it 'should raise error on missing filename'
86
+ it 'should raise error on absolute filename'
33
87
  end
34
88
 
35
- it "should set optional stderr" do
36
- @double.could_receive([], :stderr => "HO!")
37
- @double.run([])
38
- @double.stderr.should eql("HO!")
89
+ describe '.run' do
90
+ before do
91
+ @double = double('double', :run => nil)
92
+ ArubaDoubles::Double.stub(:new).and_return(@double)
93
+ end
94
+
95
+ it 'should initialize a new double with the program name' do
96
+ ArubaDoubles::Double.should_receive(:new).with('rspec')
97
+ ArubaDoubles::Double.run
98
+ end
99
+
100
+ it 'should execute a block on that double when given' do
101
+ block = Proc.new {}
102
+ @double.should_receive(:instance_eval).with(&block)
103
+ ArubaDoubles::Double.run(&block)
104
+ end
105
+
106
+ it 'should run the double' do
107
+ @double.should_receive(:run)
108
+ ArubaDoubles::Double.run
109
+ end
39
110
  end
40
-
41
- it "should set optional exit status" do
42
- @double.could_receive([], :exit_status => 1)
43
- @double.run([])
44
- @double.exit_status.should eql(1)
111
+
112
+ describe '#run' do
113
+ before do
114
+ @double = ArubaDoubles::Double.new('foo',
115
+ :puts => 'default stdout',
116
+ :warn => 'default stderr',
117
+ :exit => 23)
118
+ @double.on %w[--hello],
119
+ :puts => 'hello, world.',
120
+ :warn => 'BOOOOM!',
121
+ :exit => 42
122
+ @double.stub(:puts => nil, :warn => nil, :exit => nil)
123
+ end
124
+
125
+ context 'when ARGV does match' do
126
+ def run_double
127
+ @double.run %w[--hello]
128
+ end
129
+
130
+ it 'should print given stdout' do
131
+ @double.should_receive(:puts).with('hello, world.')
132
+ run_double
133
+ end
134
+
135
+ it 'should print given stderr' do
136
+ @double.should_receive(:warn).with('BOOOOM!')
137
+ run_double
138
+ end
139
+
140
+ it 'should return given exit code' do
141
+ @double.should_receive(:exit).with(42)
142
+ run_double
143
+ end
144
+ end
145
+
146
+ context 'when ARGV does not match' do
147
+ def run_double
148
+ @double.run %w[--these --arguments --do --not --match]
149
+ end
150
+
151
+ it 'should print default stdout' do
152
+ @double.should_receive(:puts).with('default stdout')
153
+ run_double
154
+ end
155
+
156
+ it 'should print no stderr' do
157
+ @double.should_receive(:warn).with('default stderr')
158
+ run_double
159
+ end
160
+
161
+ it 'should exit with zero' do
162
+ @double.should_receive(:exit).with(23)
163
+ run_double
164
+ end
165
+ end
166
+
167
+ it 'should read ARGV'
45
168
  end
46
- end
47
169
 
48
- describe ArubaDoubles::Double, '#run' do
49
- before do
50
- @double = ArubaDoubles::Double.new
170
+ describe '.create' do
171
+ before do
172
+ @double = double('double', :create => nil)
173
+ ArubaDoubles::Double.stub(:new).and_return(@double)
174
+ end
175
+
176
+ it 'should initialize a new double with the program name' do
177
+ ArubaDoubles::Double.should_receive(:new).with('foo')
178
+ ArubaDoubles::Double.create('foo')
179
+ end
180
+
181
+ it 'should execute a block on that double when given' do
182
+ block = Proc.new {}
183
+ @double.should_receive(:instance_eval).with(&block)
184
+ ArubaDoubles::Double.create('foo', &block)
185
+ end
186
+
187
+ it 'should create the double' do
188
+ @double.should_receive(:create)
189
+ ArubaDoubles::Double.create('foo')
190
+ end
191
+
192
+ it 'should return the new double'
51
193
  end
52
-
53
- it "should read ARGV by default" do
54
- original_arguments = ARGV
55
- ARGV = ["--foo"]
56
- @double.could_receive(["--foo"], :stdout => "foo")
57
- @double.run
58
- @double.stdout.should eql("foo")
59
- ARGV = original_arguments
194
+
195
+ describe '#create' do
196
+ it 'should create the executable command inside the doubles dir' do
197
+ file = double('file', :puts => nil, :close => nil)
198
+ ArubaDoubles::Double.stub(:bindir).and_return('/tmp/foo')
199
+ File.should_receive(:open).with('/tmp/foo/bar', 'w').and_return(file)
200
+ FileUtils.stub(:chmod)
201
+ ArubaDoubles::Double.new('bar').create
202
+ end
203
+
204
+ it 'should make the file executable' do
205
+ ArubaDoubles::Double.stub(:bindir).and_return('/tmp/foo')
206
+ File.stub(:open).and_return(stub(:puts => nil, :close => nil))
207
+
208
+ filename = '/tmp/foo/bar'
209
+ FileUtils.should_receive(:chmod).with(0755, filename)
210
+
211
+ ArubaDoubles::Double.new('bar').create
212
+ end
60
213
  end
61
-
62
- it "should raise when called with unexpected arguments" do
63
- lambda {
64
- @double.run(["--unexpected"])
65
- }.should raise_error('Unexpected arguments: ["--unexpected"]')
214
+
215
+ describe '#to_ruby' do
216
+ before do
217
+ @double = ArubaDoubles::Double.new('foo')
218
+ end
219
+
220
+ it 'should start with a she-bang line' do
221
+ @double.to_ruby.should include('#!/usr/bin/env ruby')
222
+ end
223
+
224
+ it 'should require the libs' do
225
+ @double.to_ruby.should include('require "aruba-doubles"')
226
+ end
227
+
228
+ it 'should include the doubles boilerplate' do
229
+ @double.to_ruby.should match(/^ArubaDoubles::Double.run\s+do.*end$/m)
230
+ end
231
+
232
+ it 'should include the defined outputs' do
233
+ @double.on %w(--foo), :puts => 'bar'
234
+ @double.to_ruby.should include('on ["--foo"], {:puts=>"bar"}')
235
+ end
236
+
237
+ it 'should not include a block when no output is defined'
66
238
  end
67
- end
68
239
 
69
- describe ArubaDoubles::Double do
70
- it "should be serializable" do
71
- double = ArubaDoubles::Double.new
72
- double.could_receive([])
73
- double.could_receive(["--foo"], :stdout => "foo")
74
- double.could_receive(["--bar"], :stderr => "OOPS!", :exit_status => 255)
75
- loaded_double = ArubaDoubles::Double.new(double.expectations)
76
- loaded_double.run([])
77
- loaded_double.stdout.should be_nil
78
- loaded_double.stderr.should be_nil
79
- loaded_double.exit_status.should be_nil
80
- loaded_double.run(["--foo"])
81
- loaded_double.stdout.should eql("foo")
82
- loaded_double.stderr.should be_nil
83
- loaded_double.exit_status.should be_nil
84
- loaded_double.run(["--bar"])
85
- loaded_double.stdout.should be_nil
86
- loaded_double.stderr.should eql("OOPS!")
87
- loaded_double.exit_status.should eql(255)
240
+ describe '#delete' do
241
+ it 'should delete the executable file if it exists' do
242
+ ArubaDoubles::Double.stub(:bindir).and_return('/tmp/foo')
243
+ double = ArubaDoubles::Double.new('bar')
244
+ File.should_receive(:exists?).with('/tmp/foo/bar').and_return(true)
245
+ FileUtils.should_receive(:rm).with('/tmp/foo/bar')
246
+ double.delete
247
+ end
88
248
  end
89
249
  end
data/spec/spec_helper.rb CHANGED
@@ -1 +1 @@
1
- require 'aruba-doubles/double'
1
+ require 'aruba-doubles'
metadata CHANGED
@@ -1,97 +1,91 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: aruba-doubles
3
- version: !ruby/object:Gem::Version
4
- hash: 17
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 3
10
- version: 0.2.3
11
6
  platform: ruby
12
- authors:
13
- - "Bj\xC3\xB6rn Albers"
7
+ authors:
8
+ - Björn Albers
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-03-19 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-04-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: cucumber
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70348087415700 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 19
29
- segments:
30
- - 1
31
- - 0
32
- - 2
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
33
21
  version: 1.0.2
34
22
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: aruba
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70348087415700
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70348087413900 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2.6.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70348087413900
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70348087413220 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.2.2
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70348087413220
47
+ - !ruby/object:Gem::Dependency
48
+ name: aruba
49
+ requirement: &70348087412140 !ruby/object:Gem::Requirement
40
50
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 3
45
- segments:
46
- - 0
47
- - 4
48
- - 6
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
49
54
  version: 0.4.6
50
55
  type: :development
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
53
- name: guard-cucumber
54
56
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
57
+ version_requirements: *70348087412140
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-cucumber
60
+ requirement: &70348087411000 !ruby/object:Gem::Requirement
56
61
  none: false
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- hash: 5
61
- segments:
62
- - 0
63
- - 7
64
- - 3
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
65
  version: 0.7.3
66
66
  type: :development
67
- version_requirements: *id003
68
- - !ruby/object:Gem::Dependency
69
- name: guard-rspec
70
67
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
68
+ version_requirements: *70348087411000
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: &70348087410280 !ruby/object:Gem::Requirement
72
72
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- hash: 9
77
- segments:
78
- - 0
79
- - 5
80
- - 1
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
81
76
  version: 0.5.1
82
77
  type: :development
83
- version_requirements: *id004
84
- description: Stub command line applications with Cucumber
85
- email:
78
+ prerelease: false
79
+ version_requirements: *70348087410280
80
+ description: Cucumber Steps to double Command Line Applications
81
+ email:
86
82
  - bjoernalbers@googlemail.com
87
83
  executables: []
88
-
89
84
  extensions: []
90
-
91
85
  extra_rdoc_files: []
92
-
93
- files:
86
+ files:
94
87
  - .gitignore
88
+ - .rbenv-version
95
89
  - .rvmrc
96
90
  - Gemfile
97
91
  - Guardfile
@@ -99,55 +93,49 @@ files:
99
93
  - README.md
100
94
  - Rakefile
101
95
  - aruba-doubles.gemspec
102
- - features/double.feature
103
- - features/environment-friendly.feature
96
+ - features/define_output.feature
97
+ - features/double_commands.feature
104
98
  - features/step_definitions/dev_steps.rb
105
99
  - features/support/env.rb
106
100
  - lib/aruba-doubles.rb
107
- - lib/aruba-doubles/api.rb
108
101
  - lib/aruba-doubles/cucumber.rb
109
102
  - lib/aruba-doubles/double.rb
110
- - lib/aruba-doubles/hooks.rb
111
103
  - spec/aruba-doubles/double_spec.rb
112
104
  - spec/spec_helper.rb
113
105
  homepage: https://github.com/bjoernalbers/aruba-doubles
114
106
  licenses: []
115
-
116
107
  post_install_message:
117
108
  rdoc_options: []
118
-
119
- require_paths:
109
+ require_paths:
120
110
  - lib
121
- required_ruby_version: !ruby/object:Gem::Requirement
111
+ required_ruby_version: !ruby/object:Gem::Requirement
122
112
  none: false
123
- requirements:
124
- - - ">="
125
- - !ruby/object:Gem::Version
126
- hash: 3
127
- segments:
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ segments:
128
118
  - 0
129
- version: "0"
130
- required_rubygems_version: !ruby/object:Gem::Requirement
119
+ hash: 2074239273219406016
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
121
  none: false
132
- requirements:
133
- - - ">="
134
- - !ruby/object:Gem::Version
135
- hash: 3
136
- segments:
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ segments:
137
127
  - 0
138
- version: "0"
128
+ hash: 2074239273219406016
139
129
  requirements: []
140
-
141
130
  rubyforge_project:
142
- rubygems_version: 1.8.11
131
+ rubygems_version: 1.8.10
143
132
  signing_key:
144
133
  specification_version: 3
145
- summary: aruba-doubles-0.2.3
146
- test_files:
147
- - features/double.feature
148
- - features/environment-friendly.feature
134
+ summary: aruba-doubles-1.0.0
135
+ test_files:
136
+ - features/define_output.feature
137
+ - features/double_commands.feature
149
138
  - features/step_definitions/dev_steps.rb
150
139
  - features/support/env.rb
151
140
  - spec/aruba-doubles/double_spec.rb
152
141
  - spec/spec_helper.rb
153
- has_rdoc:
@@ -1,88 +0,0 @@
1
- Feature: Double command line applications
2
-
3
- In order to double command line applications
4
- As a developer using Cucumber
5
- I want to use the "I could run" steps
6
-
7
- Scenario: Stub with default stdout, stderr and exit status
8
- Given I could run `foo`
9
- When I successfully run `foo`
10
- Then the stdout should be empty
11
- And the stderr should be empty
12
-
13
- Scenario: Stub stdout
14
- Given I could run `foo` with stdout:
15
- """
16
- hello, world.
17
- """
18
- When I successfully run `foo`
19
- Then the stdout should contain exactly:
20
- """
21
- hello, world.
22
-
23
- """
24
- And the stderr should be empty
25
-
26
- Scenario: Stub stderr
27
- Given I could run `foo` with stderr:
28
- """
29
- error: something crashed!
30
- """
31
- When I successfully run `foo`
32
- And the stdout should be empty
33
- Then the stderr should contain exactly:
34
- """
35
- error: something crashed!
36
-
37
- """
38
-
39
- Scenario: Stub exit status
40
- Given I could run `foo` with exit status 255
41
- When I run `foo`
42
- Then the exit status should be 255
43
- And the stdout should be empty
44
- And the stderr should be empty
45
-
46
- Scenario: Stub exit status and stdout
47
- Given I could run `foo` with exit status 255 and stdout:
48
- """
49
- hello, world.
50
- """
51
- When I run `foo`
52
- Then the exit status should be 255
53
- And the stdout should contain exactly:
54
- """
55
- hello, world.
56
-
57
- """
58
- And the stderr should be empty
59
-
60
- Scenario: Run with unexpected arguments
61
- Given I could run `foo --bar 'hello, world.'`
62
- When I successfully run `foo --bar 'hello, world.'`
63
- Then the stdout should be empty
64
- And the stderr should be empty
65
- When I run `foo --bar hello world`
66
- Then the exit status should not be 0
67
- And the stdout should be empty
68
- And the stderr should contain:
69
- """
70
- Unexpected arguments: ["--bar", "hello", "world"]
71
- """
72
-
73
- Scenario: Stub multiple calls
74
- Given I could run `foo --bar` with stdout:
75
- """
76
- hello, bar.
77
- """
78
- And I could run `foo --baz` with stdout:
79
- """
80
- hello, baz.
81
- """
82
- When I successfully run `foo --bar`
83
- And the stdout should contain "hello, bar."
84
- And the stdout should not contain "hello, baz."
85
- And the stderr should be empty
86
- When I successfully run `foo --baz`
87
- And the stdout should contain "hello, baz."
88
- And the stderr should be empty
@@ -1,29 +0,0 @@
1
- Feature: Environment-friendly
2
-
3
- In order to not mess up my system or tests
4
- As a developer using Cucumber
5
- I want Aruba-Doubles to behave environment-friendly
6
-
7
- Scenario: Create doubles directory only when necessary
8
- When I run `ls`
9
- Then the doubles directory should not exist
10
-
11
- Scenario: Patch the original path only once
12
- Given I could run `ls`
13
- And I could run `ls`
14
- Then the path should include 1 doubles directory
15
-
16
- Scenario: Create doubles directory...
17
- Given I could run `ls`
18
- When I keep the doubles directory in mind
19
-
20
- Scenario: ...and check that it was deleted
21
- Then the previous doubles directory should not exist
22
-
23
- @no-clobber
24
- Scenario: Create doubles directory...
25
- Given I could run `ls`
26
- When I keep the doubles directory in mind
27
-
28
- Scenario: ...and check that it was not deleted
29
- Then the previous doubles directory should exist
@@ -1,73 +0,0 @@
1
- require 'aruba-doubles/double'
2
- require 'shellwords'
3
-
4
- module ArubaDoubles
5
- module Api
6
- def doubled?
7
- !@doubles_dir.nil?
8
- end
9
-
10
- def create_double_by_cmd(cmd, options = {})
11
- arguments = Shellwords.split(cmd)
12
- filename = arguments.shift
13
- create_double(filename, arguments, options)
14
- end
15
-
16
- def create_double(filename, arguments, options = {})
17
- unless doubled?
18
- create_doubles_dir
19
- patch_original_path
20
- end
21
- write_double(filename, get_double(filename, arguments, options))
22
- end
23
-
24
- def write_double(filename, double)
25
- fullpath = File.expand_path(filename, @doubles_dir)
26
- File.open(fullpath, 'w') do |f|
27
- f.puts "#!/usr/bin/env ruby"
28
- f.puts "# Doubled command line application by aruba-doubles\n"
29
- f.puts "require 'rubygems'"
30
- f.puts "require 'cucumber' # Required for Ruby 1.8.7 (see issue #4)"
31
- f.puts "require 'yaml'"
32
- f.puts "require 'aruba-doubles/double'"
33
- f.puts "ArubaDoubles::Double.run! YAML.load %Q{"
34
- f.puts double.expectations.to_yaml
35
- f.puts "}"
36
- end
37
- FileUtils.chmod(0755, fullpath)
38
- end
39
-
40
- def remove_doubles
41
- restore_original_path
42
- remove_doubles_dir
43
- end
44
-
45
- private
46
-
47
- def create_doubles_dir
48
- @doubles_dir = Dir.mktmpdir
49
- end
50
-
51
- def doubles
52
- @doubles ||= {}
53
- end
54
-
55
- def get_double(filename, arguments, options)
56
- doubles[filename] ||= ArubaDoubles::Double.new
57
- doubles[filename].could_receive(arguments, options)
58
- end
59
-
60
- def patch_original_path
61
- @__aruba_doubles_original_path = (ENV['PATH'] || '').split(File::PATH_SEPARATOR)
62
- ENV['PATH'] = ([@doubles_dir] + @__aruba_doubles_original_path).join(File::PATH_SEPARATOR)
63
- end
64
-
65
- def restore_original_path
66
- ENV['PATH'] = @__aruba_doubles_original_path.join(File::PATH_SEPARATOR) unless doubled?
67
- end
68
-
69
- def remove_doubles_dir
70
- FileUtils.rm_r(@doubles_dir) unless @doubles_dir.nil?
71
- end
72
- end
73
- end
@@ -1,3 +0,0 @@
1
- After('~@no-clobber') do
2
- remove_doubles if doubled?
3
- end