通过使用 testify/mock 模拟存储库来测试服务失败

问题描述 投票:0回答:1

我是 Golang 新手。我正在尝试为服务文件编写单元测试。我正在使用 testify,它的包 testify/mock 和 testify/assert。看了不同的文章发现下面写的

MockCommentRepository
是正确的。但是,我的一个测试失败并显示以下消息

--- FAIL: TestCommentServiceCreateForCreated (0.00s)
panic: 

mock: Unexpected Method Call
-----------------------------

Create(*domains.Comment)
                0: &domains.Comment{Model:gorm.Model{ID:0x0, CreatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, ID:0x7b, Title:"Test Comment", Content:"Test Content", PostID:0x7b, Post:domains.Post{Model:gorm.Model{ID:0x0, CreatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, ID:0x0, Title:"", Content:"", Published:false}}

The closest call I have is: 

Create(*domains.Comment)
                0: &domains.Comment{Model:gorm.Model{ID:0x0, CreatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, ID:0x7b, Title:"Test Comment", Content:"Test Content", PostID:0x7b, Post:domains.Post{Model:gorm.Model{ID:0x0, CreatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, ID:0x7b, Title:"Test Post", Content:"Test Content", Published:true}}

Difference found in argument 0:

--- Expected
+++ Actual
@@ -24,6 +24,6 @@
   },
-  ID: (uint) 123,
-  Title: (string) (len=9) "Test Post",
-  Content: (string) (len=12) "Test Content",
-  Published: (bool) true
+  ID: (uint) 0,
+  Title: (string) "",
+  Content: (string) "",
+  Published: (bool) false
  }

Diff: 0: FAIL:  (*domains.Comment=&{{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} 123 Test Comment Test Content 123 {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} 0   false}}) != (*domains.Comment=&{{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} 123 Test Comment Test Content 123 {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} 123 Test Post Test Content true}}) [recovered]

这是我要测试的服务文件。

type CommentService struct {
    commentRepo domains.CommentRepositoryInterface
}

func NewCommentService(commentRepo domains.CommentRepositoryInterface) *CommentService {
    return &CommentService{
        commentRepo: commentRepo,
    }
}

func (s *CommentService) Create(request *dtos.CreateCommentRequest) (*dtos.CreateCommentResponse, error) {
    post, err := s.commentRepo.FindPostByID(request.PostID)
    if err != nil {
        return nil, fmt.Errorf("failed to find post: %w", err)
    }

    if !post.Published {
        return nil, fmt.Errorf("post is not published")
    }

    comment := &domains.Comment{
        ID:      123,
        Title:   request.Title,
        Content: request.Content,
        PostID:  request.PostID,
    }

    if err := s.commentRepo.Create(comment); err != nil {
        return nil, fmt.Errorf("failed to create comment: %w", err)
    }

    response := &dtos.CreateCommentResponse{
        ID:      comment.ID,
        Message: "Comment created successfully",
    }

    return response, nil
}

这是 CommentRepository 的模拟

package mocks

import (
    "post_comment/domains"

    "github.com/stretchr/testify/mock"
)

type MockCommentRepository struct {
    mock.Mock
}

func (m *MockCommentRepository) Create(comment *domains.Comment) error {
    args := m.Called(comment)

    if args.Get(0) == nil {
        return nil
    }

    return args.Error(0)
}

func (m *MockCommentRepository) FindPostByID(postID uint) (*domains.Post, error) {
    args := m.Called(postID)

    if args.Get(0) == nil {
        return nil, args.Error(1)
    }

    return args.Get(0).(*domains.Post), args.Error(1)
}

这些是 CommentService 的测试。

