go-ldap / ldap

Basic LDAP v3 functionality for the GO programming language.
Other
2.22k stars 353 forks source link

Mocking LDAP for Unit Testing #146

Closed DarthHater closed 6 years ago

DarthHater commented 6 years ago

Hello there!

I've loved using this lib but I've been struggling a bit with how to mock it for unit tests.

I have tried using mockgen and that is a no go since Conn is not an interface (Conn is I think the biggest thing I want to mock).

I was wondering if others have had success with other tools to mock this out, or if there were plans to add Interfaces to some of the stuff to make this easier when using tools like mockgen etc...

Thanks again for the great work on this, btw.

liggitt commented 6 years ago

I'd recommend writing your code to the Client interface and mocking that

https://github.com/go-ldap/ldap/blob/master/client.go

DarthHater commented 6 years ago

Well, that seems reasonable! Thanks for the quick reply @liggitt lemme see how it goes.

DarthHater commented 6 years ago

Ok at first glance, I'm not sure how to totally approach this. Could be that I'm a dummy (total possibility). There are a few methods on Conn that Client doesn't have, such as Dial and DialTLS. This means I can mock out the stuff that's doing a search etc... (which is great), just can't really mock the connect parts (probably not the biggest deal). That sound accurate?

liggitt commented 6 years ago

There are a few methods on Conn that Client doesn't have, such as Dial and DialTLS.

Dial and DialTLS aren't on Conn, they return a Conn. Conn has Start and StartTLS, which the interface does have.

This means I can mock out the stuff that's doing a search etc... (which is great), just can't really mock the connect parts (probably not the biggest deal). That sound accurate?

Yeah, you need a different entrypoint for setup anyway, since otherwise you'd be using a completely real connection. Make the part you want to consume take a Client interface (or a function that produces a Client interface, and inject something that calls Dial and returns the result when running for real, and something that returns your mock when running unit tests)

DarthHater commented 6 years ago

Right-o, that's what I was kinda figuring. Thanks for the feedback!

DarthHater commented 6 years ago

Got some tests working, thanks again. I was coming at this backwards, appreciate the help.

rodrigc commented 3 years ago

@DarthHater do you have some sample code for where you successfully mocked out LDAP? I need to do something similar.

atselvan commented 1 year ago

@DarthHater Can you give some examples of how you tested?

7wesley commented 1 year ago

You could do something like this:

type Connector interface {
    Dial(network string, addr string) (LDAPConn, error)
}

// implements connector interface
type LDAP struct {
    test bool
}

// choose between mock and real
func (l LDAP) Dial(network string, addr string) (LDAPConn, error) {
    if l.test {
        return LDAPConnMock{}, nil
    }
    return ldap.Dial(network, addr)
}

type LDAPConn interface {
    Bind(username string, password string) error
    Close()
}

// implements LDAPConn interface
type LDAPConnMock struct{}

func (c LDAPConnMock) Bind(username string, password string) error {
    return nil
}

func (c LDAPConnMock) Close() {}

func connectLDAP(ldap LDAP) {
    l, err := ldap.Dial("tcp", "host")
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()

    err = l.Bind("test", "test")
    if err != nil {
        log.Fatal(err)
    }

}

func main() {
    connectLDAP(LDAP{test: true})
}

Create an interface Connector with your connection method (let's say Dial) and another interface LDAPConn for methods you need from the ldap.Conn type. Implement Dial to return your LDAPConn interface, returning either ldap.Dial or a mock.