Golang'da birim testleri yazarken servisleri taklit etmek istiyorsanız, sadece arayüzleri kullanabilirsiniz. Testlerinizin "gerçek" kodu çağırmasını istemiyorsanız, genellikle taklit etmek gerekir. Örneğimiz Twitter ile kimlik doğrulaması yapıyor ve ona mesaj gönderiyor.


Yapı


└── internal
├── app
│ ├── app.go
│ └── app_test.go
└── twitter
└── twitter.go

Dosyalar


app.go


package app

import (
"github.com/inanzzz/internal/twitter"
)

type App struct {
client twitter.Twitter
}

func New(c twitter.Twitter) App {
return App{client: c}
}

func (a App) SendMessage(msg string) string {
return a.client.Tweet(msg)
}

twitter.go


package twitter

import (
"fmt"
)

type Twitter struct {
BaseURL string
ClientID string
ClientSecret string
}

func New() Twitter {
return Twitter{
BaseURL: "https://www.real-url.com",
ClientID: "real-id",
ClientSecret: "real-secret",
}
}

func (t Twitter) Tweet(msg string) string {
return fmt.Sprintf(
"Sending '%s' to '%s' using '%s:%s' credentials",
msg,
t.BaseURL,
t.ClientID,
t.ClientSecret,
)
}

app_test.go


package app

import (
"log"
"testing"

"github.com/inanzzz/internal/twitter"
)

func TestApp_SendMessage(t *testing.T) {
twt := twitter.New()

app := New(twt)

res := app.SendMessage("hello")

log.Println(res)
}

Sonuç


Aşağıda görebileceğiniz gibi, testi çalıştırdığımızda, gerçek yapılandırma parametreleriyle gerçek Twitter API'sı çağrıldı. Bu kesinlikle kaçınmak istediğiniz bir şey. Sorunu bir sonraki aşamada çözelim.


=== RUN   TestApp_SendMessage
2020/05/12 17:50:39 Sending 'hello' to 'https://www.real-url.com' using 'real-id:real-secret' credentials
--- PASS: TestApp_SendMessage (0.00s)
PASS

Yapı


└── internal
├── app
│ ├── app.go
│ └── app_test.go
└── twitter
├── mock
│ └── twitter.go // This is new
└── twitter.go

Dosyalar


app.go


Somut olan twitter.Twitter tipi, somut olmayan twitter.SocialMedia arayüzü ile değiştirdik.


package app

import (
"github.com/inanzzz/internal/twitter"
)

type App struct {
client twitter.SocialMedia
}

func New(c twitter.SocialMedia) App {
return App{client: c}
}

func (a App) SendMessage(msg string) string {
return a.client.Tweet(msg)
}

twitter.go


Burada somut olmayan SocialMedia arayüz tanımlandı.


package twitter

import (
"fmt"
)

type SocialMedia interface {
Tweet(msg string) string
}

type Twitter struct {
BaseURL string
ClientID string
ClientSecret string
}

func New() Twitter {
return Twitter{
BaseURL: "https://www.real-url.com",
ClientID: "real-id",
ClientSecret: "real-secret",
}
}

func (t Twitter) Tweet(msg string) string {
return fmt.Sprintf(
"Sending '%s' to '%s' using '%s:%s' credentials",
msg,
t.BaseURL,
t.ClientID,
t.ClientSecret,
)
}

mock/twitter.go


Yarattığımız bu taklit twitter.SocialMedia arayüzünün imzasını tatmin ediyor.


package mock

import (
"fmt"
)

type TwitterMock struct {
BaseURL string
ClientID string
ClientSecret string
}

func (t TwitterMock) Tweet(msg string) string {
return fmt.Sprintf(
"Sending '%s' to '%s' using '%s:%s' credentials",
msg,
t.BaseURL,
t.ClientID,
t.ClientSecret,
)
}

app_test.go


Burada değişken olarak mock.TwitterMock'u enjekte ediyoruz.


package app

import (
"log"
"testing"

"github.com/inanzzz/internal/twitter/mock"
)

func TestApp_SendMessage(t *testing.T) {
twt := mock.TwitterMock{
BaseURL: "https://www.mock-url.com",
ClientID: "mock-id",
ClientSecret: "mock-secret",
}

app := New(twt)

res := app.SendMessage("hello")

log.Println(res)
}

Sonuç


Aşağıda görebileceğiniz gibi, testimiz taklit servisle etkileşime girdi. Mantıklı olduğunda, taklit dosyalardaki değerleri el ile kodlayabilirsiniz.


=== RUN   TestApp_SendMessage
2020/05/12 18:01:51 Sending 'hello' to 'https://www.mock-url.com' using 'mock-id:mock-secret' credentials
--- PASS: TestApp_SendMessage (0.00s)
PASS