Reading and Writing to the Database

When we modified the Account earlier, we made it write out each transaction message as amount,account_number. The TransactionProcessor needs the account_number to find an account in the database and update its balance:

databases/01/lib/transaction_processor.rb
 
require_relative ​'transaction_queue'
 
require_relative ​'account'
 
 
transaction_queue = TransactionQueue.new
 
puts ​"transaction processor ready"
 
loop ​do
 
transaction_queue.read ​do​ |message|
 
sleep 1
*
transaction_amount, number = message.split(/,/)
*
account = Account.find_by_number!(number.strip)
*
new_balance = account.balance + transaction_amount.to_i
*
account.balance = new_balance
*
account.save
 
end
 
end

However, looking at the message files in the messages directory, we see that the account numbers are missing. That’s because we haven’t assigned an account number anywhere! Let’s go back to the code where we instantiate an Account and make it assign a number, and while we’re at it, let’s save it to the database using ActiveRecord’s create! method:

databases/02/features/support/world_extensions.rb
 
def​ my_account
 
@my_account ||= Account.create!(:number => ​"test"​, :balance => 0)
 
end

The step that failed is now passing, and we’re left with a last failing step:

 
Starting transaction_processor in ~/message_queues/01 with
 
'ruby lib/transaction_processor.rb'
 
transaction processor ready
 
Server transaction_processor (94557) is up.
 
Feature: Cash Withdrawal
 
 
Scenario: Successful withdrawal from an account in credit
 
Given my account has been credited with $100
 
When I withdraw $20
 
Then $20 should be dispensed
 
~/.rvm/gems/ruby-1.9.3-p194@hwcuc/gems/activerecord-3.2.7/
 
lib/active_record/relation/finder_methods.rb:267:in `find_by_attributes':
 
Couldn't find Account with number = (ActiveRecord::RecordNotFound)
 
from ~/.rvm/gems/ruby-1.9.3-p194@hwcuc/gems/activerecord-3.2.7/
 
lib/active_record/dynamic_matchers.rb:45:in `method_missing'
 
from lib/transaction_processor.rb:12:in `block (2 levels) in <main>'
 
from ~/databases/02/lib/transaction_queue.rb:21:in `read'
 
from lib/transaction_processor.rb:8:in `block in <main>'
 
from lib/transaction_processor.rb:7:in `loop'
 
from lib/transaction_processor.rb:7:in `<main>'
 
And the balance of my account should be $80
 
 
expected: 80
 
got: 0
 
(compared using ==)
 
(RSpec::Expectations::ExpectationNotMetError)
 
./features/step_definitions/account_steps.rb:7
 
./features/support/async_support.rb:8
 
./features/support/async_support.rb:6
 
./features/support/async_support.rb:6
 
./features/step_definitions/account_steps.rb:7
 
features/cash_withdrawal.feature:6
 
 
Failing Scenarios:
 
cucumber features/cash_withdrawal.feature:2
 
 
1 scenario (1 failed)
 
4 steps (1 failed, 3 passed)
 
0m2.260s
 
Shutting down transaction_processor (94557)
 
Server transaction_processor (94557) is shut down

We expected the balance to be $80, but it looks like it’s still $0. Where did the money go? Let’s look in the database.

 
$ ​sqlite3 db/bank.db
 
sqlite>​ select * from accounts;
 
1|test|80
 
sqlite>​ .quit

Phew! The money is safe in our account. It turns out the step failed because we are still looking at the original instance of the account record—stored in @my_account—from when we created it with a zero balance. Even though the underlying database row has been modified by the TransactionProcessor, ActiveRecord doesn’t know that the record we have is out-of-date, so we need to tell it to reload the record from the database:

databases/03/features/step_definitions/account_steps.rb
 
Then /^the balance of my account should be (#{CAPTURE_CASH_AMOUNT})$/ ​do​ |amount|
 
eventually { my_account.reload.balance.should eq(amount) }
 
end

Let’s run the scenario again:

 
Starting transaction_processor in ~/message_queues/01 with
 
'ruby lib/transaction_processor.rb'
 
transaction processor ready
 
Server transaction_processor (94557) is up.
 
Feature: Cash Withdrawal
 
 
Scenario: Successful withdrawal from an account in credit
 
Given my account has been credited with $100
 
Validation failed: Number has already been taken
 
(ActiveRecord::RecordInvalid)
 
./features/support/world_extensions.rb:20
 
./features/step_definitions/account_steps.rb:2
 
features/cash_withdrawal.feature:3
 
When I withdraw $20
 
Then $20 should be dispensed
 
And the balance of my account should be $80
 
 
Failing Scenarios:
 
cucumber features/cash_withdrawal.feature:2
 
 
1 scenario (1 failed)
 
4 steps (1 failed, 3 skipped)
 
0m0.060s
 
Shutting down transaction_processor (94557)
 
Server transaction_processor (94557) is shut down

This time the first step in our scenario, which ran perfectly fine the last time we run the scenario, has failed. We have stumbled upon one of the most common problems of automated tests for a system using a database. The previous test run left data in the database, and running it again makes it fail. We can’t create an account because the Account number has to be unique, and a row for that Account was left behind by the previous test run. We have a leaky scenario!

The good thing about common problems is that there often is a common solution. We have to make sure each scenario starts with a clean database. There are two strategies to achieve this—transaction and truncation. We’ll explore both, starting with transactions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.82.217