// comment_service_test.go
func (s *CommentService) Create(request *dtos.Create) (*dtos.CreateCommentResponse, error) {
    post, err := s.commentRepo.FindPostByID(request.PostID)
    if err != nil {
        return nil, fmt.Errorf("failed to find post: %w", err)
    }

    if !post.Published {
        return nil, fmt.Errorf("post is not published")
    }

    comment := &domains.Comment{
        ID:      123,
        Title:   request.Title,
        Content: request.Content,
        PostID:  request.PostID,
    }

    if err := s.commentRepo.Create(comment); err != nil {
        return nil, fmt.Errorf("failed to update comment: %w", err)
    }

    response := &dtos.CreateCommentResponse{
        ID:      comment.ID,
        Message: "Comment updated successfully",
    }

    return response, nil
}

// No problem here
func TestCommentCreateFailsWhenPostIsNotPublished(t *testing.T) {
    // create a mock of CommentRepository
    mockRepo := new(mocks.MockCommentRepository)

    // Create a mock post
    mockPost := &domains.Post{
        Title:     "Test Post",
        Content:   "Test Content",
        Published: false,
    }

    var postId uint
    postId = 123

    // mockRepo.On("FindPostByID", postId).Return(mockPost, nil)
    mockRepo.On("FindPostByID", postId).Return(mockPost, nil)

    // create new comment service
    service := services.NewCommentService(mockRepo)

    // Create a valid request
    request := &dtos.CreateCommentRequest{
        Title:   "Test Comment",
        Content: "Test Content",
        PostID:  postId,
    }

    comment, err := service.Create(request)

    // Assert that no error occurred
    assert.Error(t, err)
    assert.EqualError(t, err, "post is not published")
    assert.Nil(t, comment)
}

// This tests is failing
func TestCreatesCommentSuccessfully(t *testing.T) {
    // create a mock of CommentRepository
    mockRepo := new(mocks.MockCommentRepository)

    var postId uint
    postId = 123

    // Create a mock post
    mockPost := &domains.Post{
        ID:        postId,
        Title:     "Test Post",
        Content:   "Test Content",
        Published: true,
    }
    // mockRepo.On("FindPostByID", postId).Return(mockPost, nil)
    mockRepo.On("FindPostByID", postId).Return(mockPost, nil).Once()

    // create new comment service
    service := services.NewCommentService(mockRepo)

    // Create a valid request
    request := &dtos.CreateCommentRequest{
        Title:   "Test Comment",
        Content: "Test Content",
        PostID:  postId,
    }

    mockComment := &domains.Comment{
        ID:      123,
        Title:   request.Title,
        Content: request.Content,
        PostID:  postId,
        Post:    *mockPost,
    }

    mockRepo.On("Create", mockComment).Return(nil).Once()

    response, err := service.Create(request)

    // Assert that no error occurred
    assert.NoError(t, err)

    // // Assert the response values
    assert.NotNil(t, response)
    assert.NotNil(t, response.ID)
    assert.Equal(t, "Comment created successfully", response.Message)

    mockRepo.AssertExpectations(t)
}

我不确定代码中出了什么问题。有人可以指导我解决这个问题吗?先谢谢你了

unit-testing go mocking testify
1个回答
0
投票

根据您提供的示例,当前的单元测试期望

Post
对象具有以下值:

Post {
    ID:        123
    Title:     "Test Comment"
    Content:   "Test Comment"
    Published: false
}

但是,

CommentService
Create
方法未正确初始化
post
对象。

因此,当调用

service.Create(request)
时,创建的注释将始终有一个未初始化的
post
字段(假设操作成功)。

这意味着

Post
对象的所有字段都将默认为其各自类型的默认值:

  • ""
    对于
    string
  • 0
    对于
    int
  • false
    bool

要解决此问题,您需要将

post
变量分配给
Post
结构体的
Comment
字段,如下所示:

post, err := s.commentRepo.FindPostByID(request.PostID)

// error handling

comment := &Comment{
    ID:      123,
    Title:   request.Title,
    Content: request.Content,
    PostID:  request.PostID,
    Post: *post, // <--- add this
}
© www.soinside.com 2019 - 2024. All rights reserved.