How mock mongo.ErrNoDocuments with mtest in Golang

896 Views Asked by At

I have this function:

func CheckExistUser(documentNumber string, mongoClient *mongo.Database) error {
    ctx := context.Background()
    colletionUsersName := "my-collection"

    colletionUsers := mongoClient.Collection(colletionUsersName
    filter := bson.D{{Key: "cpf", Value: documentNumber}}

    user := &entity.UserEntity{}
    err := colletionUsers.FindOne(ctx, filter).Decode(user)

    if err == mongo.ErrNoDocuments { // uncovered test here
        return models.ErrAccountNotFound // uncovered test here
    }

    if err != nil {
        return err
    }
    return nil
}

I want to test the line if err == mongo.ErrNoDocument. I made a test mocking generic error and success.

This is my generic error test

func TestFindOneWhenMongoReturnsError(t *testing.T) {
    mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
    defer mt.Close()

    mt.Run("success", func(mt *mtest.T) {
        fakeError := mtest.WriteError{
            Index:   1,
            Code:    200,
            Message: "generic error in mongo",
         }
        mt.AddMockResponses(mtest.CreateWriteErrorsResponse(fakeError))

        documentNumber := "12345678900"

        err := repository.CheckExistUser(documentNumber, mt.Client.Database("mongodb://mock"))
        assert.ErrorContains(t, err, fakeError.Message)
    })
}

This works like a charm. But work only for these lines

    if err != nil {
        return err
    }

I cannot make the function FindOne return the instance mongo.ErrNoDocuments. How can I do to test this line:

if err == mongo.ErrNoDocuments {
1

There are 1 best solutions below

1
icza On

To mock the result of a Collection.FindOne() call, you (also) have to create and use a cursor response, composed of a single batch of course, and the batch (list of result documents) should be empty. (A FindOne() operation is also a Find() operation with an implicit limit=1.)

Here's how you can do it using mtest.CreateCursorResponse():

mt.AddMockResponses(mtest.CreateCursorResponse(
    0,
    fmt.Sprintf("%s.%s", "dbName", "collName"),
    mtest.FirstBatch,
    // no documents listed here on purpose
))

If you would want the cursor to contain / return documents those could be listed as the final variadic argument, here I intentionally left that empty, so the cursor will return no elements.

This will result in FindOne() returning mongo.ErrNoDocuments.

Here's a complete test:

func TestFindOneWhenUserDoesntExist(t *testing.T) {
    mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
    defer mt.Close()

    mt.Run("ErrAccountNotFound", func(mt *mtest.T) {
        mt.AddMockResponses(mtest.CreateCursorResponse(
            0,
            fmt.Sprintf("%s.%s", "dbName", "collName"),
            mtest.FirstBatch,
            // no documents listed here on purpose
        ))

        documentNumber := "12345678900"

        err := CheckExistUser(documentNumber, mt.Client.Database("mongodb://mock"))
        assert.ErrorIs(t, err, models.ErrAccountNotFound)
    })
}

Here's a page with many more examples of using mtest: https://medium.com/@victor.neuret/mocking-the-official-mongo-golang-driver-5aad5b226a78