How to test an application built on non-testable code?

XSolve
XSolve Blog

--

If you’ve been into technology for some time, you certainly know that it’s possible to write code which is not testable. Most of our work is done with already existing code. Legacy code often does not use IoC and is not based on interfaces, and sometimes it has nothing to do with OOP. The question is: is it possible to automatically test code like this (assuming that rewriting the application is not an option)?

Below you can see a sample class which does not use Dependency Injection:

Hmmm…

What’s wrong with the code above? Firstly, it’s strictly related to the Dao class, secondly, it uses the static method of CurrentDate.getDay(). Changes in the Dao class or in the getDay() method of the CurrentDate class directly affect the test result. Unit tests, as the very name suggests, should test a unit (a class) and only the condition of this unit should influence the tests‘ results. If the implementation of the CurrentDate.getDay() method changes, our test will stop working.

Let’s consider another example:

The whole POJO class would be written well if not for one detail — the class is about implementation and not interface. This way, POJO directly depends on the LastUpdatedDateTime class — any modification to this class can affect the POJO class.

What to do in such a situation? Give up automatization and do only manual tests? There is another solution!

ATDD in action

If we are not able to write unit tests for methods of a given class, we can move the problem to a higher level (a common engineering practice — solving a problem by creating several others) and instead of a unit, we are going to test ready-made functionalities. To do that, we’re going to use acceptance tests.

Let’s now describe what’s happened above.

  1. Standard JUnit library annotation, marking the method as a test.
  2. Statistical method opening the browser (Mozilla Firefox by default) and going to the given URL (String). The method is waiting for the page to load completely.
  3. We can refer to every element in DOM through a selector.

What next?

Acceptance testing brings value to the project in many ways.

First of all, by using libraries such as JBehave or Cucumber (at best — Spock) we can modify them to a form which is understandable for business clients, and thus, make them familiar with the testing process (or even involve them in the process, e.g. by writing features for Cucumber).

Apart from that, new developers will find it easier to get into the project — all the functionalities will be described in detail and available in the repository.

What’s most important, testing functionalities, we move away from the code, which enables testing an app build of non-testable classes. We cannot solve the problem of a badly written application this way, but we get around it to ensure quality in our project at least in a part.

What about the test pyramid?

You might be wondering what to do with the principle saying that application quality should be based more on unit tests than functionality tests. This rule is right — a lot of things may go wrong while conducting other than unit tests: losing the connection to the database or dependence on external services. Unit tests are much quicker, too, which allows getting more immediate feedback. The problem is that bad code can be written regardless of anything (even in Java, which is quite idiot-proof); bad code meaning code which can’t be tested in an optimum manner. I think that it’s better to have any (well written!) automatic tests than have none at all. Besides, sometimes you have to convince the client to write unit tests (and, which usually follows, to refactoring), and that — let’s be honest — is rather difficult.

Summary

If you’re working with spaghetti code and you’re facing the problem of quality assurance, it’s better to use acceptance tests, which check the functionality of the tested system from the end user’s perspective. With an adequate number of well-written tests, you will avoid regression and you will automatize a grand part of your team’s work. Remember also that constant dropping wears away a stone: after some time, your client may see the value of such tests and may become convinced to invest time and money in unit tests, which are the basis of every well-written application.

Here are the libraries and frameworks that we use at XSolve:

Originally published at xsolve.software on July 28, 2016.

--

--

XSolve
XSolve Blog

Agile Software House focused on PHP/Symfony, Java, JavaScript and Mobile.