6

I have next struct.

package logger

import "fmt"

type IPrinter interface {
    Print(value string)
}

type ConsolePrinter struct{}

func (cp *ConsolePrinter) Print(value string) {
    fmt.Printf("this is value: %s", value)
}

Test coverage says I need to test that ConsolePrinter Print method.

How can I cover this method?

Thanks.

EnterSB
  • 984
  • 2
  • 10
  • 27
  • 3
    Use the same principle as in this answer: [Fill os.Stdin for function that reads from it](https://stackoverflow.com/questions/46365221/fill-os-stdin-for-function-that-reads-from-it/46365584#46365584). – icza Nov 14 '17 at 08:42
  • @icza Thanks, I've followed your instructions, I am adding answer below. – EnterSB Nov 14 '17 at 09:08

3 Answers3

21

Following comment that @icza wrote, I've written the test below.

func TestPrint(t *testing.T) {
    rescueStdout := os.Stdout
    r, w, _ := os.Pipe()
    os.Stdout = w

    cp := &ConsolePrinter{}
    cp.Print("test")

    w.Close()
    out, _ := io.ReadAll(r)
    os.Stdout = rescueStdout

    if string(out) != "this is value: test" {
        t.Errorf("Expected %s, got %s", "this is value: test", out)
    }
}

I've found example in question What is the best way to convert byte array to string?.

Ivan Ivković
  • 417
  • 3
  • 14
EnterSB
  • 984
  • 2
  • 10
  • 27
12

Use Examples to convey the usage of a function.

Don't fret over 100% test coverage, especially for simple straightforward functions.

func ExampleHello() {
    fmt.Println("hello")
    // Output: hello
}

The additional benefit is that examples are outputted in a generated doc with go doc tool.

Piko Monde
  • 396
  • 1
  • 4
  • 18
John Smith
  • 1,091
  • 9
  • 17
  • If I understood well, I would have to call ConsolePrinter Print method in example, right? – EnterSB Nov 14 '17 at 08:56
  • 1
    Just try don't delibarate :) – John Smith Nov 14 '17 at 09:12
  • 1
    So now you know, but you've got a documented and tested piece of code. Remember, that a cover is only a software metric. There is no need to duplicate tests to get a cover higher. – John Smith Nov 14 '17 at 09:16
  • I understand what you say. Still, I wanted to see if there was way for that part of code to be covered with tests. – EnterSB Nov 14 '17 at 09:22
  • by your opinion, which is better, example as you wrote or test? – EnterSB Nov 14 '17 at 09:28
  • 1
    For example - "when you want to document a function to the user, use Examples. Otherwise use tests - often you need to use data provider to test the function. It's all relative. Make your choice. – John Smith Nov 14 '17 at 09:33
2

I would recommend to create new instance of the logger, which would behave exactly as fmt methods for printing data to console. Also, you can configure it with additional features like showing the filename, date and etc. Such custom logger can be passed as a parameter to your service/instance factory method. That would make it really easy to mock and test.

Your code

type Logs interface {
    Println(v ...interface{})
}

type InstanceToTest struct {
    log Logs
}

func InstanceToTestFactory(logger Logs) *InstanceToTest {
    return &InstanceToTest{logger}
}

func (i *InstanceToTest) SomeMethod(a string) {
    i.log.Println(a)
}

Create a mock for logger

type LoggerMock struct {
    CalledPrintln []interface{}    
}

func (l *LoggerMock) Println(v ...interface{}) {
    l.CalledPrintln = append(CalledPrintln, v)
}

And in your test

func TestInstanceToTestSomeMethod(t *testing.T) {
    l := &LoggerMock{}
    i := InstanceToTestFactory(l)
    a := "Test"

    i.SomeMethod(a)

    if len(l.CalledPrintln) == 0 || l.CalledPrintln[0] != a {
        t.Error("Not called")
    }
}
Bohdan Kostko
  • 373
  • 2
  • 10