Friday, April 14, 2017

On Unit Testing, Java TDD for developers to write

It has been a while, let's kick off 2017 with a blog entry.

I have read and I am reading about four or five posts a day about unit testing. It really has been a long time obsession for me. I have moved past the technical and practical considerations on unit testing frameworks and done with the debates with "should you use Junit or Mockito or Karma?" I am more interested in the psychology of unit testing, who does it, likes it, hates it? It really is one of those easy to learn, hard to master concepts. For example, many many may play chess when they are young and can end up being horribly chess players most of their life, I am part of that majority. Unfortunately, I have never played chess and sat down for hours and tried to master it. I never see the common patterns or have a developed end game. I mostly just play with a knowledge of the basic rules. Following good unit testing practices within your software development shop is a lot like playing chess. It is easy to learn and difficult master. Actually, there are a lot of big differences, chess is a game, chess is not coding, and people take their software development very seriously. So if you don't master unit testing, but are able to complete your job tasks, some might argue that is an acceptable risk in the world of software development. And why master chess or master unit testing? If developers are fine without unit testing, then why even suggest it. Some developers just don't want to invest the energy to master the practice. And in some development shops, there is no hard requirement to do so.

I am not going to convince you to write unit tests with this one post, I will leave that up to software guru Martin Fowler and the people at ThoughtWorks who have written large tomes on the subject. But I will present my thoughts on why some developers won't write unit tests but why they should. Those developers and architects that do advocate unit testing generally fall into that category where they have written just enough unit tests to find it useful and they generally love the practice, they also encourage others to follow along. I am sort of in that camp, I have almost become religious about it. I can't imagine my real code without unit tests and I just feel guilty by only testing through manual functional testing.

Jeff Atwood of Coding Horror wrote a short blog post on the topic, "I Pity the Fool Who Doesn't Write Unit Tests". Here is the one blurb that stuck out for me, "Even if you only agree with a quarter of the items on that list-- and I'd say at least half of them are true in my experience-- that is a huge step forward for software developers". And this one, "It's more fun to code with them than without". That is the essence of this unit testing religion, we can't force it on developers and we can't force developers to write unit tests only a certain way. I and many others don't believe in the practice of 100% coverage. You will rarely get there anyway, depending on the project or company. Some will argue that you shouldn't break the rule on non-determinism and this is a big one. Basically, the unit test should return the same output every time you run the test. You should avoid breaking this rule for unit tests but you can still write and add automated integration tests to your suite and not waste time, combine a collection of unit tests and integration tests. A simple integration test might test connecting to your REST microservice and validating the HTTP status code. At that point, your test moves into the integration testing category. If you connect to the database, run a particular SQL statement and validate data model returned from the SQL invocation, then your test is basically integration. Both scenarios are not units are non-deterministic but I would still consider them to be useful. Also, as a start for new developers getting familiar with unit testing, writing integration tests may be more familiar to them than decomposing or refactoring their code for a real unit test. There is a benefit in database or HTTP integration tests, you can add them to a test suite and run them in a automated form after a code change and after a build. Even bad tests can be useful.

Misko Hevery is creator of one of the most popular JavaScript frameworks to emerge in the last couple of years. It is a Google project that he started working as an Agile Coach. As he puts it, he wants to maintain the high level of automated testing culture at Google. Most of his published articles are not about AngularJs but on the benefits of automated testing. I can only imagine that he developed the MVC JavaScript framework because the old crop of frameworks were a pain to work with for developers. They were not testable.
I have given my advocacy speech on unit testing, but how do I use it, what practices do I follow?
  • For every piece of new code, I formulate a unit test case. New code could include my model structure or interface into my Java services. This is critical, unit testing encourages you to write testable early code. Meaning, I try to use interfaces and abstract classes which allow me to inject mock objects early in the development process.
  • For local development, I can build, write code, write and update my unit tests and then run the automated suite of tests. The key part is re-running the test suite. Normally I want my unit tests to pass, if they don't pass then I can look at my code and refactor. Also, the code I write today, I can run a year from now, I should expect the same result.
  • As you are writing your unit tests. Have fun, this is not production code, the unit tests don't run in production, you can test input as little or as much as possible.
  • I try to avoid unit tests around code that doesn't do anything. Write unit tests around your modules that have some kind of behavior. We shouldn't write model POJO code with setters and getters, but there is no reason to test a setter method. It is more fun to code around the real functionality.
  • Writing unit tests also encourages the developer to write testable code
  • Write Java code that doesn't use static methods or variables. Imagine that, try writing code that doesn't make use of the static keyword. Why would you do this? Static, class level routines are procedural and inherently hard to test. You can override their functionality, they are completely class level.
  • Writing unit tests encourages refactoring. Some refactoring may include the use of OOP techniques. Use interfaces and abstract classes.
  • Use a DI/Dependency Injection framework like AngularJS (yea I called AngularJS DI), Spring or Guice. DI frameworks encourages the container to create new objects for you. Managing objects on your own and using the 'new' operator encourages untestable code.
In Summary, see what Jeff Atwood, Martin Fowler and Misko Hevery have said about Unit Testing. And we pity the fool that don't do it.