How to define that mock method gets invoked zero times

1.9k views Asked by At

I'm trying to test the following method:

//AuthenticationMiddleware Middleware which handles all of the authentication.
func AuthenticationMiddleware(context context.ContextIntf, w web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) {
    //Check if url is one that doesn't need authorization. If not than send them to the login page.
    for _, url := range AuthMWInstance.GetInfo().nonAuthURLs {
        if url.Method == r.Method && strings.Contains(r.URL.Path, url.DomainName) {
            next(w, r)
            return
        }
    }

    if errSt := CheckForAuthorization(context, r, w); errSt != nil {
        responses.Write(w, responses.Unauthorized(*errSt))
        return
    }
    defer context.GetInfo().Session.SessionRelease(w)
    next(w, r)
}

In this case, there's a SessionRelease that gets invoked iff r contains a URL that requires authorization, and that authorization was successful.

It may be important to know that :

type MiddlewareSt struct {
    //NonAuthUrls URLs that can be accessed without a token.
    nonAuthURLs []url.URLSt
}

type MiddlewareIntf interface {
    GetInfo() *MiddlewareSt
    CheckTokenAndSetSession(context context.ContextIntf, r *web.Request, w web.ResponseWriter,
        token string, scope string, remoteAddr string) *errors.ErrorSt
}

var AuthMWInstance MiddlewareIntf

and that CheckForAuthorization's return value ultimately relies upon AuthMWInstance

My Test Strategy

  • Create a stub middleware instance to initialize AuthMWInstance to, that simply returns nil for CheckTokenAndSetSession (with the session setting abstracted out, of course, to the creation of the stub object itself, which has the Session), and a MiddlewareSt full of fake nonAuthURLs for GetInfo()
  • Create a mock session.Store that, for all tests except the Sanity Test, expects zero calls to SessionRelease.

It's probably worth noting (but assumed) that I'm using testify,mockery libraries for the mocking and the assertion stuff.

The test

Is implemented thus:

func TestAuthenticationMiddleware(t *testing.T) {
    // bring in the errors
    sdkTesting.InitErrors()
    // create/set up the test doubles
    // mock session
    sessionMock := new(testing_mock.MockStore)
    // temporarily set AuthMWInstance to a stub
    instance := AuthMWInstance
    AuthMWInstance = &StubMiddlewareInstance{
        Session: sessionMock,
    }
    // AuthMWInstance.Session
    defer func() { AuthMWInstance = instance }()
    // fake ResponseWriter
    w := new(StubResponseWriter)
    // fake web requests
    requestWithoutAuth := new(web.Request)
    requestWithoutAuth.Request = httptest.NewRequest("GET",
        "http://example.com/logout",
        nil,
    )

    // do tests here
    t.Run("AuthorizationNotRequired", func(t *testing.T) {
        // place expectations on sessionMock, namely that it does
        //  **not** invoke `SessionRelease`
        sessionMock.On("SessionRelease", w).
            Times(0)

        AuthenticationMiddleware(new(context.Context),
            w,
            requestWithoutAuth,
            web.NextMiddlewareFunc(func(web.ResponseWriter, *web.Request) {}))

        sessionMock.AssertExpectations(t)
    })

}

Runtime behavior

The following false fail happens: enter image description here. It's literally as if, instead of doing:

sessionMock.On("SessionRelease", w).
            Times(0)

, I was like:

sessionMock.On("SessionRelease", w).
            Once()

NOTE session.Store.SessionRelease does not return anything, hence why I didn't even bother using Return().

Am I asserting that it is to be called exactly zero times right?

1

There are 1 answers

1
Mike Warren On BEST ANSWER

I feel a bit silly for this.

The problem was that I was bothering with

sessionMock.AssertExpectations(t)

when I could have simply said

sessionMock.AssertNotCalled(t, "SessionRelease", w)

(Documentation on that method here)

Doing the latter resolved the issue, and did exactly what I was trying to accomplish.