Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract set up of test environment & common assertions #7

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 67 additions & 57 deletions core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,47 @@ func TestMain(m *testing.M) {
os.Exit(code)
}

// TestCreateItem Given a description and the storage accessor returns an id, when CreateItem is called, then the item is created and returned with the id set.
func TestCreateItem(t *testing.T) {
// arrange: mock storage accessor
// testEnv is a test environment that contains common test test resources and implements common test functions.
type testEnv struct {
t *testing.T
ctrl *gomock.Controller
mockAccessor *MockStorageAccessor
core *TheCore
}

func newTestEnv(t *testing.T) *testEnv {
ctrl := gomock.NewController(t)
mockAccessor := NewMockStorageAccessor(ctrl)
theCore := NewCore(mockAccessor)
mockAccessor.EXPECT().
return &testEnv{t, ctrl, mockAccessor, theCore}
}

// expectNoError Checks if an error is nil. If not, it fails the test as an error.
func (e *testEnv) expectNoError(err error) {
if err != nil {
e.t.Errorf("unexpected error: %v", err)
}
}

// expectError Checks if an error is not nil. If it is, it fails the test as an error.
func (e *testEnv) expectError(err error) {
if err == nil {
e.t.Errorf("expected error")
}
}

// expectEqual Checks if two values are equal with reflect.DeepEqual. If not, it fails the test as an error.
func (e *testEnv) expectEqual(want, got any) {
if !reflect.DeepEqual(want, got) {
e.t.Errorf("want: %v, got: %v", want, got)
}
}

// TestCreateItem Given a description and the storage accessor returns an id, when CreateItem is called, then the item is created and returned with the id set.
func TestCreateItem(t *testing.T) {
// arrange
e := newTestEnv(t)
e.mockAccessor.EXPECT().
Create(gomock.Any()).
DoAndReturn(func(item *TodoItem) (int, error) {
id := 1
Expand All @@ -34,51 +68,41 @@ func TestCreateItem(t *testing.T) {

// act
want := TodoItem{Id: 1, Description: "some description", Completed: false}
got := theCore.CreateItem(want.Description)
got := e.core.CreateItem(want.Description)

// assert
if !reflect.DeepEqual(want, got) {
t.Errorf("want: %v, got: %v", want, got)
}
e.expectEqual(want, got)
}

// TestUpdateItem Given an item of a specific id is returned by the storage accessor, when UpdateItem is called, then the item is updated and returned with the new completed status.
func TestUpdateItem(t *testing.T) {
// arrange: mock storage accessor
ctrl := gomock.NewController(t)
mockAccessor := NewMockStorageAccessor(ctrl)
theCore := NewCore(mockAccessor)
mockAccessor.EXPECT().
// arrange
e := newTestEnv(t)
e.mockAccessor.EXPECT().
Read(gomock.Any()).
DoAndReturn(func(func(TodoItem) bool) []TodoItem {
return []TodoItem{
{Id: 1, Description: "some description", Completed: false},
}
})
mockAccessor.EXPECT().
e.mockAccessor.EXPECT().
Update(gomock.Any()).
Return(nil)

// act:
want := TodoItem{Id: 1, Description: "some description", Completed: true}
got, err := theCore.UpdateItem(want.Id, want.Completed)
got, err := e.core.UpdateItem(want.Id, want.Completed)

// assert: the item should be updated and returned without error
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(want, got) {
t.Errorf("want: %v, got: %v", want, got)
}
e.expectNoError(err)
e.expectEqual(want, got)
}

// TestUpdateItemNotFound Given an item of a specific id is not returned by the storage accessor, when UpdateItem is called, then an ItemNotFoundError is returned.
func TestUpdateItemNotFound(t *testing.T) {
// arrange: mock storage accessor
ctrl := gomock.NewController(t)
mockAccessor := NewMockStorageAccessor(ctrl)
theCore := NewCore(mockAccessor)
mockAccessor.EXPECT().
// arrange
e := newTestEnv(t)
e.mockAccessor.EXPECT().
Read(gomock.Any()).
DoAndReturn(func(func(TodoItem) bool) []TodoItem {
return []TodoItem{}
Expand All @@ -87,69 +111,57 @@ func TestUpdateItemNotFound(t *testing.T) {
// act:
id := 1
completed := true
_, err := theCore.UpdateItem(id, completed)
_, err := e.core.UpdateItem(id, completed)

// assert: an error should be returned
if err == nil {
t.Errorf("expected error")
}
e.expectError(err)
if reflect.TypeOf(err).String() != "core.TodoItemNotFoundError" {
t.Errorf("unexpected error: %v", err)
}
}

// TestDeleteItem Given an id and the storage accessor returns no error, when DeleteItem is called, then no error is returned.
func TestDeleteItem(t *testing.T) {
// arrange: mock storage accessor
ctrl := gomock.NewController(t)
mockAccessor := NewMockStorageAccessor(ctrl)
theCore := NewCore(mockAccessor)
mockAccessor.EXPECT().
// arrange
e := newTestEnv(t)
e.mockAccessor.EXPECT().
Delete(gomock.Any()).
Return(nil)

// act
id := 1
err := theCore.DeleteItem(id)
err := e.core.DeleteItem(id)

// assert
if err != nil {
t.Errorf("unexpected error: %v", err)
}
e.expectNoError(err)
}

// TestDeleteItemError Given an id and the storage accessor returns an error, when DeleteItem is called, then the error is returned.
func TestDeleteItemError(t *testing.T) {
// arrange: mock storage accessor
ctrl := gomock.NewController(t)
mockAccessor := NewMockStorageAccessor(ctrl)
theCore := NewCore(mockAccessor)
mockAccessor.EXPECT().
// arrange
e := newTestEnv(t)
e.mockAccessor.EXPECT().
Delete(gomock.Any()).
Return(errors.New("error"))

// act
id := 1
err := theCore.DeleteItem(id)
err := e.core.DeleteItem(id)

// assert
if err == nil {
t.Errorf("expected error")
}
e.expectError(err)
// NOTE: There's no guarantee that the error is the same error that was returned by the storage accessor.
}

// TestGetItems Given items are returned by the storage accessor, when GetItems is called, then the items are returned.
func TestGetItems(t *testing.T) {
// arrange: mock storage accessor
ctrl := gomock.NewController(t)
mockAccessor := NewMockStorageAccessor(ctrl)
theCore := NewCore(mockAccessor)
// arrange
e := newTestEnv(t)
mockItems := [2]TodoItem{
{Id: 1, Description: "some description", Completed: false},
{Id: 2, Description: "another description", Completed: true},
}
mockAccessor.EXPECT().
e.mockAccessor.EXPECT().
Read(gomock.Any()).
DoAndReturn(func(func(TodoItem) bool) []TodoItem {
// With completed = false.
Expand All @@ -159,10 +171,8 @@ func TestGetItems(t *testing.T) {
// act
completed := false
want := []TodoItem{mockItems[0]}
got := theCore.GetItems(completed)
got := e.core.GetItems(completed)

// assert
if !reflect.DeepEqual(want, got) {
t.Errorf("want: %v, got: %v", want, got)
}
e.expectEqual(want, got)
}
Loading
Loading