diff --git a/examples/example_service/example_service_test.go b/examples/example_service/example_test_using_gomock_test.go similarity index 90% rename from examples/example_service/example_service_test.go rename to examples/example_service/example_test_using_gomock_test.go index 1f690c1..6638d28 100644 --- a/examples/example_service/example_service_test.go +++ b/examples/example_service/example_test_using_gomock_test.go @@ -11,7 +11,7 @@ import ( "github.com/vingarcia/ksql/nullable" ) -func TestCreateUser(t *testing.T) { +func TestCreateUserWithGoMock(t *testing.T) { t.Run("should call ksql.Insert correctly", func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() @@ -25,8 +25,8 @@ func TestCreateUser(t *testing.T) { var users []interface{} mockDB.EXPECT().Insert(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, table ksql.Table, records ...interface{}) error { - users = append(users, records...) + DoAndReturn(func(ctx context.Context, table ksql.Table, record interface{}) error { + users = append(users, record) return nil }) @@ -51,19 +51,18 @@ func TestCreateUser(t *testing.T) { var users []map[string]interface{} mockDB.EXPECT().Insert(gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, table ksql.Table, records ...interface{}) error { - for _, record := range records { - // The StructToMap function will convert a struct with `ksql` tags - // into a map using the ksql attr names as keys. - // - // If you are inserting an anonymous struct (not usual) this function - // can make your tests shorter: - uMap, err := ksqltest.StructToMap(record) - if err != nil { - return err - } - users = append(users, uMap) + DoAndReturn(func(ctx context.Context, table ksql.Table, record interface{}) error { + // The StructToMap function will convert a struct with `ksql` tags + // into a map using the ksql attr names as keys. + // + // If you are inserting an anonymous struct (not usual) this function + // can make your tests shorter: + uMap, err := ksqltest.StructToMap(record) + if err != nil { + return err } + users = append(users, uMap) + return nil }) @@ -77,7 +76,7 @@ func TestCreateUser(t *testing.T) { }) } -func TestUpdateUserScore(t *testing.T) { +func TestUpdateUserScoreWithGoMock(t *testing.T) { t.Run("should call ksql.QueryOne() & Patch() correctly", func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() @@ -121,7 +120,7 @@ func TestUpdateUserScore(t *testing.T) { }) } -func TestListUsers(t *testing.T) { +func TestListUsersWithGoMock(t *testing.T) { t.Run("should call ksql.QueryOne() & Query() correctly", func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() @@ -183,7 +182,7 @@ func TestListUsers(t *testing.T) { }) } -func TestStreamAllUsers(t *testing.T) { +func TestStreamAllUsersWithGoMock(t *testing.T) { t.Run("should call ksql.QueryChunks correctly", func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() @@ -255,7 +254,7 @@ func TestStreamAllUsers(t *testing.T) { }) } -func TestDeleteUser(t *testing.T) { +func TestDeleteUserWithGoMock(t *testing.T) { t.Run("should call ksql.Delete correctly", func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() diff --git a/examples/example_service/example_test_using_ksql_Mock_test.go b/examples/example_service/example_test_using_ksql_Mock_test.go new file mode 100644 index 0000000..f8bddce --- /dev/null +++ b/examples/example_service/example_test_using_ksql_Mock_test.go @@ -0,0 +1,253 @@ +package exampleservice + +import ( + "context" + "testing" + + "github.com/ditointernet/go-assert" + "github.com/vingarcia/ksql" + "github.com/vingarcia/ksql/ksqltest" + "github.com/vingarcia/ksql/nullable" +) + +func TestCreateUserWithKsqlMock(t *testing.T) { + t.Run("should call ksql.Insert correctly", func(t *testing.T) { + var users []interface{} + mockDB := ksql.Mock{ + InsertFn: func(ctx context.Context, table ksql.Table, record interface{}) error { + users = append(users, record) + return nil + }, + } + + s := Service{ + db: mockDB, + streamChunkSize: 100, + } + + user := UserEntity{Name: nullable.String("TestUser")} + + err := s.CreateUser(context.TODO(), user) + assert.Equal(t, nil, err) + assert.Equal(t, 1, len(users)) + assert.Equal(t, &user, users[0]) + }) + + t.Run("another way of testing input structs", func(t *testing.T) { + var users []map[string]interface{} + mockDB := ksql.Mock{ + InsertFn: func(ctx context.Context, table ksql.Table, record interface{}) error { + // The StructToMap function will convert a struct with `ksql` tags + // into a map using the ksql attr names as keys. + // + // If you are inserting an anonymous struct (not usual) this function + // can make your tests shorter: + uMap, err := ksqltest.StructToMap(record) + if err != nil { + return err + } + users = append(users, uMap) + + return nil + }, + } + + s := Service{ + db: mockDB, + streamChunkSize: 100, + } + + user := UserEntity{Name: nullable.String("TestUser")} + + err := s.CreateUser(context.TODO(), user) + assert.Equal(t, nil, err) + assert.Equal(t, 1, len(users)) + + assert.Equal(t, "TestUser", users[0]["name"]) + }) +} + +func TestUpdateUserScoreWithKsqlMock(t *testing.T) { + t.Run("should call ksql.QueryOne() & Patch() correctly", func(t *testing.T) { + + var users []interface{} + mockDB := ksql.Mock{ + QueryOneFn: func(ctx context.Context, result interface{}, query string, params ...interface{}) error { + // This function will use reflection to fill the + // struct fields with the values from the map + return ksqltest.FillStructWith(result, map[string]interface{}{ + // Use int this map the keys you set on the ksql tags, e.g. `ksql:"score"` + // Each of these fields represent the database rows returned + // by the query. + "score": 42, + }) + }, + + PatchFn: func(ctx context.Context, table ksql.Table, record interface{}) error { + users = append(users, record) + return nil + }, + } + + s := Service{ + db: mockDB, + streamChunkSize: 100, + } + + err := s.UpdateUserScore(context.TODO(), 1, -2) + assert.Equal(t, nil, err) + assert.Equal(t, 1, len(users)) + + resultUser := UserEntity{ + ID: 1, + Score: nullable.Int(40), + } + assert.Equal(t, &resultUser, users[0]) + }) +} + +func TestListUsersWithKsqlMock(t *testing.T) { + t.Run("should call ksql.QueryOne() & Query() correctly", func(t *testing.T) { + mockDB := ksql.Mock{ + QueryOneFn: func(ctx context.Context, result interface{}, query string, params ...interface{}) error { + // This function will use reflection to fill the + // struct fields with the values from the map + return ksqltest.FillStructWith(result, map[string]interface{}{ + // Use int this map the keys you set on the ksql tags, e.g. `ksql:"score"` + // Each of these fields represent the database rows returned + // by the query. + "count": 420, + }) + }, + QueryFn: func(ctx context.Context, results interface{}, query string, params ...interface{}) error { + return ksqltest.FillSliceWith(results, []map[string]interface{}{ + { + "id": 1, + "name": "fake name", + "age": 42, + }, + { + "id": 2, + "name": "another fake name", + "age": 43, + }, + }) + }, + } + + s := Service{ + db: mockDB, + streamChunkSize: 100, + } + + total, users, err := s.ListUsers(context.TODO(), 40, 2) + assert.Equal(t, nil, err) + assert.Equal(t, 420, total) + assert.Equal(t, 2, len(users)) + + expectedUsers := []UserEntity{ + { + ID: 1, + Name: nullable.String("fake name"), + Age: nullable.Int(42), + }, + { + ID: 2, + Name: nullable.String("another fake name"), + Age: nullable.Int(43), + }, + } + assert.Equal(t, expectedUsers, users) + }) +} + +func TestStreamAllUsersWithKsqlMock(t *testing.T) { + t.Run("should call ksql.QueryChunks correctly", func(t *testing.T) { + mockDB := ksql.Mock{ + QueryChunksFn: func(ctx context.Context, parser ksql.ChunkParser) error { + // Chunk 1: + err := ksqltest.CallFunctionWithRows(parser.ForEachChunk, []map[string]interface{}{ + { + "id": 1, + "name": "fake name", + "age": 42, + }, + { + "id": 2, + "name": "another fake name", + "age": 43, + }, + }) + if err != nil { + return err + } + + // Chunk 2: + err = ksqltest.CallFunctionWithRows(parser.ForEachChunk, []map[string]interface{}{ + { + "id": 3, + "name": "yet another fake name", + "age": 44, + }, + }) + return err + }, + } + + s := Service{ + db: mockDB, + streamChunkSize: 2, + } + + var users []UserEntity + err := s.StreamAllUsers(context.TODO(), func(u UserEntity) error { + users = append(users, u) + return nil + }) + + assert.Equal(t, nil, err) + assert.Equal(t, 3, len(users)) + + expectedUsers := []UserEntity{ + { + ID: 1, + Name: nullable.String("fake name"), + Age: nullable.Int(42), + }, + { + ID: 2, + Name: nullable.String("another fake name"), + Age: nullable.Int(43), + }, + { + ID: 3, + Name: nullable.String("yet another fake name"), + Age: nullable.Int(44), + }, + } + assert.Equal(t, expectedUsers, users) + }) +} + +func TestDeleteUserWithKsqlMock(t *testing.T) { + t.Run("should call ksql.Delete correctly", func(t *testing.T) { + + var ids []interface{} + mockDB := ksql.Mock{ + DeleteFn: func(ctx context.Context, table ksql.Table, idOrObj interface{}) error { + ids = append(ids, idOrObj) + return nil + }, + } + + s := Service{ + db: mockDB, + streamChunkSize: 100, + } + + err := s.DeleteUser(context.TODO(), 42) + assert.Equal(t, nil, err) + assert.Equal(t, 1, len(ids)) + assert.Equal(t, 42, ids[0]) + }) +}