views:

547

answers:

10

Code that is untestable really annoys me. The following things make oo-code untestable:

  • global states, e.g., the Singleton Design Pattern
  • static methods that do some fancy work e.g database access
  • deep inheritance tree
  • work in constructor e.g. control statements
  • classes that violate the Single Responsibility Principle

Are there more warning signs?

+5  A: 

Hard-coded dependencies.

orip
that's not so much untestable as a failure of TDD development in general
annakata
Well, TDD is a design (and development) approach, but you can test code without using, or even liking, TDD :)
orip
+4  A: 
  • Not programming to interfaces
  • Newing up objects intead of using factories/IOC
barneytron
Not programming to Interface is the big one in my opinion. By putting dependencies behind a internal interface you mock external dependencies in your testing.
RS Conley
+10  A: 

None of those things make code untestable. They may make it harder to find edge case bugs but, provided you have fully specified the success criteria for testing (and test-driven development eases this), all you have to do is pass the criteria.

TDD can apply to the behaviour of specific parts as well as the project as a whole, so you can easily test very small components. But, it's meant to test the results, not the means by which those results were obtained.

Provided the tests are passed, you have met the requirements. If there are bugs following that, this is an issue with the tests, not the code being tested (in which case the tests should be modified to catch the previously unforeseen problem).

You should not care (in terms of delivery of functionality) whether there's a while statement in one of your constructors. You should ask yourself what business requirement mandates that? I strongly doubt your client will deliver a list of requirements including "inheritance limited to 4 levels". They may well list "bug-free" as a requirement but you'll have to negotiate them down on that one :-).

paxdiablo
While they may not make it untestable, things like fancy static methods, make it almost impossible to mock out external resources, which again may cause running unit tests to take a lot longer time to run. In TDD, unit tests should not touch external resources.
Erlend
+1, but for an interesting view of why it's bad to do "real work" in a constructor see a good post by Miško Hevery: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
orip
Maybe not "untestable" but certainly "hard to test" which in turn delays delivery.
Craig P. Motlin
+7  A: 

See the following blog post by Miško Hevery: How to Write 3v1L, Untestable Code.

kshahar
+1, Miško Hevery rules
orip
thanks, very interessting blog
Ludwig Wensauer
+1  A: 

Work done in GUI classes that has nothing to do with presentation. GUI should be completely decoupled from the underlying model.

Jesse Pepper
+1  A: 

Code is untestable as long as you can not modify it. If you have the possibility to refactor the project, no code is untestable. Usually, only very small modifications are needed to make testing easier. And they can be justified because they increase the quality of the code.

Even in the cases you describe, the code is not necessarily untestable. It's just more difficult to test. For example, it's easier to test code if you can isolate the database access and avoid them during your unit tests. But if you have to, you can put up a database dedicated to running your tests.

Bluebird75
+1  A: 

I would say that none of those things makes code untestable. They do make unit testing difficult, because each of those increases coupling in your implementation.

Among other annoyances that make unit testing difficult:

  • graphical user interface code mingled with business logic code
  • all anti-patterns, but God object in particular (http://en.wikipedia.org/wiki/God_object)
  • along the same lines, a huge function is also very annoying

In general, any recommendation you might hear about creating better code is also a recommendation for easier to unit test code.

A: 

lack of layering, excess of coupling... ie class Y has been written to know about X, but it shouldn't, X is reusable. There is a strong relationship between testability and reusability.

Jesse Pepper
+1  A: 

Databases! Particularly those with triggers!

I know that you can mock the database, but I've always found that most of the bugs in my code (mostly CRUD apps) are data/mapping issues and if you mock the database you don't find that kind of bug.

Martin Brown
+1  A: 

Miško Hevery's Guide on Writing Testable Code details the flaws that make code hard to test. His list overlaps with yours somewhat but goes into incredible detail.

  • Constructor does Real Work
  • Digging into Collaborators
  • Brittle Global State & Singletons
  • Class Does Too Much
Craig P. Motlin