🦔

Type switchesの使い所

2022/07/29に公開

初めに

以前、testの実装でメソッドをmock化しようとした時に、
返り値にinterface型の値を返す(正確にいうと、参照渡しで渡された値に、返り値を入れて返す)
メソッドのmockを実装した際に少し詰まった。

その際にType switchesを使用して解決したのでメモとして残しておく。

前提

テストでmock化したかったのは下記実装のRunメソッド
引数で受け取ったresに任意の値を入れて返すということをやりたかった。

package gqlclient

import (
	"context"
	"github.com/machinebox/graphql"
)

type GQLClient interface {
	Run(ctx context.Context, query string, vars map[string]interface{}, res interface{}) error
}

type gqlClient struct {
	client  *graphql.Client
}

func New(endpoint string) GQLClient {
	return &gqlClient{
		client: graphql.NewClient(endpoint),
	}
}

func (c *gqlClient) Run(
	ctx context.Context,
	query string,
	vars map[string]interface{},
	res interface{},
) error {
	req := graphql.NewRequest(query)

	for k, v := range vars {
		req.Var(k, v)
	}

	if err := c.client.Run(ctx, req, &res); err != nil {
		return err
	}

	return nil
}

実装

引数resはどんなクエリの型でも受け取れるように、interface型で定義されている。
このようなとき、resHogeという形であればHogeに対して返り値を代入してあげるという処理が必要。

このような時にType switchesを使って実装した。

Type switches

https://go-tour-jp.appspot.com/methods/16
Type switchesとは型の変換をswtich文のような形で書ける構文
interface型の値iの型がint型だった場合は、1を代入し、string型の場合は"string"
を代入できる。

switch v := i.(type) {
case int:
    v = 1
case string:
    v = "string"
}

mockしたコード

package gqlclient

import (
	"context"
)

type IDResponse struct {
	ID int `json:"id"`
}
type Model struct {
	ID IDResponse `json:"sidecar"`
}
type CreateResponse struct {
	Model IDResponse `json:"sidecar"`
}

type gqlClientMock struct{}

func NewMock() *gqlClientMock {
	return new(gqlClientMock)
}
func (c *gqlClientMock) Run(
	ctx context.Context,
	query string,
	vars map[string]interface{},
	res interface{},
) error {
	switch v := res.(type) {
	case *CreateResponse:
		v.Model.ID = 1
	}
	return nil
}


所感

interface型の値であっても、型安全に値を扱うことができ、非常に便利だと感じた。
普段TypeScriptをよく書くが、同じような書き方はないので勉強になった。

Discussion