1

I have a function that needs to do something like "try to move to node X in a directed graph", and my current implementation uses an indefinite loop like this

while(true) {    
    
    trySomething();    

    if (checkAreWeThereYet()) {
        break;
    }    

    if (inferInfiniteLoopIsHappening()) {
        throw;
    }
}

But with the current implementation I haven't found a way (yet) to actually trigger an infinite loop and test that last part (which I added in precisely because there was a bug in a previous iteration of the code that led to an infinite loop) to make sure it's working as intended.

I've been thinking of mocking some of the implementation details to fake "making a mistake" that would then lead to an infinite loop, but that strikes me as maybe not a best practice (?)

The only online resources I've been able to find so far around this are for the "simpler" but analogous "switch statement's default case is currently unreachable, but might be hit if a bug is introduced in upstream code" scenario, but I'm wondering if I've just been googling the wrong terms (?)

1 Answer 1

1

It is a classic case for TDD.

I would recommend deleting the current design and drive a new design with automated checks, rather than trying to write tests to adhere to your design.

For more details on TDD, I would recommend this presentation on TDD by Bob Martin.

I've been thinking of mocking some of the implementation details to fake "making a mistake" that would then lead to an infinite loop, but that strikes me as maybe not a best practice

Your new design (after some refactoring) probably will be different from your current implementation. E.g., you have three responsibilities in this function: The looping, the trySomething, and the dealing with the new state in the graph. These probably will end up in different objects, which then you can inject as you wish on your main object.

while(!analyzer.areWeDone()) {    
    actor.trySomething();    
}

As demonstrated in the presentation, the final design will be created from the simplest cases (where analyzer.areWeDone is true) to the most completed ones, where all branches are covered.

5
  • I think I follow, but in my case if I did TDD exactly I would never be able to detect infinite loop bugs coming from future changes to the code for cases not explicitly captured by current tests (if I'm understanding correctly)? I can't think of a test I could write where the expected behavior of "Avoids getting caught potentially using infinite resources" wouldn't already be fulfilled by the existing code and so not need any additional detection code
    – grunet
    Commented Apr 24, 2021 at 19:30
  • Also for a test with an expected behavior of "Lets consumers know when potentially using infinite resources" I can't think of how to implement that without knowingly creating the option to hit an infinite loop (which seems counterproductive)
    – grunet
    Commented Apr 24, 2021 at 19:39
  • A test is used to allow refactoring a program, not to detect problems in mutations of this program. If changes in behavor will happen, they need to be covered by new tests as well. Commented Apr 24, 2021 at 20:43
  • Additionally, a general check for infinite loops is not possible (aka the halting problem). If in your particular context such algorithm can be written, it can be exercised by automated checks (after all it is supposed to be exercised in runtime) Commented Apr 24, 2021 at 20:45
  • I think maybe "detecting problems in mutations of the program" is more along the lines of what I'm looking for, not so much tests in the traditional (TDD) sense. I'll try to do some more digging into those keywords. Thanks for clarifying the situation!
    – grunet
    Commented Apr 24, 2021 at 21:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.