# Learning TDD With RSpec

This is part 1 of a planned 3 part series.

### Introduction:
The challenge was to implement the code for a checkout system that handles pricing schemes such as “apples cost 50 cents, three apples cost $1.30.”

The basic guidelines on how to implement the kata can be found [here](http://codekata.com/kata/kata09-back-to-the-checkout/) 

The idea is to use the Red, Green, Refactor approach.

Write a failing test, and then write little code to get the failing test to pass and then clean up the code.

### Initialising project:
1. Navigate to an appropriate directory: `~ cd practice`
2. Create a new directory & change to it: `~ mkdir checkout && cd $_`
3. Create and edit Gemfile: `vim Gemfile` (vim is being used as the editor for this project)

```ruby
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'rspec'
```
4.  Press `ctrl + z` to navigate back to terminal and then type `bundle binstubs --all` (including `binstubs --all` after `bundle` is a shortcut to not use `bundle exec` when running the `rspec` command)
5.   Type `bin/rspec --init` to initialise rspec (a `spec` folder will be created) 

### Writing the first test:
1. Create a new file under the spec folder: `touch spec/check_out_spec.rb`
2. Write the first failing test which checks when nothing has been scanned;
```ruby
 RSpec.describe 'checkout' do
   context 'when nothing has been scanned' do
     it 'shows a total of zero' do

       expect(total).to eq 0
     end
   end
 end
```
9. As expected, this test fails when `⎵ + t` is pressed on the keyboard;

```
./bin/rspec spec/check_out_spec.rb

Randomized with seed 59413
F

Failures:

  1) checkout when nothing has been scanned shows a total of zero
     Failure/Error: expect(total).to eq 0

     NameError:
       undefined local variable or method `total' for #<RSpec::ExampleGroups::Checkout::WhenNothingHasBeenScanned "shows a total of zero" (./spec/check_out_spec.rb:3)>
     # ./spec/check_out_spec.rb:5:in `block (3 levels) in <top (required)>'

Finished in 0.00169 seconds (files took 0.04863 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/check_out_spec.rb:3 # checkout when nothing has been scanned shows a total of zero

Randomized with seed 59413


shell returned 1

Press ENTER or type command to continue
```
10. Let us incrementally get this test to pass. The feedback loop with TDD is second to none as the error actually tells us what to do next
```ruby
 RSpec.describe 'checkout' do
   context 'when nothing has been scanned' do
     it 'shows a total of zero' do
       pricing_rules = :Rule
       checkout = CheckOut.new(pricing_rules)
       total = checkout.total

       expect(total).to eq 0
     end
   end
 end
```
In the above code snippet, two lines have been added; `pricing_rules = :Rule` and `checkout = CheckOut.new(pricing_rules)`

The error is now different;
```
~
./bin/rspec spec/check_out_spec.rb

Randomized with seed 59719
F

Failures:

  1) checkout when nothing has been scanned shows a total of zero
     Failure/Error: checkout = CheckOut.new(pricing_rules)

     NameError:
       uninitialized constant CheckOut
     # ./spec/check_out_spec.rb:5:in `block (3 levels) in <top (required)>'

Finished in 0.00174 seconds (files took 0.04954 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/check_out_spec.rb:3 # checkout when nothing has been scanned shows a total of zero

Randomized with seed 59719


shell returned 1

Press ENTER or type command to continue
```

Now we create add a class: `CheckOut` as the code below;
```ruby
 class CheckOut

 end

 RSpec.describe 'checkout' do
   context 'when nothing has been scanned' do
     it 'shows a total of zero' do
       pricing_rules = :Rule
       checkout = CheckOut.new(pricing_rules)
       total = checkout.total

       expect(total).to eq 0
     end
   end
 end
```

Now, this is the error returned;
```
~
./bin/rspec spec/check_out_spec.rb

Randomized with seed 45459
F

Failures:

  1) checkout when nothing has been scanned shows a total of zero
     Failure/Error: checkout = CheckOut.new(pricing_rules)

     ArgumentError:
       wrong number of arguments (given 1, expected 0)
     # ./spec/check_out_spec.rb:9:in `initialize'
     # ./spec/check_out_spec.rb:9:in `new'
     # ./spec/check_out_spec.rb:9:in `block (3 levels) in <top (required)>'

Finished in 0.00264 seconds (files took 0.05216 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/check_out_spec.rb:7 # checkout when nothing has been scanned shows a total of zero

Randomized with seed 45459


shell returned 1

Press ENTER or type command to continue
```

Now we can write code to solve the above error. It would look something like so;
```ruby
 class CheckOut
   def initialize(pricing_rules)

   end

 end

 RSpec.describe 'checkout' do
   context 'when nothing has been scanned' do
     it 'shows a total of zero' do
       pricing_rules = :Rule
       checkout = CheckOut.new(pricing_rules)
       total = checkout.total

       expect(total).to eq 0
     end
   end
 end
```
For this, we get the undefined method `total` as the error shows;
```ruby
~
./bin/rspec spec/check_out_spec.rb

Randomized with seed 46712
F

Failures:

  1) checkout when nothing has been scanned shows a total of zero
     Failure/Error: total = checkout.total

     NoMethodError:
       undefined method `total' for #<CheckOut:0x000000013952bd60>
     # ./spec/check_out_spec.rb:13:in `block (3 levels) in <top (required)>'

Finished in 0.00174 seconds (files took 0.05301 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/check_out_spec.rb:10 # checkout when nothing has been scanned shows a total of zero

Randomized with seed 46712


shell returned 1

Press ENTER or type command to continue
```
To fix the error, add a method: `total` as shown below;
```ruby
class CheckOut
  def initialize(pricing_rules)

  end

  def total

  end
end

RSpec.describe 'checkout' do
  context 'when nothing has been scanned' do
    it 'shows a total of zero' do
      pricing_rules = :Rule
      checkout = CheckOut.new(pricing_rules)
      total = checkout.total

      expect(total).to eq 0
    end
  end
end
```
We now get another error as shown;
```ruby
~
./bin/rspec spec/check_out_spec.rb

Randomized with seed 15684
F

Failures:

  1) checkout when nothing has been scanned shows a total of zero
     Failure/Error: expect(total).to eq 0

       expected: 0
            got: nil

       (compared using ==)
     # ./spec/check_out_spec.rb:18:in `block (3 levels) in <top (required)>'

Finished in 0.00714 seconds (files took 0.05137 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/check_out_spec.rb:13 # checkout when nothing has been scanned shows a total of zero

Randomized with seed 15684


shell returned 1

Press ENTER or type command to continue
```
Now, to get this to pass, we need to change the `total` method to return `0`;
```ruby

class CheckOut
  def initialize(pricing_rules)

  end

  def total
    0
  end
end

RSpec.describe 'checkout' do
  context 'when nothing has been scanned' do
    it 'shows a total of zero' do
      pricing_rules = :Rule
      checkout = CheckOut.new(pricing_rules)
      total = checkout.total

      expect(total).to eq 0
    end
  end
end
```
Below is the output of the passing test;
```ruby
~
./bin/rspec spec/check_out_spec.rb

Randomized with seed 9210
.

Finished in 0.00187 seconds (files took 0.04874 seconds to load)
1 example, 0 failures

Randomized with seed 9210


Press ENTER or type command to continue
```
This is just one of the tests though. Other tests need to be done i.e. `when A has been scanned`, `when B has been scanned`, `when C has been scanned`, `when D has been scanned`

This concludes part 1.

In part 2, we will tackle how to handle tests for the scanning of A, B, C & D.

In part 3, we will do some refactoring to make the code better.

Shout out to [Rob](https://twitter.com/purinkle) for his immense assistance on this and making it easy to comprehend.
