mann
mann

Reputation: 27

How to generate code coverage for REST service

I want to get test coverage of a REST service written in Go. I am spawning REST service through a goroutine, then making HTTP requests using rest client, and reviewing the HTTP responses. Tests are passing successfully but go test -cover returns 0% test coverage. Is there a way to get the actual test coverage of all the packages used inside the go lang REST service.

my test file: main_test.go

import (
    "testing"
)

// Test started when the test binary is started. Only calls main.
func TestSystem(t *testing.T) {
    go main()    // Spinning up the go lang REST server in a separate go routine.
    http.Post("https://localhost/do_something")
}

my output:

go test -cover  main_test.go
ok      command-line-arguments  0.668s  coverage: 0.0% of statements 

Upvotes: 0

Views: 915

Answers (3)

mann
mann

Reputation: 27

Thanks all for your reply. I am able to get the coverage by -coverpkg. Not sure what exactly solved the issue below are couple of changes which worked for me as suggested by @Flimzy 1. Moved REST server Initialization code from main to a function. and then in test case starting the REST server within go routine and gracefully terminating it by sending a message to channel

    channel := make(chan error) // create a channel will listen for error
    go func(channel chan error) {
        inithandler.InitRestServer(channel, )
        _ = <-channel // terminate the channel
    }(channel)
http.Post("https://localhost/do_something") // call the endpoint and resp verification
.
.
channel <- errors.New("stop")

trigger test with -coverpkg

go test main_test.go -cover -coverpkg ./...

Upvotes: 1

Alex Yu
Alex Yu

Reputation: 3547

Preface

This could not be a complete answer covering all cases.

It's an attempt to explain how it could be possible to get zero coverage from what OP explained.

Test itself

Tests are passing successfully but go test -cover returns 0% test coverage.

  1. Start of service with go main() inside Test-function
  2. Attempt of http.Post to service without checks for errors and that response is not nil
  3. No panics during test so test is passing

Problems in test

  1. No guarantee that service is actually started before attempt of http.Post
  2. Results of http.Post are not checked

For first problem:

  • bad/dirty solution: add time.Sleep after go main()
  • good/clean solution: instrument service with some kind of signalling/monitoring that serve loop is started

For second problem:

Take a look at such Test-function:

func TestPost(t *testing.T) {
    var b bytes.Buffer
    http.Post("http:/inexistent.com/inexistent-url", "", &b)
    t.Log("No problems!")
}

This test will always pass. Because it tests non-existense of panics only.

To make this test more correct:

func TestPost(t *testing.T) {
    var b bytes.Buffer
    if _, err:= http.Post("http:/inexistent.com/inexistent-url", "", &b); err!=nil{
    t.Errorf("http.Post : %v", err)
}

Proposals

  1. Check http.Post results
  2. Check that tested service is actually started

Upvotes: 1

VolatileCoder
VolatileCoder

Reputation: 274

Regardless of the root cause, I think there’s an issue with your general approach here.

I would suggest that some of what you’re testing here isn’t your code and you shouldn’t bother. You don’t need to actually test the http protocol and http package, right?

So the way I’ve handled this in the past is to break apart my the meaningful code that I want to test out of the http handler functions. The http handlers should only be responsible for validating input, calling the actual business logic, and then formatting output.

Once you’ve broken apart your code this way, you can directly call your meaningful functions from your tests. Your http handler functions should be so simple that there is no place for bugs to lurk.

Don’t seek 100% test coverage. Test what matters.

Upvotes: -1

Related Questions