Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test code that loops forever

I have an app (epazote) that once starts runs forever but I want to test some values before it blocks/waits until ctrl+c is pressed or is killed.

Here is an small example: http://play.golang.org/p/t0spQRJB36

package main

import (
    "fmt"
    "os"
    "os/signal"
)

type IAddString interface {
    AddString(string)
}

type addString struct{}

func (self *addString) AddString(s string) {
    fmt.Println(s)
}

func block(a IAddString, s string) {

    // test this
    a.AddString(s)


    // ignore this while testing
    block := make(chan os.Signal)
    signal.Notify(block, os.Interrupt, os.Kill)

    for {
        signalType := <-block
        switch signalType {

        default:
            signal.Stop(block)
            fmt.Printf("%q signal received.", signalType)
            os.Exit(0)
        }
    }
}

func main() {
    a := &addString{}
    block(a, "foo")
}

I would like to know if is posible to ignore some parts of the code while testing, or how to test this cases, I have implemented an interface, in this case for testing AddString that helped me to test some parts but have no idea of how to avoid the "block" and test.

Any ideas?

Update: Putting the code inside the loop Addstring in another function works but only for testing that function, but If I want to do a full code coverage, I still need to check/test the blocking part, for example how to test that is properly behaving when receiving ctrl+c or a kill -HUP, I was thinking on maybe creating a fake signal.Notify but don't know how to overwrite imported packages in case that could work.

like image 369
nbari Avatar asked Jan 25 '16 23:01

nbari


1 Answers

Introduce test delegates into your code.

Extract your loop into a function that takes 2 functions as arguments: onBeginEvent and onEndEvent. The functions signatures shall take:

  • state that you want to inspect inside the test case
  • optional: counter of loop number (so you can identify each loop). It is optional because actual delegate implementation can count number of times it was invoked by itself.

In the beginning of your loop you call OnBegingEvent(counter, currentState); than your code does its normal work and in the end you call OnEndEvent(counter, currentState); Presumably your code has changed currentState.

In production you could use an empty implementation of the function delegates or implement nil check in your loop.

You can use this model to put as many checks of your processing algorithms as you want. Let's say you have 5 checks. Now you look back at it and realize that's becoming too hard. You create an Interface that defines your callback functions. These callback functions are a powerful method of changing your service behavior. You step back one more time and realize that interface is actually your "service's policy" ;)

Once you take that route you will want to stop your infinite loop somehow. If you want a tight control within a test case you could take a 3rd function delegate that returns true if it is time to quit from the loop. Shared variable is an option to control quit condition.

This is certainly a higher level of testing than unit testing and it is necessary in complex services.

like image 109
Sergei G Avatar answered Oct 23 '22 09:10

Sergei G