如何正确地对MongoDB的CRUD结果进行单元测试

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

How to properly unit test mongoDB CRUD result

问题

我的应用程序主要使用mongo-go-drive包对MongoDB进行CRUD操作。这个函数是gRPC服务器服务之一,它只是调用数据库方法action.GetProducts(ctx)并返回*mongo.cursor。然后对结果进行解码。对于每个文档,我将文档内容放入一个单独的产品结构体中,然后将其附加到产品切片中(使用gRPC proto的repeated GetProductResponse类型创建了GetProductsResponse结构体)。在将所有产品附加到GetProductsResponse之后,将响应返回给gRPC客户端。

我对测试也不太熟悉,应该如何拆分函数并进行模拟(如何模拟游标)进行单元测试?首先,是否有必要对该函数进行单元测试,即使它只是将结果附加到切片中,或者是否应该直接进行集成测试并跳过单元测试,因为它涉及数据库I/O?

func (s *Server) GetProducts(ctx context.Context, in *pb.EmptyRequest) (*pb.GetProductsResponse, error) {
	cursor, err := action.GetProducts(ctx)
	if err != nil {
		return nil, err
	}

	products := pb.GetProductsResponse{}

	res := model.Product{}

	for cursor.Next(ctx) {
		// Convert document to above struct
		err := cursor.Decode(&res)
		if err != nil {
			return nil, fmt.Errorf("failed to decode document: %v", err)
		}

		product := &pb.GetProductResponse{ProductId: res.Product_id.Hex(), Name: res.Name, Price: res.Price, Qty: int32(res.Qty)}

		products.Products = append(products.Products, product)
	}

	return &products, nil
}
英文:

My application mostly consists of CRUDs to/from MongoDB using mongo-go-drive package. This function is one of gRPC server services and all it does is calling database method action.GetProducts(ctx) and it returns *mongo.cursor. Then the result is decoded. For each document, I put the document content into a singular product struct, then append it to products slices (the GetProductsResponse struct is made using gRPC proto repeated GetProductResponse type). After appending all product into GetProductsResponse, I return the response to gRPC client.

I am also new to testing in general, how should I break down the function and do the mocking (how to mock the cursor?) for unit testing? Is it even necessary in the first place to do unit test on the function even though all it does is appending the result, or should I just go straight for the integration test and skip the unit test since it involves database I/O?

func (s *Server) GetProducts(ctx context.Context, in *pb.EmptyRequest) (*pb.GetProductsResponse, error) {
	cursor, err := action.GetProducts(ctx)
	if err != nil {
		return nil, err
	}

	products := pb.GetProductsResponse{}

	res := model.Product{}

	for cursor.Next(ctx) {
		// Convert document to above struct
		err := cursor.Decode(&res)
		if err != nil {
			return nil, fmt.Errorf("failed to decode document: %v", err)
		}

		product := &pb.GetProductResponse{ProductId: res.Product_id.Hex(), Name: res.Name, Price: res.Price, Qty: int32(res.Qty)}

		products.Products = append(products.Products, product)
	}

	return &products, nil
}

答案1

得分: 1

如果您与数据库进行交互,那就不再是单元测试了,因为您正在与另一个外部系统进行集成。

无论如何,我通常会这样定义我的“repository”层函数:

package repo

var FetchUserById = func(id string) (*model.User, error){
    // 这里是真正的逻辑
    return user, err
}

然后,当我需要测试我的“service”层逻辑时,我会这样模拟整个“repository”层:

repo.FetchUserById = func(id string) (*model.User, err) {
   return myMockedUser, nil
}
英文:

If you interact with the DB is not unit testing anymore, because you're integrating with another external system.

Anyway, I use to define my "repository" layer function this way:

package repo

var FetchUserById = func(id string) (*model.User, error){
    // here the real logic
    return user, err
}

and then, when I have to test my "service" layer logic, I would mock the entire "repository" layer this way:

repo.FetchUserById = func(id string) (*model.User, err) {
   return myMockedUser, nil
}

huangapple
  • 本文由 发表于 2022年1月3日 00:20:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/70557637.html
匿名

发表评论

匿名网友

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

确定