Last active
August 29, 2015 14:20
-
-
Save SamirTalwar/7cbece56643ce5d5c5f4 to your computer and use it in GitHub Desktop.
Bank Account Kata with Transactions in F#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module BankAccount | |
| module Prod = | |
| type Result<'T> = | |
| | Success of 'T | |
| | Failure of string | |
| let (|>>) (result: Result<'T>) (f: 'T -> Result<'U>) = | |
| match result with | |
| | Success(value) -> f value | |
| | Failure(error) -> Failure(error) | |
| type Currency = | |
| | USD | |
| override self.ToString() = | |
| match self with | |
| USD -> "USD" | |
| type Account = { Amount: (int * Currency) } | |
| let newAccount = { Amount = (0, USD) } | |
| let deposit depositAmount currency { Amount = (oldAmount, _) } = { Amount = (oldAmount + depositAmount, USD)} | |
| let withdraw withdrawalAmount currency { Amount = (oldAmount, _) } : Result<Account> = | |
| match oldAmount - withdrawalAmount with | |
| | newAmount when newAmount < 0 -> Failure("This bank account cannot be overdrawn.") | |
| | newAmount -> Success { Amount = (newAmount, USD) } | |
| let transfer amount currency bankAccountA bankAccountB : Result<Account * Account> = | |
| withdraw amount currency bankAccountA |>> (fun newBankAccountA -> | |
| let newBankAccountB = deposit amount currency bankAccountB | |
| Success (newBankAccountA, newBankAccountB)) | |
| module Tests = | |
| open Prod | |
| open FsCheck | |
| open NUnit.Framework | |
| [<Test>] | |
| let ``a new account has a balance of 0``() = | |
| let bankAccount = newAccount | |
| Assert.AreEqual ((0, USD), bankAccount.Amount) | |
| [<Test>] | |
| let ``money can be deposited into a bank account``() = | |
| let bankAccount = newAccount |> deposit 10 USD | |
| Assert.AreEqual ((10, USD), bankAccount.Amount) | |
| [<Test>] | |
| let ``money deposited into a bank account should be added to current amount``() = | |
| let bankAccount = newAccount |> deposit 10 USD |> deposit 5 USD | |
| Assert.AreEqual((15, USD), bankAccount.Amount) | |
| [<Test>] | |
| let ``money can be withdrawn from a bank account``() = | |
| let (Success(bankAccount)) = newAccount |> deposit 50 USD |> withdraw 20 USD | |
| Assert.AreEqual({ Amount = (30, USD) }, bankAccount) | |
| [<Test>] | |
| let ``a bank account cannot be overdrawn``() = | |
| let (Failure(error)) = newAccount |> deposit 50 USD |> withdraw 100 USD | |
| Assert.AreEqual("This bank account cannot be overdrawn.", error) | |
| [<Test>] | |
| let ``multiple withdrawals can be chained``() = | |
| let (Success(bankAccount)) = newAccount |> deposit 1000 USD |> withdraw 500 USD |>> withdraw 200 USD | |
| Assert.AreEqual((300, USD), bankAccount.Amount) | |
| [<Test>] | |
| let ``money can be transferred from one account to another``() = | |
| let previousBankAccountA = newAccount |> deposit 500 USD | |
| let previousBankAccountB = newAccount |> deposit 200 USD | |
| let (Success((bankAccountA, bankAccountB))) = transfer 100 USD previousBankAccountA previousBankAccountB | |
| Assert.AreEqual((400, USD), bankAccountA.Amount) | |
| Assert.AreEqual((300, USD), bankAccountB.Amount) | |
| [<Test>] | |
| let ``money can't be transferred from one account to another if there aren't enough funds``() = | |
| let previousBankAccountA = newAccount |> deposit 500 USD | |
| let previousBankAccountB = newAccount |> deposit 200 USD | |
| let (Failure(error)) = transfer 600 USD previousBankAccountA previousBankAccountB | |
| Assert.AreEqual("This bank account cannot be overdrawn.", error) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment