# Step 3 - Interacting with objects

## Response setup

Okay, so we have a basic `withdraw` method for our Atm class. It's a good start. Now, if we have a look at the requirements we initially got from our client, we see that a successful withdraw should generate a response in the form of a Hash.The user story for the response is:

```
As a User               
In order to know if my withdrawal was successful or unsuccessful               
I want to receive a message with my withdrawal details
```

**This hash is the equivalent of a receipt that the Atm prints out in the real life.** It should look like this if the transaction was successful:

```ruby
{ status: true, message: 'success', date: '2016-01-30', amount: 35, bills: [20,10,5]}
```

For unsuccessful transactions, it should look like this:

```ruby
{ status: false, message: '[reason for failure e. e. wrong pin]', date: '2016-01-30'}
```

Let's break this down.

`status`

* Can be `true` or `false` depending if the transaction was successful.

`message`

* A message to the user. We can set that to `success` when the transaction was successful and to something else if we for some reason can not perform the transaction.&#x20;

`date`

* The date of the transaction - simply today's date.

`amount`

* Visible only when transaction was successful.&#x20;
* Simply the amount that was withdrawn.

`bills`

* Visible only when transaction was successful.
* An array of bills that was dispatched by the ATM. This symbolize the actual cash you would get in real life.&#x20;

## Testing the happy path

The first test we will write is the so called "Happy Path". We know that a transaction can either be successful or rejected for some reason. We'll get back to the rejections (that is the lion-share of the work that lies ahead of us). At this stage, let's focus on a simple successful transaction.

Let's start with preparing our test.

The ATM needs to interact with another class - we will call it `Account`. The Account class will symbolize both the bank account and a card we can use in the ATM (there is no need to create both an Account class _and_ a Card class for the sake of this prototype).

```
As an ATM operator          
In order for the right person to make a withdrawal            
I want each person to have an separate account
```

However, we have not created that class yet, so in out `atm_spec` we will use a so called `instance_double` in order to be able to test the functionality. Doubles are objects that can be used as stand-ins for instances of other classes (hence the name `instance_double`). Even if they still are not defined (as in our case). We will go over doubles more extensively further down the road in the camp. You can think of doubles as "fake" objects that we use for testing. We don't want to build the `Account` class yet, so we'll just make a fake one for now.

Let's define a `class_double` in our spec and give it a name of `account`. We'll give our `account` a `@balance` of `100`. Then we'll be able to use this in our testing.

!FILENAME spec/atm\_spec.rb

```ruby
describe Atm do
  let(:account) { instance_double('Account') }

  before do
    # Before each test we need to add an attribute of `balance`
    # to the `account` object and set the value to `100`
    allow(account).to receive(:balance).and_return(100)
    # We also need to allow `account` to receive the new balance
    # using the setter method `balance=`
    allow(account).to receive(:balance=)
  end
  [...]
end
```

Okay, we want the `withdraw` method to have access to the `account` object in order to know things about it. Things like a `balance` for instance, right? **The ATM needs to know if there are enough funds in the account before it clears the transaction.**

```
As a ATM operator           
In order ensure that an Acccount holder can only withdraw funds that he has balance for           
I want to allow a withdrawal only if there are sufficient funds in the account
```

First we will write a test and then we will modify the implementation code.

**Note:** _Make sure that you read the comments in the_ `it` _block below but **do not include them** in your spec._

!FILENAME spec/atm\_spec.rb

```ruby
describe Atm do
  [...]
  it 'allow withdraw if account has enough balance.' do
    # We need to tell the spec what to look for as the responce
    # and store that in a variable called `expected_outcome`.
    # Please note that we are omitting the `bills` part at the moment,
    # We will modify this test and add that later.

    expected_output = { status: true, message: 'success', date: Date.today, amount: 45 }

    # We need to pass in two arguments to the `withdraw` method.
    # The amount of money we want to withdraw AND the `account` object.
    # The reason we pass in the `account` object is that the Atm needs
    # to be able to access information about the `accounts` balance
    # in order to be able to clear the transaction.
    expect(subject.withdraw(45, account)).to eq expected_output
  end
end
```

Now, make sure you run this spec and study the error messages from Rspec carefully.

In order to make this pass, we need to modify the `withdraw` method in our implementation code. Again, be mindful of the comments in the code below.

!FILENAME spec/atm\_spec.rb

```ruby
def withdraw(amount, account)
  # We will be using Ruby's `case`- `when` - `then` flow control statement
  # and check if there is enough funds in the account
  case
  when amount > account.balance
    # we exit the method if the amount we want to withdraw is 
    # bigger than the balance on the account
    return
  else
    # If it's not, we perform the transaction
    # We DEDUCT the amount from the Atm's funds
    @funds -= amount
    # We also DEDUCT the amount from the accounts balance
    account.balance = account.balance - amount
    # and we return a responce for a successfull withdraw.
    { status: true, message: 'success', date: Date.today, amount: amount }
  end
end
```

Run your specs again.

Note that your first spec, the one that was passing just a moment ago is failing now. The error message tells you that you have passed in 1 argument but the method expects 2. Why is that?

Well, we have modified the `withdraw` method since we wrote that first spec. Not the method is expecting the `account` object as well as the amount we want to withdraw to be passed in as arguments. So we need to modify that spec and add account as an argument. (See the modified `expect` statement below).

!FILENAME spec/atm\_spec.rb

```ruby
[...]
it 'funds are reduced at withdraw' do
  subject.withdraw(50, account)
  expect(subject.funds).to eq 950
end
[...]
```

**Now, everything should go green when you run your tests.**
