Unit Testing, Clean code and more …(Part 1)

Shilpa Goley
5 min readAug 4, 2020

Are you wondering if there’s a link between unit testing and clean code? Aren’t these two independent practices? I am going to try and establish an important link between the two which would explain their importance in software development within any organisation. Let’s start with another question: What is clean code?

What is clean code?

Clean code does not have a fixed definition. Its description can be quite subjective to each developer and organisation. In short:

Clean code is code that is easy to read, understand, maintain and extend.

Although we do not have a specific definition for clean code, based on experience and knowledge of the best practices followed across industry and dev community, the following points can be considered as guidelines.

  1. Self explanatory: code should express intent clearly and the reader should understand the use and need of every variable, method and expression.
  2. Extensible: adding new features and enhancements should be easy and code should adapt to evolving design.
  3. Domain Driven: code should be domain driven and should use ubiquitous language, which means using common terms that business and developers understand.
  4. Developer experience: for the code written for APIs, libraries etc special care should be taken to ensure ease of use. The code should be well abstracted to expose only necessary details to the users.
  5. Applies to all code: clean code guidelines apply to all code. Be it production code, test code or infrastructure code!

What has tests got to do with clean code?

It is clear from the above explanation that clean code is the final goal we need to achieve to ensure a reliable and maintainable application. But it is not an easy journey to get there. This is where tests come in handy !

Tests will constantly tell you where you are going wrong. If you look at your tests close enough they will not only ensure that the functionality is as needed but will also indicate how complex and coupled the code is. Whether the tests are written before (by following TDD) or after the code, they will act as navigators and constantly guide you if you take the wrong turn.

Now, there are different types of tests that exists in the system. Let us take a quick look at them.

Test Pyramid

Image credit: https://martinfowler.com/bliki/TestPyramid.html

Most of us are familiar with the test pyramid. The test pyramid shows us the best approach to test a system and move towards a robust and reliable system. It tells us that a minimal set of UI or end-to-end test cases are needed to test out entire flows (10%). A set of integration tests ensure that groups of related components work well together (20%). The maximum number of tests in the system need to be unit tests (70%) which would cover most of the functionality in the software.

Unit Tests

That brings us to ‘Unit Tests’. What is a Unit Test? A unit test gives us fast feedback earlier in the development cycle around whether the intended functionality has been achieved, it performs well and it is secure at the most granular level. But what is considered a unit.

Based on context, a unit could be a class, a method/function, a module or even a group of classes. So, if a unit does not have a fixed definition, how do I decide what the test should do? What are the characteristics of a good unit test?

Characteristics of a good Unit test

  1. Significantly faster than other tests like UI and integration tests.
  2. Runs in isolation and does not depend on any other test.
  3. Tests one and only one thing. Split the test in two or more if it seems to be testing too many things
  4. Written and maintained by developers as part of feature development.
  5. Runs in all environments without changes, including non-production and production like test, stage, pre-prod etc.
  6. Covers all logical flows of the unit.

Ways of Testing the unit

Image credit: https://martinfowler.com/bliki/UnitTest.html

An example of sociable tests is as follows:

public class User {
private final String name;
private final Wallet wallet;
public User(String name, Wallet wallet){
this.name = name;
this.wallet = wallet;
}
public boolean hasMoney() {
return ! wallet.isEmpty();
}
}
//Testclass UserTest {@Test
void shouldNotHaveAnyMoneyWhenCreatedWithEmptyWallet(){
User joe = new User("Joe", Wallet.withNoMoney());
assertFalse(joe.hasMoney());
}
@Test
void shouldHaveMoneyWhenCreatedWithNonEmptyWallet(){
Wallet walletWithMoney = WalletBuilder.withValue(20.0).build();
User joe = new User("Joe", walletWithMoney);
assertTrue(joe.hasMoney());
}
}

And an example of a solitary test is this:

public class Wallet {
private static final String DEFAULT_CURRENCY = “INR”;
private Money money;

static Wallet withNoMoney() {
return new Wallet();
}
private Wallet(){
this(new Money(DEFAULT_CURRENCY, 0.0))
}
public Wallet(Money money) {
this.money = money;
}
boolean isEmpty() {
return money == null || money.getValue == 0;
}
}
//Testclass WalletTest { @Test
void shouldTestThatWalletIsEmpty() {
Wallet wallet = new Wallet();
assertTrue(wallet.isEmpty());
}
}

Solitary Tests

In case of solitary tests, the attempt is to isolate the unit under test so that the test does not get affected by the changed behaviour of any other components it depends on. But having said that if the unit does have these ‘collaborators’ it depends on, how to we represent them. There are different types of ‘test doubles’ which can be used to mimic the collaborators’ behaviour, each one can be used as seen appropriate for the given situation:

  1. Dummy — Is just a placeholder, does nothing else
  2. Fake — Has an implementation but is not fit for production. E.g in-memory database
  3. Stubs — Provides canned results to calls made
  4. Spies — Stubs that record information
  5. Mocks — tests against expectations of set calls and throws exceptions if calls are not made or unexpected calls made

Look out for “Unit Testing, Clean code and more…(Part 2)” where we will talk more in detail about solitary unit testing, esp. mocking and TDD.

Additional Credits: Subhrajit Roy and I had co-hosted this session at an external event.

References: https://martinfowler.com/bliki/UnitTest.html

--

--