aruba-doubles 0.2.3 → 1.0.0

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