A practical demonstration of Test-Driven Development using a string calculator implementation in TypeScript and Jest.
This calculator handles string inputs with various delimiters and demonstrates TDD principles through incremental development, as instructed in https://osherove.com/tdd-kata-1/.
- Basic string-to-number addition (by default)
- Multiple numbers support, initially delimited by
,
- Handle new-line
\n
between numbers - Custom delimiter support, when input starts with
//
- Negative number validation (throws exception)
- [Bonus] Upper limit of 1000 for numbers
- [Bonus] Handle custom delimiters of multiple lengths
- [Bonus] Handle multiple custom delimiters
- [Bonus] Handle multiple custom delimiters of multiple lengths
- Perform multiplication on
*
delimiter
- TypeScript 5.7+
- Jest 29.7
- Clone the repository
- Run
npm install
to install dependencies - Run
npm test
to execute test suite
const calculator = new Calculator();
// Basic usage
calculator.add("1,2,3"); // Returns 6
// Custom delimiter
calculator.add("//;\n1;2;3"); // Returns 6
// Multiple custom delimiters
calculator.add("//[***][%%%]\n1***2%%%3"); // Returns 6
// Multiplication usage
calculator.add("//*\n1*2*3*4"); // Returns 24
# Run tests once
npm test
# Run tests in watch mode
npm run test:watch
The project includes GitHub Actions workflow that:
- Runs on multiple Node.js versions (18.x, 20.x, 22.x)
- Executes test suite on every push and PR
The calculator was built iteratively:
- Started with simplest case (empty string)
- Added single number support
- Evolved to handle multiple numbers
- Implemented custom delimiters
- Added validation rules via Regular Expression
Every feature was developed following the clean TDD principles:
1. Write failing test
2. Implement minimal code
3. Refactor while keeping tests green
The implementation handles:
- Empty strings
- Custom delimiters
- Multiple delimiters
- Negative numbers
- Numbers > 1000
- Always write tests first
- Keep functions focused and small
- Consider edge cases in tests
- Refactor after getting green tests
Each class (methods in this case) has a single responsibility, which is to handle a specific task.
Software entities (classes, modules, methods, etc.) should be open for extension, but closed for modification.
Subtypes must be substitutable for their base types.
Clients should not be forced to depend on methods they do not use.
High-level modules should not depend on low-level modules. Both should depend on abstractions.