Post Self
Post Self

Reputation: 1554

How to test function with many initial checks

Let's say we have function foo. foo checks, before its actual code runs, a couple of initial conditions, which are laid out as consecutive if-conditions. If an error occurs, the user is informed with an alert/toast. The ifs are laid out in the following manner:

function foo() {
  if (!condition_one) {
    alert(text_one);
    return;
  }

  if (!condition_two) {
    alert(text_two);
    return;
  }

  if (!condition_three) {
    alert(text_three);
    return;
  }

  if (!condition_four) {
    alert(text_four);
    return;
  }

  // ...
}

Before writing this function, we write our first unit test, in accordance with TDD principles. This first test checks the case that condition_one fails. The second test checks that condition_one succeeds, i.e., text_one is not alerted.

We now copy the first test case and transform it, such that it become the test that checking that condition_two fails. We have to extend the first test case for this, since we need to have the first condition succeed to even get to the second condition. We now write the succeeding test for condition_two and repeat the process until we have tested all initial conditions.

The issue is, that each time we go to the next condition, all setup code of all previous conditions is accumulated and the actual setup code for this condition gets lost in the clutter and it is hard to know what we are even testing.

What are possible solutions to this problem? I understand that this is exactly what Aspect-Oriented-Programming is trying to remedy but that is not an option for me.

PS: this issue also arises in other large if-else structures and is thus more widely applicable than this specific scenario.

Upvotes: 0

Views: 82

Answers (2)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57204

Design is what we do to get more of what we want than we would get by just doing it.

The answer is that we need to think about the "what we want" for our tests, and then make that happen.

In this case, one of the "what you wants" is the ability to distinguish the important detail of the test from the background noise. That's usually achieved by moving the background noise out of the body of the test.

In the abstract, your third test has roughly this shape

assume condition_one
assume not condition_two
foo()
assert alert(text_two)

Implicit in this description is the fact that we don't care about anything other than condition_one and condition_two.

So in code, that could look like

InitialConditions.any()
InitialConditions.condition_one(true)
InitialConditions.condition_two(false)

foo()

assert alert(text_two)

As you got along, Given (whatever that is) is getting more complicated to allow you to express more precisely the assumptions of each test - but the prose that is each test is still basically linear with the complexity of the details, not the complexity of the whole.

A good introductory read would be Nat Pryce on Test Data Builders (2007).

Upvotes: 1

Ali Soltani
Ali Soltani

Reputation: 9927

As far as testing is concerned, every functions couldn't properly be tested if they don't follow Single Responsibility Principle (SRP). As can be seen in foo function, there are too many conditions and as a result of it, there are some reasons to change this method. In this sort of methods, writing test is too hard and sometime it would be impossible.

I urge that refactoring this method for sticking to SRP, then writing test would be a piece of cake.

Upvotes: 0

Related Questions