unit-testinggointerfacemocking

Mocking external package struct that depends on HTTP request


I'm working on a terminal game based on this card game that gets its cards from deckofcardsapi.com. 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
}

Solution

  • 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