最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

unit testing - Mocking external package struct that depends on HTTP request - Stack Overflow

programmeradmin2浏览0评论

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
Add a comment  | 

2 Answers 2

Reset to default 1

Depending 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

发布评论

评论列表(0)

  1. 暂无评论