DEV Community

Sandor Dargo
Sandor Dargo

Posted on • Edited on • Originally published at sandordargo.com

The Art of Unit Testing by Roy Osherove

The Art of Unit Testing is useful for both beginner unit testers and for those who already have a bit of experience. While the edition I read is with C# examples it is useful and understandable for people who work in other languages. People like me.

It is practical, probably as long as you work with statically typed languages, not like Python or Ruby. In dynamic languages, you can extend basically anything at any point in time. You can change behaviour, replace implementations, hence design for testability is not a concern - as confirmed by the author, Roy Osherove.

In the beginning, the author spends quite some time on defining what is unit testing and he iterates over several definitions reaching the final one:

"A unit test is an automated piece of code that invokes the unit of work being tested, and then checks some assumptions about a single end result of that unit. A unit test is almost always written using a unit testing framework. It can be written easily and runs quickly. It’s trustworthy, readable, and maintainable. It’s consistent in its results as long as production code hasn’t changed."

This also means that if a test uses the real system time, filesystem or a real database, it's not a unit test anymore, but rather an integration test.

I had a discussion about this with other devs, and apparently there are many who accept certain "integration" tests as unit tests as long as they are deterministic and fast enough.

While the author discusses a lot Test Driven Development, Solid principles and so, he claims that we can and should speak about three different areas:

  • unit testing
  • code design
  • test-driven development

According to the author, while test-driven development is useful and has a lot of advantages including more testable code, it won't lead automatically to better architecture. The above mentioned three areas are three different skills that one has to learn. The book focuses only on the first one, unit testing.

I found some interesting ideas in the book. At some points, I was a bit surprised and I thought that the content might be outdated. But maybe it was not out-of-date, just a bit language-specific. As an example, I prefer much more dependency injection compared to introducing inheritance just for the sake of unit testability. In C++, methods are not virtual by default and I prefer not to declare something virtual just in order to make it replaceable. Osherove is much more permissive with introducing inheritance for testing purposes. Again, this might depend on the language.

The book doesn't only help you learn about unit testing, but also gives advice on where to start unit testing in legacy code - you should definitely read Your Code as a Crime Scene - and on how to introduce it in an organization, how to convince people. Though, these are not the main topics. If you want to get more detailed information on introducing changes, I'd advise you to read Driving Technical Change by Terrence Ryan.

On the other hand, on unit testing best practices it really goes into details and if you have hard times to convince your peers about some techniques that you heard before here and there, now you have the perfect reference!

I'd mention one idea from the book, that I have never considered before, not even when I worked with Java and JUnit.

I thought that such a test for asserting that you expect an exception is great:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    List<T> emptyList = new ArrayList<T>();
    Object o = emptyList.get(0);

}
Enter fullscreen mode Exit fullscreen mode

According to Osherove, it's not.

The exception can come from any instruction, not just from the call that you actually want to test. So he prefers the following format instead.

@Test
public void doStuffThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  IndexOutOfBoundsException e = assertThrows(
    IndexOutOfBoundsException.class, foo::doStuff);

  assertThat(e).hasMessageThat().contains("woops!");
}
Enter fullscreen mode Exit fullscreen mode

The point is that your assertion should consider only the call that you expect to throw, no more.

All in all, I liked The Art of Unit Testing for its detailed insights. If you work with C#, most probably it's the goto book on unit testing, but even for other languages, it is more than useful. Read it, adapt it to your language and help your team to unit test better!

Happy testing!

This article has been originally posted on my blog. If you are interested in receiving my latest articles, please sign up to my newsletter and follow me on Twitter.

Top comments (2)

Collapse
 
zakwillis profile image
zakwillis

Hi there. Thanks for letting the trade secret out the bag :). It is a great book, I must have read 4 years ago?
The book goes deep. From limited memory, I think the book puts the point across that unit testing is as much about showing other developers how code units work.
There is a danger in overly fussing about unit testing. Which is why I prefer data Testing.

Collapse
 
icebot47 profile image
icebot47 • Edited

Hi Sandor,

Thx for writing that article,
indeed it is a goto-book if you write in C# ;)

Kind regards,
Simon