我是 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)
}
我不确定代码中出了什么问题。有人可以指导我解决这个问题吗?先谢谢你了
根据您提供的示例,当前的单元测试期望
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
}