Bank Account Simulation Class With Self-Written Tests at 90% Coverage

Quick Overview

A pair-programming exercise: implement a class that simulates bank account operations, write your own tests reaching at least 90% coverage, and then extend the working code with a new requirement. It tests invariant-driven validation, all-or-nothing transfers, meaningful test design, and clean incremental changes.

Bank Account Simulation Class With Self-Written Tests at 90% Coverage

Company: Shopify

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Technical Screen

In a pair-programming round you receive a small repository, clone it, and work in your own IDE while sharing your screen. The task is to write a class that simulates a banking system's account operations. You also write your own automated tests, which must reach at least 90% code coverage. If time remains after all requirements are implemented, the interviewer picks one bonus requirement, and you extend your existing code to meet it. The full requirement list comes with the repository and is not reproduced here. For practice, assume this baseline for an in-memory bank: - open an account with a unique id and a zero balance; - deposit a positive amount into an account; - withdraw a positive amount from an account; - transfer a positive amount from one account to another; - query an account's balance. ### Clarifying Questions - What should happen on a withdrawal or transfer that exceeds the balance: reject it, or allow an overdraft up to some limit? - Should failures be reported by raising exceptions or by returning a status value? - How should money be represented: integer minor units, a decimal type, or floating point? - Is a transfer to the same account, or to or from an unknown account, an error? - Which language and test framework does the repository use, and is coverage measured by line or by branch? ### Part 1 — Implement the Account Class Implement the baseline operations with clear invariants, in code you would be comfortable extending under time pressure. ```hint Protect the invariants first List what must always be true of the bank's state, such as balances never becoming invalid and a failed transfer changing nothing. Then make every operation validate its inputs before it mutates anything. ``` #### What This Part Should Cover - Money representation and input validation. - All-or-nothing behavior for transfers and consistent error reporting. - Readable structure that leaves room for a later requirement. ### Part 2 — Test to at Least 90% Coverage Write automated tests for your class and show that they reach at least 90% coverage. ```hint Let the error paths drive coverage The branches a happy-path test never executes are mostly the validation and failure paths. Plan one test per rule in your invariants, and read the coverage report's list of missed lines rather than only its percentage. ``` #### What This Part Should Cover - Tests for the normal flow, every rejection path, and the state after a failed operation. - How coverage is measured and reported, and what the number does and does not prove. - Test structure that stays fast and readable during a live session. ### Part 3 — Extend With a Bonus Requirement The interviewer picks one bonus requirement, which you must implement on top of your existing code. The bonus options are not listed here. For practice, assume the chosen requirement is a per-account transaction history that records every successful deposit, withdrawal, and transfer and can be queried in order. ```hint Change the write paths in one place Look for the single point where every successful balance change already passes, and consider recording history there instead of in each public method. ``` #### What This Part Should Cover - Integrating the new requirement without breaking existing behavior or tests. - What each history record contains, including how a transfer appears on both accounts. - Additional tests that keep coverage at or above the target. ### What a Strong Answer Covers - Correct, validated operations whose failures leave the state unchanged. - A test suite that verifies behavior rather than only executing lines, with the coverage target met and demonstrated. - Incremental, communicative work suited to a screen-shared session: small steps, frequent test runs, and stated trade-offs. - An extension that fits the existing design instead of being bolted onto every method. ### Follow-up Questions 1. How would you make transfers safe if several threads call the bank at the same time? 2. Your suite reaches 95% line coverage, but a bug remains in the overdraft check. How could that happen, and what testing technique would catch it? 3. How would you persist the accounts and history so that a crash in the middle of a transfer cannot lose or duplicate money?

Overview: A pair-programming exercise: implement a class that simulates bank account operations, write your own tests reaching at least 90% coverage, and then extend the working code with a new requirement. It tests invariant-driven validation, all-or-nothing transfers, meaningful test design, and clean incremental changes.

|Home/Software Engineering Fundamentals/Shopify
Shopify logo
Shopify
Sep 7, 2026
mediumSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
0
0

In a pair-programming round you receive a small repository, clone it, and work in your own IDE while sharing your screen. The task is to write a class that simulates a banking system's account operations. You also write your own automated tests, which must reach at least 90% code coverage. If time remains after all requirements are implemented, the interviewer picks one bonus requirement, and you extend your existing code to meet it.

The full requirement list comes with the repository and is not reproduced here. For practice, assume this baseline for an in-memory bank:

  • open an account with a unique id and a zero balance;
  • deposit a positive amount into an account;
  • withdraw a positive amount from an account;
  • transfer a positive amount from one account to another;
  • query an account's balance.

Clarifying Questions Guidance

  • What should happen on a withdrawal or transfer that exceeds the balance: reject it, or allow an overdraft up to some limit?
  • Should failures be reported by raising exceptions or by returning a status value?
  • How should money be represented: integer minor units, a decimal type, or floating point?
  • Is a transfer to the same account, or to or from an unknown account, an error?
  • Which language and test framework does the repository use, and is coverage measured by line or by branch?

Part 1 — Implement the Account Class

Implement the baseline operations with clear invariants, in code you would be comfortable extending under time pressure.

What This Part Should Cover Guidance

  • Money representation and input validation.
  • All-or-nothing behavior for transfers and consistent error reporting.
  • Readable structure that leaves room for a later requirement.

Part 2 — Test to at Least 90% Coverage

Write automated tests for your class and show that they reach at least 90% coverage.

What This Part Should Cover Guidance

  • Tests for the normal flow, every rejection path, and the state after a failed operation.
  • How coverage is measured and reported, and what the number does and does not prove.
  • Test structure that stays fast and readable during a live session.

Part 3 — Extend With a Bonus Requirement

The interviewer picks one bonus requirement, which you must implement on top of your existing code. The bonus options are not listed here. For practice, assume the chosen requirement is a per-account transaction history that records every successful deposit, withdrawal, and transfer and can be queried in order.

What This Part Should Cover Guidance

  • Integrating the new requirement without breaking existing behavior or tests.
  • What each history record contains, including how a transfer appears on both accounts.
  • Additional tests that keep coverage at or above the target.

What a Strong Answer Covers Guidance

  • Correct, validated operations whose failures leave the state unchanged.
  • A test suite that verifies behavior rather than only executing lines, with the coverage target met and demonstrated.
  • Incremental, communicative work suited to a screen-shared session: small steps, frequent test runs, and stated trade-offs.
  • An extension that fits the existing design instead of being bolted onto every method.

Follow-up Questions Guidance

  1. How would you make transfers safe if several threads call the bank at the same time?
  2. Your suite reaches 95% line coverage, but a bug remains in the overdraft check. How could that happen, and what testing technique would catch it?
  3. How would you persist the accounts and history so that a crash in the middle of a transfer cannot lose or duplicate money?
Loading comments...