I'm working on a terminal game based on this card game that gets its cards from deckofcardsapi. Cards are stored in a struct named Card
in a package called card
and each Card
is stored in a struct called Board
inside an external package called board
. Since Card
has to make requests to the external API my plan is to create Card
fakes with interfaces for testing Board
board properties such as isFull
and errors that panic the program when a user tries to put a card on a full or non existent field.
This is my first project in Go but I've already got experience with Python, mocking and unit testing. I'd like to create Card
fakes without any interface parameter but all examples I've seen on the Internet include a parameter that I think is follows the principle Accept everything, return structs or something like that. I've read Go for devops testing framework and Go workshop interfaces chapter on packtpub plus lots of research on the Internet that usually showed me complex examples that left me confused. As a newly Go adopter my question is: How can mock an external package struct that depends on HTTP request using fakes and interfaces?
This is my code:
board/board_test.go
package board
import (
"errors"
"poker_squares/card"
"testing"
)
type fakeCard struct {
data card.Card
err bool
}
func (c fakeCard) Card() (card.Card, error) {
if c.err {
return "", errors.New("Couldn't create card")
}
return c.data, nil
}
func TestBoard(t *testing.T) {
tests := []struct {
desc string
board Board
want string
expectError bool
}{
{
desc: "Add a card, board isn't full and isFull property is false",
expectError: false,
},
# Remove the rest of tests for brevity
}
}
card/card.go
package card
type CardGetter interface {
Card() (Card, error)
}
type Card struct {
value string
suit string
}
func(c Card)()(Card, error){
# Fetch card from deckofcards API
}
I'm working on a terminal game based on this card game that gets its cards from deckofcardsapi. Cards are stored in a struct named Card
in a package called card
and each Card
is stored in a struct called Board
inside an external package called board
. Since Card
has to make requests to the external API my plan is to create Card
fakes with interfaces for testing Board
board properties such as isFull
and errors that panic the program when a user tries to put a card on a full or non existent field.
This is my first project in Go but I've already got experience with Python, mocking and unit testing. I'd like to create Card
fakes without any interface parameter but all examples I've seen on the Internet include a parameter that I think is follows the principle Accept everything, return structs or something like that. I've read Go for devops testing framework and Go workshop interfaces chapter on packtpub plus lots of research on the Internet that usually showed me complex examples that left me confused. As a newly Go adopter my question is: How can mock an external package struct that depends on HTTP request using fakes and interfaces?
This is my code:
board/board_test.go
package board
import (
"errors"
"poker_squares/card"
"testing"
)
type fakeCard struct {
data card.Card
err bool
}
func (c fakeCard) Card() (card.Card, error) {
if c.err {
return "", errors.New("Couldn't create card")
}
return c.data, nil
}
func TestBoard(t *testing.T) {
tests := []struct {
desc string
board Board
want string
expectError bool
}{
{
desc: "Add a card, board isn't full and isFull property is false",
expectError: false,
},
# Remove the rest of tests for brevity
}
}
card/card.go
package card
type CardGetter interface {
Card() (Card, error)
}
type Card struct {
value string
suit string
}
func(c Card)()(Card, error){
# Fetch card from deckofcards API
}
Share
Improve this question
asked Mar 14 at 17:34
wavesinaroomwavesinaroom
2973 silver badges12 bronze badges
2 Answers
Reset to default 1Depending on how you structure your program, of course, it might be advisable to separate the network interactions from other behavior of your Card
(is there any). Presuming fetching the data upon a call to Card
makes for a good API design (can't tell, so going with what's suggested), it can be achieved by introducing a new field in Card
of type CardGetter
.
And for that, you would have the prod CardGetter
implementation doing the actual network call, and a couple of test-double implementations to simulate the behaviours you will need for testing (return expected data, induce failure, etc).
Note that mocking in python is relying heavily on the dynamic properties of the language, so mocking will work substantially differently in GO (or any other stricty and statically typed programing language).
You are generaly expected to declare interfaces for anything that you will need to mock-out, and create types/impls for the mocks. Also, it will be up to you to get those injected into the SUTs. No dyanmic configs of struct behaviour during test runtime, no annotations to patch type behaviour globally, etc.
I only had to implement CardGetter
interface in my code the following to fake Card
:
type fakeCard struct {
card Card
err bool
}
func (c fakeCard) Card() (*Card, error) {
values := []string{"2", "3", "4", "5", "6", "7", "8", "9", "J", "Q", "K", "A"}
suit := []string{"HEARTS", "SPADES", "CLUBS", "DIAMONDS"}
card := Card{Value: values[rand.Intn(len(values))], Suit: suit[rand.Intn(len(suit))]}
if c.err {
return nil, errors.New("Couldn't create card")
}
return &card, nil
}
Hope that this answer helps other beginners like me in the future