skip to Main Content

I’m trying to use the mtest package (https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo/integration/mtest) to perform some testing with mock results on my MongoDB calls, but I can’t seem to figure out how to properly mock the *mongo.UpdateResult value that gets returned when you make an UpdateOne(...) call on a collection.

Here is a snippet demonstrating the problem:

package test

import (
    "context"
    "errors"
    "testing"

    "github.com/stretchr/testify/assert"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/integration/mtest"
)

func UpdateOneCall(mongoClient *mongo.Client) error {
    filter := bson.D{{Key: "SomeIDField", Value: "SomeID"}}
    update := bson.D{{Key: "$set", Value: bson.D{{Key: "ANewField", Value: true}}}}
    collection := mongoClient.Database("SomeDatabase").Collection("SomeCollection")
    updateResult, err := collection.UpdateOne(context.Background(), filter, update)
    if err != nil {
        return err
    }
    if updateResult.ModifiedCount != 1 {
        return errors.New("no field was updated")
    }
    return nil
}

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

    mt.Run("Successful Update", func(mt *mtest.T) {

        mt.AddMockResponses(mtest.CreateSuccessResponse(
            bson.E{Key: "NModified", Value: 1},
            bson.E{Key: "N", Value: 1},
        ))

        err := UpdateOneCall(mt.Client)

        assert.Nil(t, err, "Should have successfully triggered update")
    })
}

The collection.UpdateOne(context.Background(), filter, update) call works perfectly fine. There are no errors returned. Unfortunately, the updateResult.ModifiedCount value is always 0.

I’ve tried multiple combinations of mtest.CreateSuccessResponse(...) and bson.D, utilizing names such as NModified and N (as can be seen in the snippet), as well as ModifiedCount and MatchedCount. Nothing seems to do the trick.

Is there anyway to mock this call such that it actually returns a value for the ModifiedCount?

2

Answers


  1. Chosen as BEST ANSWER

    @Vishwas Mallikarjuna got the right answer, so I'm leaving their post as the accepted one because they absolutely deserve that. However, given their findings, I just wanted to expand a little bit.

    The issue came down to case-sensitivity. Now that I know that, I was able to mock the MatchedCount, ModifiedCount, UpsertedCount, and the UpsertedID.

    This chunk of code shows how you would go about influencing all of these values:

    package test
    
    import (
        "context"
        "testing"
    
        "github.com/stretchr/testify/assert"
        "go.mongodb.org/mongo-driver/bson"
        "go.mongodb.org/mongo-driver/mongo"
        "go.mongodb.org/mongo-driver/mongo/integration/mtest"
        "go.mongodb.org/mongo-driver/x/mongo/driver/operation"
    )
    
    const (
        mockMatchedCount            int64  = 5
        oneLessThanMockedMatchCount int64  = 4
        mockModifiedCount           int64  = 22
        mockUpsertedCount           int64  = 13
        mockUpsertedID              string = "CouldBeAnythingIThink"
    )
    
    func UpdateOneCall(mongoClient *mongo.Client) (*mongo.UpdateResult, error) {
        filter := bson.D{{Key: "SomeIDField", Value: "SomeID"}}
        update := bson.D{{Key: "$set", Value: bson.D{{Key: "ANewField", Value: true}}}}
        collection := mongoClient.Database("SomeDatabase").Collection("SomeCollection")
        return collection.UpdateOne(context.Background(), filter, update)
    }
    
    func TestUpdateOneCall(t *testing.T) {
        mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
        defer mt.Close()
    
        mt.Run("Successful Update", func(mt *mtest.T) {
    
            upsertedVals := make([]operation.Upsert, mockUpsertedCount)
            upsertedVals[0] = operation.Upsert{ID: mockUpsertedID}
            mt.AddMockResponses(mtest.CreateSuccessResponse(
                bson.E{Key: "n", Value: mockMatchedCount},
                bson.E{Key: "nModified", Value: mockModifiedCount},
                bson.E{Key: "upserted", Value: upsertedVals},
            ))
    
            result, err := UpdateOneCall(mt.Client)
    
            assert.Nil(t, err, "Should have successfully triggered update")
            assert.Equal(t, result.MatchedCount, oneLessThanMockedMatchCount)
            assert.Equal(t, result.ModifiedCount, mockModifiedCount)
            assert.Equal(t, result.UpsertedCount, mockUpsertedCount)
            assert.Equal(t, result.UpsertedID, mockUpsertedID)
        })
    }
    

    Also, if you're wondering why the actual result.MatchedCount is oneLessThanMockedMatchCount, it comes down to how having an Upserted value works. If you go through the logic, in the file go.mongodb.org/[email protected]/mongo/collection.go you'll find this chunk of code that explains it:

    opRes := op.Result()
    res := &UpdateResult{
        MatchedCount:  opRes.N,
        ModifiedCount: opRes.NModified,
        UpsertedCount: int64(len(opRes.Upserted)),
    }
    if len(opRes.Upserted) > 0 {
        res.UpsertedID = opRes.Upserted[0].ID
        res.MatchedCount--
    }
    
    return res, err
    

  2. mt.AddMockResponses(bson.D{
                {"ok", 1},
                {"nModified", 1},
            })
    

    This worked for me to get ModifiedCount : 1

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search