DEV Community

Preston Vasquez
Preston Vasquez

Posted on

Mocking MongoDB Go Driver V2: How to Do It (and Why You Probably Shouldn't)

MongoDB Go Driver V1 included the mtest package allowing developers to mock driver responses in memory. A few popular blog posts since emerged detailing how to do this, including "How I'm testing MongoDB calls in Go (for now)" by Liza Shulyayeva. In V1, GODRIVER-1767 marked mtest unstable so that the driver team could alter the API to accommodate new testing requirements. In V2, GODRIVER-2724 internalized mtest making the V1 solution for mocking using mtest unavailable.

Mocking can be overly complex and is brittle (AKA “Mock Hell”). Tests should be focused on your code, not the internals of the Go Driver. Since mocks are not identical to your live environment, problems can stay hidden longer. By the time an issue shows up in production, fixing it can be far more costly than if you’d caught it earlier in a more realistic test. Instead, it's better to use a container-based integration testing framework like testcontainers:

func TestMyRepositoryFunction(t *testing.T) {
    ctx := context.Background()

    //  Start up a MongoDB container
    req := testcontainers.ContainerRequest{
        Image:        "mongo:latest",
        ExposedPorts: []string{"27017/tcp"},
        WaitingFor:   wait.ForLog("Waiting for connections").WithStartupTimeout(30 * time.Second),
    }
    mongoC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
    })
    require.NoError(t, err)
    defer mongoC.Terminate(ctx)

    // Get mapped host & port
    host, err := mongoC.Host(ctx)
    require.NoError(t, err)
    port, err := mongoC.MappedPort(ctx, "27017")
    require.NoError(t, err)

    // Connect with the Go driver
    uri := fmt.Sprintf("mongodb://%s:%s", host, port.Port())
    client, err := mongo.Connect(options.Client().ApplyURI(uri))
    require.NoError(t, err)
    defer client.Disconnect(ctx)

    err := MyRepositoryFunction(context.TODO(), client.Database("mydb").Collection("mycoll"))
    assert.NoError(t, err, "Should have successfully run")
}
Enter fullscreen mode Exit fullscreen mode

For necessary edge cases V2 provides a MockDeployment option in the experimental API which may be modified or removed without notice. Here is a re-worked example from this StackOverflow answer for V2:

func TestMyRepositoryFunction(t *testing.T) {
    mockDep := drivertest.NewMockDeployment()

    mockDep.AddResponses(
        bson.D{{Key: "ok", Value: "1"}},
        bson.D{
            {Key: "ok", Value: "1"},
            {Key: "value", Value: bson.M{"_id": "custom123", "key": 24}},
        })

    opts := options.Client()
    opts.Deployment = mockDep

    client, err := mongo.Connect(opts)
    require.NoError(t, err)
    defer client.Disconnect(ctx)

    err := MyRepositoryFunction(context.TODO(), client.Database("mydb").Collection("mycoll"))

    assert.NoError(t, err, "Should have successfully run")
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.