T
T
thewizardplusplus2015-06-01 16:24:22
Unit testing
thewizardplusplus, 2015-06-01 16:24:22

How to test complex functions with side effects?

Suppose, in accordance with the principle of separation of concerns, I have identified many small functions that solve their tasks, and covered them with tests. But after all there is also a certain general function which causes these small.
How to test this general function? After all, it covers all possible options for executing code in these small functions.
Do not test it at all, since all the functionality is already tested in small functions? This is not possible - what if I wrote this large function incorrectly (I called the small ones in the wrong order or forgot something). Tests are needed, the only question is which ones.
Also interested in how to test functions with side effects. Yes, I know about dependency injection, but it turns out that it must be used always, always, so that testing is possible? Will this complicate the code, make it more confusing and unreadable?
* * *
Well, to make it clearer, I will give a specific example. Suppose we need to process command line arguments passed to a program. Namely:

  • -vprint version if given ;
  • validate the values ​​of the remaining arguments (say that they fall within certain limits or are included in the specified list).

We wrote a function for this (some kind of abstract pseudo-language):
function ProcessArgs(args) {
    let parsed_args = ParseArgs(args)
    ShowVersionIfNeed(parsed_args)
    ValidateValues(parsed_args)

    return parsed_args
}

Moreover, the functions ParseArgs(), ShowVersionIfNeed()and ValidateValues()are also implemented and, what is important, covered with tests.
Question - how to test the function ProcessArgs()? Iterate over all possible options for all internal functions? That is, in fact, to sort through all the tests written for them in all possible combinations? Or how?
And the second question. Here is the function ShowVersionIfNeed():
function ShowVersionIfNeed(args) {
    if args.contains('-v') {
        sys.Exit(sys.ExitSuccess)
    }
}

As you can see, it has a program interrupt. How to test this? Running it will break the work of all other tests.
Add dependency injection?
function ProcessArgs(args, exiter) {
    let parsed_args = ParseArgs(args)
    ShowVersionIfNeed(parsed_args, exiter)
    ValidateValues(parsed_args)

    return parsed_args
}

function ShowVersionIfNeed(args, exiter) {
    if args.contains('-v') {
        exiter.Exit(sys.ExitSuccess)
    }
}

But it also complicates and confuses the code. Or not, and so do it normally and correctly?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
K
Konstantin Kitmanov, 2015-06-01
@thewizardplusplus

How to test this general function? After all, it covers all possible options for executing code in these small functions.

It seems to me that we need to look here from the point of view of BDD, that is, check not some synthetic tests, but check that the program behaves correctly in certain situations.
Lock sys.Exit if the language allows, or use DI, yes. DI is needed not only for tests, but also to reduce cohesion.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question