如何使用Golang中的mtest模拟mongo.ErrNoDocuments错误。

huangapple go评论104阅读模式
英文:

How mock mongo.ErrNoDocuments with mtest in Golang

问题

我有这个函数:

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 { // 这里有未覆盖的测试
        return models.ErrAccountNotFound // 这里有未覆盖的测试
    }

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

我想测试这一行 if err == mongo.ErrNoDocuments。我创建了一个模拟通用错误和成功的测试。

这是我的通用错误测试:

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: "mongo中的通用错误",
        }
        mt.AddMockResponses(mtest.CreateWriteErrorsResponse(fakeError))

        documentNumber := "12345678900"

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

这个测试非常顺利。但只适用于以下代码:

if err != nil {
    return err
}

我无法使函数 FindOne 返回 mongo.ErrNoDocuments 实例。我该如何测试这一行:

if err == mongo.ErrNoDocuments {
英文:

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

得分: 2

要模拟Collection.FindOne()调用的结果,您需要创建和使用一个游标响应,其中包含一个批次(结果文档的列表),并且该批次应该是空的。(FindOne()操作也是一个带有隐式limit=1Find()操作。)

以下是使用mtest.CreateCursorResponse()的方法:

mt.AddMockResponses(mtest.CreateCursorResponse(
	0,
	fmt.Sprintf("%s.%s", "dbName", "collName"),
	mtest.FirstBatch,
	// 这里故意没有列出文档
))

如果您希望游标包含/返回文档,则可以将它们列为最后一个可变参数。在这里,我故意将其留空,以便游标不返回任何元素。

这将导致FindOne()返回mongo.ErrNoDocuments

以下是一个完整的测试示例:

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,
			// 这里故意没有列出文档
		))

		documentNumber := "12345678900"

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

这里有一个包含更多使用mtest的示例的页面:https://medium.com/@victor.neuret/mocking-the-official-mongo-golang-driver-5aad5b226a78

英文:

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

huangapple
  • 本文由 发表于 2023年4月17日 22:53:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76036461.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定