1

Here's a little golang program that connects to localhost via ssh and does some sftp-like operations. It works fine but I would like to make the doTheWork() function more testable.

I've read up on using gomock to create mocks for interfaces. So I've already created two interfaces, Sftper and Walker, and I'm using those instead of calling sftp's methods directly.

What I want to do is mock out all the stuff from stfp inside doTheWork(). Close() is easy because it only returns an error (or nil). But Create() returns *sftp.File and that is a struct, not an interface. Same with Walk() which returns another struct. I would have to create one of these structs in my test code, and that's not really possible without actually calling the real sftp library and making a remote connection and creating a remote file, and this is exactly what I am trying to mock out.

What am I missing?

package main

import (
    "log"
    "os"

    "github.com/kr/fs"
    "github.com/pkg/sftp"
    "golang.org/x/crypto/ssh"
)

// Sftper helps make things testable
type Sftper interface {
    Close() error
    Create(path string) (*sftp.File, error)
    Lstat(p string) (os.FileInfo, error)
    Walk(root string) *fs.Walker
}

// Walker helps make things testable
type Walker interface {
    Step() bool
    Err() error
    Path() string
}

var sftpclient Sftper
var w Walker

func doTheWork(sftpclient Sftper) {
    defer sftpclient.Close()

    // walk a directory
    w = sftpclient.Walk("/tmp/")
    for w.Step() {
        if w.Err() != nil {
            continue
        }
        log.Println(w.Path())
    }

    // leave your mark
    f, err := sftpclient.Create("hello.txt")
    if err != nil {
        log.Fatal(err)
    }
    if _, err1 := f.Write([]byte("Hello world!")); err1 != nil {
        log.Fatal(err1)
    }

    // check it's there
    fi, err := sftpclient.Lstat("hello.txt")
    if err != nil {
        log.Fatal(err)
    }
    log.Println(fi)

}

func main() {
    config := &ssh.ClientConfig{
        User: "myusername",
        Auth: []ssh.AuthMethod{
            ssh.Password("mysupersecretpassword"),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }
    conn, err0 := ssh.Dial("tcp", "localhost:22", config)
    if err0 != nil {
        log.Fatal("Failed to dial: ", err0)
    }

    // open an SFTP session over an existing ssh connection.
    var err error
    sftpclient, err = sftp.NewClient(conn)
    if err != nil {
        log.Fatal(err)
    }
    doTheWork(sftpclient)
}
mkopriva
  • 35,176
  • 4
  • 57
  • 71
Dan Tenenbaum
  • 1,809
  • 3
  • 23
  • 35

0 Answers0