Character Copy

Level: Intermediate 30–60 min

Concepts: Mocking

Solutions: C# | TypeScript | Python


In the interest of better understanding how to isolate dependencies through mocking frameworks, write a character copier class that reads characters from a source and copies them to a destination. It must copy and write one character at a time.

To do this create a Copier class that takes in a ISource and IDestination. ISource has one method char ReadChar() and IDestination has one method void WriteChar(char c). The Copier class has one method void Copy() that when called reads from the ISource one character at a time and write to IDestination as seen in Figure 1 below.

The copying and writing is done character at a time until a newline (‘\n’) is encountered. Then the processing stops without writing the newline. Only the Copier class may exist as a concrete. You are to use a mocking framework to implement ISource and IDestination.

Character Copy Diagram

Bonus

Once you have a single character working add a new method to both the ISource and IDestination that take will read and write multiple characters at a time. E.g. ReadChars(int count), WriteChars(char[] values). Be mindful not to read beyond the newline when copying between source and destination.

Solutions

You can find all language solutions here Character Copy Solutions

Or you can select a specific language below.

Reference Walkthrough

Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/character-copy. Six scenarios — mapped 1:1 across all three languages — cover empty input, single character, multi-character, whitespace preservation, no-read-past-terminator, and exact-write-count. Each implementation defines two one-method collaborator interfaces (Source with readChar / read, and Destination with writeChar / write) alongside a three-line copy procedure that loops until '\n' is read and never writes the terminator.

This kata ships in Agent Full-Bake mode at high gear. It is the one F1 kata whose SUT is not a pure function — it’s Kent Beck’s classic “mocking” kata, where the copier’s behaviour is the sequence of calls it makes to its collaborators, so you cannot drive it without test doubles. The reference deliberately uses hand-rolled fakes (StringSource, RecordingDestination) rather than a mocking framework: a two-method interface with a five-line fake is clearer than any mock.Setup().Returns(...) chain, and assertions on destination.written read as a sentence about the domain (“the destination wrote ‘abc’”) rather than about call sequences. F3 katas like bank-account/ apply the same collaborator-interface-plus-fake discipline at larger scale — this F1 is where the pattern is introduced in its smallest form. The bonus batch operations (readChars(n) / writeChars(chars)) are intentionally out of scope. See the repo’s Gears section for when high gear is the right call.


  • Test Deletion Is a Privileged Operation
    The cheapest way for an agent to make a failing test pass is to delete it. That is logical for the agent and catastrophic for the codebase. Tests are append-only by default. Deletion needs a human author, a separate commit, and a separate review.
  • Refactor Was Always Where Compounding Lived
    Red and green were the visible two thirds of TDD. Refactor was the optional polish. Codebases that compound did the refactor pass; codebases that accrete did not. Agents skip refactor by default because it has no green-bar reward, and a year of skipped refactors is how a codebase loses its shape.
  • The Instruction File Is Not the Discipline
    Teams that want their agent to do TDD reach for the instruction file. The TDAD paper measured this directly: adding a 'do TDD' instruction raised regressions by nearly two-thirds. Contextual test discovery cut them by seventy percent. The instruction is theatre. The codebase's test surface is the discipline.