Skip to content

Commit

Permalink
pulling out a contract for the ingredient store to be used to test ag…
Browse files Browse the repository at this point in the history
…ainst a real db later
  • Loading branch information
sn-anura committed May 26, 2023
1 parent 801d024 commit 3a6289f
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 64 deletions.
8 changes: 0 additions & 8 deletions living-without-mocks/fakes.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,3 @@ type InMemoryRecipeStore struct {
func (s InMemoryRecipeStore) GetRecipes() []Recipe {
return s.Recipes
}

type InMemoryIngredientStore struct {
Ingredients []Ingredient
}

func (s InMemoryIngredientStore) GetIngredients() []Ingredient {
return s.Ingredients
}
13 changes: 13 additions & 0 deletions living-without-mocks/ingredients/in_memory_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ingredients

type InMemoryStore struct {
ingredients []Ingredient
}

func (s *InMemoryStore) Store(i ...Ingredient) {
s.ingredients = append(s.ingredients, i...)
}

func (s *InMemoryStore) GetIngredients() []Ingredient {
return s.ingredients
}
14 changes: 14 additions & 0 deletions living-without-mocks/ingredients/in_memory_store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ingredients_test

import (
"github.com/quii/learn-go-with-tests/living-without-mocks/ingredients"
"testing"
)

func TestInMemoryStore(t *testing.T) {
ingredients.StoreContract{
NewStore: func() ingredients.Store {
return &ingredients.InMemoryStore{}
},
}.Test(t)
}
11 changes: 11 additions & 0 deletions living-without-mocks/ingredients/ingredients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ingredients

type Ingredient struct {
Name string
Quantity int
}

type Store interface {
GetIngredients() []Ingredient
Store(...Ingredient)
}
25 changes: 25 additions & 0 deletions living-without-mocks/ingredients/store_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ingredients

import (
"github.com/alecthomas/assert/v2"
"testing"
)

type StoreContract struct {
NewStore func() Store
}

func (s StoreContract) Test(t *testing.T) {
t.Run("it returns what is put in", func(t *testing.T) {
want := []Ingredient{
{Name: "Bananas", Quantity: 2},
{Name: "Flour", Quantity: 1},
{Name: "Eggs", Quantity: 2},
}
store := s.NewStore()
store.Store(want...)

got := store.GetIngredients()
assert.Equal(t, got, want)
})
}
18 changes: 6 additions & 12 deletions living-without-mocks/recipe.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package living_without_mocks

import "github.com/quii/learn-go-with-tests/living-without-mocks/ingredients"

type Recipe struct {
Name string
Ingredients []Ingredient
}

type Ingredient struct {
Name string
Quantity int
Ingredients []ingredients.Ingredient
}

type RecipeBook interface {
GetRecipes() []Recipe
}

type IngredientStore interface {
GetIngredients() []Ingredient
}
type RecipeMatcher struct {
recipeBook RecipeBook
ingredientStore IngredientStore
ingredientStore ingredients.Store
}

func NewRecipeMatcher(recipes RecipeBook, ingredientStore IngredientStore) *RecipeMatcher {
func NewRecipeMatcher(recipes RecipeBook, ingredientStore ingredients.Store) *RecipeMatcher {
return &RecipeMatcher{recipeBook: recipes, ingredientStore: ingredientStore}
}

Expand All @@ -45,7 +39,7 @@ func (m RecipeMatcher) canMake(recipe Recipe) bool {
return true
}

func (m RecipeMatcher) hasIngredient(ingredient Ingredient) bool {
func (m RecipeMatcher) hasIngredient(ingredient ingredients.Ingredient) bool {
for _, pantryIngredient := range m.ingredientStore.GetIngredients() {
if pantryIngredient.Name == ingredient.Name {
return true
Expand Down
96 changes: 52 additions & 44 deletions living-without-mocks/recipe_matcher_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package living_without_mocks

import "testing"
import (
"github.com/quii/learn-go-with-tests/living-without-mocks/ingredients"
"testing"
)
import "github.com/alecthomas/assert/v2"

var (
bananaBread = Recipe{
Name: "Banana Bread",
Ingredients: []Ingredient{
Ingredients: []ingredients.Ingredient{
{Name: "Bananas", Quantity: 2},
{Name: "Flour", Quantity: 1},
{Name: "Eggs", Quantity: 2},
},
}
bananaMilkshake = Recipe{
Name: "Banana Milkshake",
Ingredients: []Ingredient{
Ingredients: []ingredients.Ingredient{
{Name: "Bananas", Quantity: 2},
{Name: "Milk", Quantity: 1},
},
Expand All @@ -23,58 +26,63 @@ var (
)

func TestRecipeMatcher(t *testing.T) {
assertAvailableRecipes := func(t *testing.T, ingredients []Ingredient, expectedRecipes []Recipe) {
ingredientsStore := InMemoryIngredientStore{Ingredients: ingredients}
recipeMatcher := NewRecipeMatcher(recipeStore, ingredientsStore)
suggestions := recipeMatcher.SuggestRecipes()

// create a map to count occurrences of each recipe in the suggestions
suggestionCounts := make(map[string]int)
for _, suggestion := range suggestions {
suggestionCounts[suggestion.Name]++
}

// check that the counts of the expected recipes match the actual counts in the suggestions
for _, expectedRecipe := range expectedRecipes {
actualCount, ok := suggestionCounts[expectedRecipe.Name]
if !ok {
t.Errorf("expected recipe %s not found in suggestions", expectedRecipe.Name)
continue
}
if actualCount != 1 {
t.Errorf("expected recipe %s to appear once in suggestions, but found %d occurrences", expectedRecipe.Name, actualCount)
}
}
// check that the number of suggestions matches the expected number of recipes
assert.Equal(t, len(suggestions), len(expectedRecipes))
}

t.Run("if we have no ingredients we can't make anything", func(t *testing.T) {
assertAvailableRecipes(t, []Ingredient{}, []Recipe{})
assertAvailableRecipes(t, &ingredients.InMemoryStore{}, []Recipe{})
})

t.Run("if we have the ingredients for banana bread we can make it", func(t *testing.T) {
assertAvailableRecipes(t, []Ingredient{
{Name: "Bananas", Quantity: 2},
{Name: "Flour", Quantity: 1},
{Name: "Eggs", Quantity: 2},
}, []Recipe{bananaBread})
store := &ingredients.InMemoryStore{}
store.Store(
ingredients.Ingredient{Name: "Bananas", Quantity: 2},
ingredients.Ingredient{Name: "Flour", Quantity: 1},
ingredients.Ingredient{Name: "Eggs", Quantity: 2},
)
assertAvailableRecipes(t, store, []Recipe{bananaBread})
})

t.Run("if we have bananas and milk, we can make banana milkshake", func(t *testing.T) {
assertAvailableRecipes(t, []Ingredient{
{Name: "Bananas", Quantity: 2},
{Name: "Milk", Quantity: 1},
}, []Recipe{bananaMilkshake})
store := &ingredients.InMemoryStore{}
store.Store(
ingredients.Ingredient{Name: "Bananas", Quantity: 2},
ingredients.Ingredient{Name: "Milk", Quantity: 1},
)
assertAvailableRecipes(t, store, []Recipe{bananaMilkshake})
})

t.Run("if we have ingredients for banana bread and milkshake, we can make both", func(t *testing.T) {
assertAvailableRecipes(t, []Ingredient{
{Name: "Bananas", Quantity: 2},
{Name: "Flour", Quantity: 1},
{Name: "Eggs", Quantity: 2},
{Name: "Milk", Quantity: 1},
}, []Recipe{bananaMilkshake, bananaBread})
store := &ingredients.InMemoryStore{}
store.Store(
ingredients.Ingredient{Name: "Bananas", Quantity: 2},
ingredients.Ingredient{Name: "Flour", Quantity: 1},
ingredients.Ingredient{Name: "Eggs", Quantity: 2},
ingredients.Ingredient{Name: "Milk", Quantity: 1},
)
assertAvailableRecipes(t, store, []Recipe{bananaMilkshake, bananaBread})
})

}

func assertAvailableRecipes(t *testing.T, ingredientStore ingredients.Store, expectedRecipes []Recipe) {
suggestions := NewRecipeMatcher(recipeStore, ingredientStore).SuggestRecipes()

// create a map to count occurrences of each recipe in the suggestions
suggestionCounts := make(map[string]int)
for _, suggestion := range suggestions {
suggestionCounts[suggestion.Name]++
}

// check that the counts of the expected recipes match the actual counts in the suggestions
for _, expectedRecipe := range expectedRecipes {
actualCount, ok := suggestionCounts[expectedRecipe.Name]
if !ok {
t.Errorf("expected recipe %s not found in suggestions", expectedRecipe.Name)
continue
}
if actualCount != 1 {
t.Errorf("expected recipe %s to appear once in suggestions, but found %d occurrences", expectedRecipe.Name, actualCount)
}
}
// check that the number of suggestions matches the expected number of recipes
assert.Equal(t, len(suggestions), len(expectedRecipes))
}

0 comments on commit 3a6289f

Please sign in to comment.