---
title: "Unit test readability should not come in place of maintainability - they go hand in hand"
url: "https://osherove.com/blog/2005/7/28/unit-test-readability-should-not-come-in-place-of-maintainab.md"
canonical: "https://osherove.com/blog/2005/7/28/unit-test-readability-should-not-come-in-place-of-maintainab.html"
published: "2005-07-28T23:11:00"
updated: "2005-07-28T23:11:00"
tags: ["Agile Related", "Testing Guidelines"]
author: "Roy Osherove"
description: "Brian Button writes this post about a best practice he believes in when writing unit tests. The main idea in that post is that when you use Setup and TearDown methods in your tests, you're essentially separating parts…"
usage: "Licensed under CC BY 4.0 — reuse, quote and adapt with attribution (Roy Osherove, link to the original page)."
license: "https://creativecommons.org/licenses/by/4.0/"
---

# Unit test readability should not come in place of maintainability - they go hand in hand

By Roy Osherove July 28, 2005 In [Agile Related](https://osherove.com/blog/category/agile-related), [Testing Guidelines](https://osherove.com/blog/category/testing-guidelines)

**Historical article** · Originally published July 28, 2005; technical details may be outdated.

[Brian Button writes this post](http://www.agileprogrammer.com/oneagilecoder/archive/2005/07/23/6261.aspx) about a best practice he believes in when writing unit tests.

The main idea in that post is that when you use Setup and TearDown methods in your tests, you're essentially separating parts of the unit tests into 2 or three areas in the class, thus making the person _reading_ the tests more likely to miss out on some subtleties inside the test method. In short, the test is less readable if there is a "hidden" setup  that occurs before it happens. He also notes this best practice is valid for things that are \*not\* resource based (such as initializing things inside the file system etc..). The final thought in that post says that you would rather have a little duplication in your tests rather than remove readability.

While I do believe that using Setup makes the test less readable, I do **not** believe that a little duplication is OK.

The problem with **code duplication** in your tests is that it soon (very soon) leads to _unmaintainable_ tests.

   You're creating an object instance inside 20-50 tests in your test fixture, and now suddenly the design changes and the class constructor signature changes. Do you _really_ think fixing 50 tests is something a developer will take the time to do? Maybe once. What about the second time this happens? and the third? And what specific point in time will this developer start thinking "_Gosh darn it, these tests are more trouble than their worth, and I have a deadline to reach!_" and finally **let the tests keep on failing**?

Sooner than you think.

I've seen this happen time and time again. With Unit tests the _slightest_ duplication of code is orders of magnitude a bigger problem than duplication in production code might be, because tests come in large numbers on each class or even function.

So, does all this mean you should still use Setup and TearDown? Not necessarily. You can still remove duplication _and_  keep your tests fairly readable by following a few simple rules.

The three rules of test duplication are:

1. Never instantiate objects inside a test - always use a factory method
2. Refactor configuring(preparing for test) object state into shared methods (not static, but common to be used by multiple tests)
3. Refactor duplicated Act-Assert combinations into common methods (very methods)

**Never instantiate objects inside a test - always use a factory method**

instead of

**\[Test\]**

**public void Create()**

**{**

**MyOBject x = new MyObject();**

**}** 

do:

**\[Test\]**

**public void Create()**

**{**

**MyOBject x = makeDefaultMyObject();**

**}**

**private MyObject makeDefaultMyObject()**

**{**

**return new MyOBject();**

**}** 

**Refactor configuring(preparing for test) object state into shared methods (not static, but common to be used by multiple tests)**

instead of

**\[Test\]**

**public void NumOfUsers\_2UsersAdded\_Returns2()**

**{**

**MyOBject x = makeDefaultMyObject();**

**x.AddUser("a","b");**

**x.AddUser("c","d");**

**Assert.AreEqual(1,x.NumOfUsers);**

**}** 

**\[Test\]**

**public void HasUsers\_2Users\_ReturnsTrue()**

**{**

**MyOBject x = makeDefaultMyObject();**

**x.AddUser("a","b");**

**x.AddUser("c","d");**

**Assert.IsTrue(x.HasUsers);**

**}** 

do:

**\[Test\]**

**public void NumOfUsers\_2UsersAdded\_Returns2()**

**{**

**MyOBject x = makeDefaultMyObject();**

**

**init\_AddDefault2UsersToMyObject(x);**

**Assert.AreEqual(2,x.NumOfUsers);**

}** 

**\[Test\]**

**public void HasUsers\_2Users\_ReturnsTrue()**

**{**

**MyOBject x = makeDefaultMyObject();**

****init\_AddDefault2UsersToMyObject(x);**

**Assert.IsTrue(x.HasUsers);**

}

private void init\_AddDefault2UsersToMyObject(MyObject x)

{

x.AddUser("A","b");

x.AddUser("C","D");

}** 

**Refactor duplicated Act-Assert combinations into common methods (very methods)**

Instead of

**\[Test\]**

**public void ParseSum\_3NumbersSingleDigit\_ReturnsSum1()**

**{**

**Calc c = makeDefulatCalculator();**

**int sum = c.ParseSum("1,2,3");**

**Assert.AreEqual(6,sum);**

**}** 

**\[Test\]**

**public void ParseSum\_3NumbersSingleDigit\_ReturnsSum2()**

**{**

**Calc c = makeDefulatCalculator();**

**int sum = c.ParseSum("4,5,6");**

**Assert.AreEqual(15,sum);**

**}** 

**do:**

**\[Test\]**

**public void ParseSum\_3NumbersSingleDigit\_ReturnsSum1()**

**{**

**Calc c = makeDefulatCalculator();**

**int expectedSum=6;**

**verify\_ParseSum(c,"1,2,3", expectedSum);**

**}** 

**\[Test\]**

**public void ParseSum\_3NumbersSingleDigit\_ReturnsSum2()**

**{**

**Calc c = makeDefulatCalculator();**

**int expectedSum=15;**

**verify\_ParseSum(c,"4,5,6", expectedSum);**

**}** 

**private void verify\_ParseSum(Calc c,string input, int expectedSum)**

**{**

**int result = c.ParseSum(input);**

**Assert.AreEqual(expectedSum,result);**

**}**

No Setup or TearDown code present, tests are still readable, but are also still maintainable. Next time a constructor changes - you'll only have to change one method in this test fixture. Next time configuration of an object changes functionality (and thus needs to be modified in all tests requiring such an instance) you only need to change one method (or a small number of common methods) instead of tens or hundreds of tests.

Re-usability and Readability are possible. You just need to follow some guidelines on how to implement them.

## Lessons on this topic

- [Advanced RSpec and Cleaning Up The Specs](https://courses.osherove.com/courses/tdd-and-bdd-in-ruby/advanced-rspec-and-cleaning-up-the-specs/) · TDD and BDD in Ruby

## More in Agile Related

[All posts in Agile Related](https://osherove.com/blog/category/agile-related)

- Aug 9, 2005

    ### [NUnitASP Architecture walkthrough video from Jim Shore](https://osherove.com/blog/2005/8/9/nunitasp-architecture-walkthrough-video-from-jim-shore.html)

- Jul 29, 2005

    ### [Nice article: Agile Top-Down](https://osherove.com/blog/2005/7/29/nice-article-agile-top-down.html)

- Jul 28, 2005

    ### [\[Audio Interview\] Randy Miller on MSF Agile and software modeling techniques](https://osherove.com/blog/2005/7/28/audio-interview-randy-miller-on-msf-agile-and-software-model.html)

- Jul 27, 2005

    ### [Unit Testing Best Practices and guidelines](https://osherove.com/blog/2005/7/27/unit-testing-best-practices-and-guidelines.html)


---

## About the author

**Roy Osherove**: Senior Solutions Architect at AWS working on AI for production and AI-driven development; author of The Art of Unit Testing and Elastic Leadership, with about 20 years of consulting and training on unit testing, TDD, team leadership and continuous delivery.

### Sites

- [RobotPaper](https://robotpaper.ai/): This blog: AI patterns and anti-patterns, AI and coding agents, and engineering leadership.
- [osherove.com](https://osherove.com): Roy's personal site. Author of The Art of Unit Testing and Elastic Leadership; consulting and training on unit testing, TDD, team leadership and continuous delivery, plus about 20 years of blog posts.
- [The Art of Unit Testing](https://www.artofunittesting.com): The Art of Unit Testing, 3rd Edition (Manning): readable, maintainable, trustworthy unit tests, plus TDD workshops in C#, JavaScript, Java and Kotlin.
- [5whys.com](https://www.5whys.com): Roy's writing on software team leadership: self-organizing teams, surviving chaos, learning mode and the Elastic Leadership model.
- [Pipeline Driven](https://pipelinedriven.org): Continuous delivery and DevOps practices: build patterns, pipeline-friendly testing, DevOps metrics and enterprise DevOps.
- [Elastic Leadership](https://www.elasticleadership.com): Elastic Leadership, Roy's framework for software team leaders (survival, learning and self-organizing modes).

### Social

- X: https://x.com/royosherove
- LinkedIn: https://www.linkedin.com/in/osherove/

### Contact

- Contact form: https://forms.gle/jQSx5KoXvrVA1JCK7

### License

Licensed under CC BY 4.0 — reuse, quote and adapt with attribution (Roy Osherove, link to the original page). License: https://creativecommons.org/licenses/by/4.0/
