This repository reflects Makers Academy Week 10 project Bank Tech Test. The general overview of this challenge is to create a bank account manager.
- Features of Note
- Specification
- Getting Started
- Running the Program
- Development Process
- Project Conclusions
- Built With
- Author(s)
- License(s)
- Acknowledgements
- Full-blown feature test, using Ruby's poorly documented
PTY
package. The feature test (integrated in RSpec) opens the program from scratch in a pseudoterminal and executes the acceptance criteria tests specified in exactly the same way a user would. - Automatic test runs using GitHub Actions. No third-party (Marketplace) code is used to achieve this.
- True deep copy (using
Marshal.load(Marshal.dump(array))
) to create a clone of the@transactions
array. This is required because the array's elements individual transactions haveDateTime
objects. Ruby'sdup
,deep_dup
andclone
methods are all shallow copies. - The date of transactions is stored as a
DateTime
object, rather than as a string. It seems best practice to store dates in an appropriate object, especially because this compartmentalisation allows easy reformatting (usingstrftime
) and greater precision (e.g. hours and minutes) should that be desirable in the future. - Transactions are stored as hashes, rather than strings. This makes it easier to reference the characteristics (
:date
, and:sum
) of each transaction.
The specification of this tech test is as follows:
- You should be able to interact with your code via a REPL like IRB or the JavaScript console. (You don't need to implement a command line interface that takes input from STDIN.)
- Deposits, withdrawal.
- Account statement (date, amount, balance) printing.
- Data can be kept in memory (it doesn't need to be stored to a database or anything).
Given a client makes a deposit of 1000 on 10-01-2012, and
A deposit of 2000 on 13-01-2012, and
A withdrawal of 500 on 14-01-2012;
When she prints her bank statement,
Then she would see:
date || credit || debit || balance
14/01/2012 || || 500.00 || 2500.00
13/01/2012 || 2000.00 || || 3000.00
10/01/2012 || 1000.00 || || 1000.00
This project requires Ruby 3.0.0. If you do not have Ruby 3.0.0, install it using these instructions.
- Clone or fork this repository.
- Install the necessary Gems from the
Gemfile
by executingbundle install
.
- Move your working directory to the project directory (
/bank_tech_test
). - Start the program by opening
account.rb
in your REPL of choice. - Create a new account by executing
account = Account.new
.
Deposit funds to the account by executing account.deposit(sum, "date")
, where the sum
parameter is a non-optional positive integer, and the date
parameter is a non-optional argument. To supply a date
argument, use integers for day, month and year in the format dd-mm-yyyy
.
Withdraw funds from the account by executing account.withdraw(sum, "date")
, where the sum
parameter is a non-optional positive integer, and the date
parameter is a non-optional argument. To supply a date
argument, use integers for day, month and year in the format dd-mm-yyyy
.
Print an account statement to console by executing account.print_statement
. Account statements are prefixed with a header, and transactions are printed in reverse order of input.
My approach to this problem relied on TDD, BDD, encapsulation and the SRP in order to create modular code that could be tested in independent units as well as collectively in features. The problem itself is simple; with no GUI it was not necessary to structure an MVC/MVP model. I split responsibilities into several classes so as to improve readability and modularity. I tried to balance the SRP against oversimplification leading to an excessive number of component classes.
To keep the code readable, I prioritised thoughtful naming of objects to reduce the need to explain in comments the purpose of each object and method.
The requirements can be parsed in several simple user stories.
Actions are italic. Nouns are bold. Attributes of nouns are bold italics.
As a user,
So that I can add money to my account,
I’d like to be able to deposit a sum in my account.
As a user,
So that I can remove money from my account,
I’d like to be able to withdraw a sum from my account.
As a user,
So that I know when I added or removed money from my account,
I’d like to be able to add a date to each transaction.
As a user,
So that I know how much money is in my account,
I’d like to be able to print my account's balance.
As a user,
So that I can see my account's transactions,
I’d like to be able to print in reverse chronological order my account's transactions.
As per Class Responsibility Collaborator modelling, there are three obvious areas of responsibility, and therefore three classes - Account, Transaction and Printer.
Class: Account
Responsibility | Collaborators |
---|---|
Knows own transactions | Transactions |
Class: Transactions
Responsibility | Collaborators |
---|---|
Knows own transaction dates | |
Knows own transaction sums | |
Knows own balance |
Class: Printer
Responsibility | Collaborators |
---|---|
Knows transactions | Account |
The relationship between these classes can be summarised in this Domain Model Diagram:
TDD was used to structure Unit Tests. These were employed to test individual classes and their methods to ensure that they interacted as expected. RSpec was instructed (using the --format documentation
and --color
arguments to provide verbose feedback on passing and failing tests.
BDD was used to structure Feature Tests. After unit tests were created to test methods and classes in isolation, feature tests were employed to test the entire program.
The code was continuously tested throughout development to ensure development was proceeding as expected, and that changes during development did not impact previously tested units and features. This was overseen in part by a CI/CD workflow built on GitHub Actions.
The code is tested using RSpec, with coverage checked using SimpleCov. The addition of a GitHub Actions Workflow (as specified in deployment_ci.yml
) assists with determining that checks are passed. Every time a commit is pushed to GitHub, the workflow runs creates an environment, installs the relevant gems, and runs the full suite of Rubocop and RSpec tests.
Refactoring was performed after the completion of any individual unit, and periodically throughout development, in order to simplify the code. The aim was to keep the code simple and readable, rather than as compact as possible. To check that the resulting code conformed to the Ruby style guide, it was parsed before each commit through Rubocop (integrating Rubocop-rspec), a linter.
As per the instructions, this program runs exclusively in a REPL. It is pictured below executing the instructions in the Acceptance Criteria.
I suggest implementing some additional features:
- Suppressing the output of the terminal to reduce clutter.
- Making the
date
argument optional by setting a default parameter (DateTime.now
). - Sorting the transaction array elements into reversed chronological order - compensating for input of transactions out of chronological order - before printing. This would require a shift to dynamic calculation of the
balance
value. - Rejection of withdrawals when funds would be insufficient to cover them (returning error
Insufficient funds available!
). - Code hardening to improve resilience against erroneous input, and provide more helpful error messages.
- Justification of tables to align columns to the widest content.
This program's dependencies are minimal and relate solely to testing. They were chosen for their ubiquity and self-contained nature, so that they could be specified as require: false
and required only in the test
environment to reduce their impact on the program's speed. For clarity, all dependencies are explicitly invoked by the Gemfile
.
- Ruby, courtesy of Yukihiro Matsumoto.
- RSpec, courtesy of Jon Rowe, Benoit Tigeot, Phil Pirozhkov, Xavier Shay and Yuji Nakayama.
- Rubocop and Rubocop-RSpec, both courtesy of Bozhidar Batsov.
- SimpleCov, courtesy of Christoph Olszowka.
- SimpleCov-Console, courtesy of Chetan Sarva.
Authored by Joshua Sinyor.
This project is licensed under the MIT License.
- Diagram(s) generated with Diagram.codes Studio.
- Table of contents generated with markdown-toc.
- Shield badges generated with Shields.io.