SlideShare une entreprise Scribd logo
1  sur  63
Télécharger pour lire hors ligne
Golang.tokyo
2018/08/21 (Tue)
( @duck8823 )
•
• @duck8823
•
• /
•
• Kotlin
• Golang
•
• @duck8823
•
•
•
• Kotlin
• Golang
•
• @duck8823
•
•
•
• Kotlin
• Golang
CI
• duck8823/duci
• Golang
• Docker
CI
• duck8823/duci
• Golang
• Docker
CI
• duck8823/duci
• Golang
• Docker
Webhooks
(HTTP)
Commit Status
(HTTP)
Webhooks
(HTTP)
Commit Status
(HTTP)
Webhooks
(HTTP)
Commit Status
(HTTP)
( )
• Incoming HTTP Requests
• Git Docker ( )
•
•
• Outgoing HTTP Requests
• Incoming HTTP Requests
• Git Docker ( ) )
• (
•
• Outgoing HTTP Requests
Incoming HTTP Requests
func HandlerFunc(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
body, _ := ioutil.ReadAll(r.Body)
w.Write([]byte(fmt.Sprintf("Hello %s.", body)))
}
• HandlerFunc
, . "
HandlerFunc
func Test_HandlerFunc(t *testing.T) {
// given
req := httptest.NewRequest("HELLO", "/", strings.NewReader("world"))
rec := httptest.NewRecorder()
// when
HandlerFunc(rec, req)
// then
if rec.Code != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code)
}
if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) {
t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes()))
}
}
HandlerFunc
func Test_HandlerFunc(t *testing.T) {
// given
req := httptest.NewRequest("HELLO", "/", strings.NewReader("world"))
rec := httptest.NewRecorder()
// when
HandlerFunc(rec, req)
// then
if rec.Code != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code)
}
if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) {
t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes()))
}
}
http.ResponseWriter
(*httptest.ResponseRecorder)
*http.Request
HandlerFunc
func Test_HandlerFunc(t *testing.T) {
// given
req := httptest.NewRequest("HELLO", "/", strings.NewReader("world"))
rec := httptest.NewRecorder()
// when
HandlerFunc(rec, req)
// then
if rec.Code != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code)
}
if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) {
t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes()))
}
}
HandlerFunc
func Test_HandlerFunc(t *testing.T) {
// given
req := httptest.NewRequest("HELLO", "/", strings.NewReader("world"))
rec := httptest.NewRecorder()
// when
HandlerFunc(rec, req)
// then
if rec.Code != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code)
}
if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) {
t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes()))
}
}
*httptest.ResponseRecorder
chi HandlerFunc
• URL ("/{param}") HandlerFunc
go-chi/chi
// "/{param}"
func ChiHandlerFunc(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
param := chi.URLParam(r.Context(), "param")
w.Write([]byte(fmt.Sprintf("Hello %s.", param)))
}
chi HandlerFunc
func TestChiHandlerFunc(t *testing.T) {
// given
ctx := chi.NewContext()
ctx.Params.Add("param", "world")
req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx)
rec := httptest.NewRecorder()
// when
ChiHandlerFunc(rec, req)
// then
// ...check rec
}
chi HandlerFunc
func TestChiHandlerFunc(t *testing.T) {
// given
ctx := chi.NewContext()
ctx.Params.Add("param", "world")
req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx)
rec := httptest.NewRecorder()
// when
ChiHandlerFunc(rec, req)
// then
// ...check rec
}
context.Context
chi HandlerFunc
func TestChiHandlerFunc(t *testing.T) {
// given
ctx := chi.NewContext()
ctx.Params.Add("param", "world")
req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx)
rec := httptest.NewRecorder()
// when
ChiHandlerFunc(rec, req)
// then
// ...check rec
}
*http.Request
context.Context
chi HandlerFunc
func TestChiHandlerFunc(t *testing.T) {
// given
ctx := chi.NewContext()
ctx.Params.Add("param", "world")
req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx)
rec := httptest.NewRecorder()
// when
ChiHandlerFunc(rec, req)
// then
// ...check rec
}
Incoming HTTP Requests
• httptest
• URL
• Incoming HTTP Requests
• Git Docker ( ) )
• (
•
• Outgoing HTTP Requests
Git Docker (3rd )
•
src-d/go-git moby/moby
•
•
type docker struct {
moby *client.Client
}
func (d *docker) Run(image string, cmd ...string) {
ctx := context.Background()
_, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{})
con, _ := d.moby.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd
}, nil, nil, "")
err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{})
}
• ( docker run )
type docker struct {
moby *client.Client
}
func (d *docker) Run(image string, cmd ...string) {
ctx := context.Background()
_, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{})
con, _ := d.moby.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd
}, nil, nil, "")
err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{})
}
• ( docker run )
type docker struct {
moby *client.Client
}
func (d *docker) Run(image string, cmd ...string) {
ctx := context.Background()
_, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{})
con, _ := d.moby.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd
}, nil, nil, "")
err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{})
}
• ( docker run )
type docker struct {
moby *client.Client
}
func (d *docker) Run(image string, cmd ...string) {
ctx := context.Background()
_, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{})
con, _ := d.moby.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd
}, nil, nil, "")
err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{})
}
• ( docker run )
type docker struct {
moby *client.Client
}
func (d *docker) Run(image string, cmd ...string) {
ctx := context.Background()
_, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{})
con, _ := d.moby.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd
}, nil, nil, "")
err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{})
}
• ( docker run )
type docker struct {
moby *client.Client
}
func (d *docker) Run(image string, cmd ...string) {
ctx := context.Background()
_, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{})
con, _ := d.moby.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd
}, nil, nil, "")
err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{})
} .
•
Mock
• *client.Client
type Moby interface {
ImagePull(...) (...)
ContainerCreate(...) (container.ContainerCreateCreatedBody, error)
ContainerStart(...) error
}
type docker struct {
moby Moby
}
Mock
• google/mock/gomock
func TestDocker_Run(t *testing.T) {
ctrl := gomock.NewController(t)
mockMoby := NewMockMoby(ctrl)
mockMoby.EXPECT().
ImagePull(gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes().
Return(nil, errors.New("error image pull"))
docker := &docker{moby: mockMoby}
docker.Run("centos", "echo", "hello world")
}
Git Docker (3rd )
•
• gomock
•
( mock )
• Incoming HTTP Requests
• Git Docker ( ) )
• (
•
• Outgoing HTTP Requests
package logger
var Writer io.Writer = os.Stdout
func Debug(message string) {
fmt.Fprintf(Writer, "[DEBUG] %sn", message)
}
•
• ( os.Stdout ) io.Writer (*os.File )
func TestDebug(t *testing.T) {
// given
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "[DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
writer.Close()
actual, _ := ioutil.ReadAll(reader)
if string(actual) != expected {
t.Errorf("wont %s, but got %s", expected, actual)
}
}
func TestDebug(t *testing.T) {
// given
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "[DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
writer.Close()
actual, _ := ioutil.ReadAll(reader)
if string(actual) != expected {
t.Errorf("wont %s, but got %s", expected, actual)
}
}
) (
).(
1
( ( 2
func TestDebug(t *testing.T) {
// given
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "[DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
writer.Close()
actual, _ := ioutil.ReadAll(reader)
if string(actual) != expected {
t.Errorf("wont %s, but got %s", expected, actual)
}
}
func TestDebug(t *testing.T) {
// given
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "[DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
writer.Close()
actual, _ := ioutil.ReadAll(reader)
if string(actual) != expected {
t.Errorf("wont %s, but got %s", expected, actual)
}
}
func TestDebug(t *testing.T) {
// given
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "[DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
writer.Close()
actual, _ := ioutil.ReadAll(reader)
if string(actual) != expected {
t.Errorf("wont %s, but got %s", expected, actual)
}
}
• io.Writer
io.Pipe
• Incoming HTTP Requests
• Git Docker ( ) )
• (
•
• Outgoing HTTP Requests
•
func Debug(message string) {
now := time.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(Writer, "%s [DEBUG] %sn", now, message)
}
time.Now()
•
func Debug(message string) {
now := clock.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(Writer, "%s [DEBUG] %sn", now, message)
}
package clock
import "time"
var Now = func () time.Time {
return time.Now()
}
func TestDebug(t *testing.T) {
// given
jst, _ := time.LoadLocation("Asia/Tokyo")
clock.Now = func() time.Time {
return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst)
}
// and
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
// ...
}
func TestDebug(t *testing.T) {
// given
jst, _ := time.LoadLocation("Asia/Tokyo")
clock.Now = func() time.Time {
return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst)
}
// and
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
// ...
}
.
func TestDebug(t *testing.T) {
// given
jst, _ := time.LoadLocation("Asia/Tokyo")
clock.Now = func() time.Time {
return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst)
}
// and
reader, writer, _ := os.Pipe()
logger.Writer = writer
// and
expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n"
// when
logger.Debug("Hello World.")
// then
// ...
}
• ( )
• time.Now
• Incoming HTTP Requests
• Git Docker ( ) )
• (
•
• Outgoing HTTP Requests
Outgoing HTTP Requests
• EO
• IL P
• H / D G RA
Outgoing HTTP Requests
• 2 ) ) / ( (
func TestRequest(t *testing.T) {
defer gock.Off()
gock.New("http://example.com").
Post("/").
Reply(http.StatusOK)
resp, _ := http.Post("http://example.com/", "application/json", nil)
if resp.StatusCode != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode)
}
}
Outgoing HTTP Requests
• 2 ) ) / ( (
func TestRequest(t *testing.T) {
defer gock.Off()
gock.New("http://example.com").
Post("/").
Reply(http.StatusOK)
resp, _ := http.Post("http://example.com/", "application/json", nil)
if resp.StatusCode != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode)
}
}
Outgoing HTTP Requests
• 2 ) ) / ( (
func TestRequest(t *testing.T) {
defer gock.Off()
gock.New("http://example.com").
Post("/").
Reply(http.StatusOK)
resp, _ := http.Post("http://example.com/", "application/json", nil)
if resp.StatusCode != http.StatusOK {
t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode)
}
}
Outgoing HTTP Requests
•
•
Webhooks
(HTTP)
Commit Status
(HTTP)
Webhooks
(HTTP)
Commit Status
(HTTP)
Webhooks
(HTTP)
Commit Status
(HTTP)
Webhooks
(HTTP)
Commit Status
(HTTP)
Webhooks
(HTTP)
Commit Status
(HTTP)
)& )
.
& &(
Webhooks
(HTTP)
Commit Status
(HTTP)
• oc
• H
• ) ( k
• P T
• p
• he ) (M

Contenu connexe

Tendances

GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンAkihiko Horiuchi
 
サーバーサイド Kotlin のテストフレームワーク事情
サーバーサイド Kotlin のテストフレームワーク事情サーバーサイド Kotlin のテストフレームワーク事情
サーバーサイド Kotlin のテストフレームワーク事情Shinya Mochida
 
性能測定道 実践編
性能測定道 実践編性能測定道 実践編
性能測定道 実践編Yuto Hayamizu
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードShigenori Sagawa
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Kohei Tokunaga
 
PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法 ※講演は翻訳資料にて行います。 - Getting the Best...
PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法  ※講演は翻訳資料にて行います。 - Getting the Best...PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法  ※講演は翻訳資料にて行います。 - Getting the Best...
PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法 ※講演は翻訳資料にて行います。 - Getting the Best...Holden Karau
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」Takuto Wada
 
Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解Masahito Zembutsu
 
マイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCマイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCdisc99_
 
Building the Game Server both API and Realtime via c#
Building the Game Server both API and Realtime via c#Building the Game Server both API and Realtime via c#
Building the Game Server both API and Realtime via c#Yoshifumi Kawai
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
TLS 1.3 と 0-RTT のこわ〜い話
TLS 1.3 と 0-RTT のこわ〜い話TLS 1.3 と 0-RTT のこわ〜い話
TLS 1.3 と 0-RTT のこわ〜い話Kazuho Oku
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装infinite_loop
 
Googleのインフラ技術から考える理想のDevOps
Googleのインフラ技術から考える理想のDevOpsGoogleのインフラ技術から考える理想のDevOps
Googleのインフラ技術から考える理想のDevOpsEtsuji Nakai
 
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティスコンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、ReactのベストプラクティスとバッドプラクティスKohei Asai
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
Python製BDDツールで自動化してみた
Python製BDDツールで自動化してみたPython製BDDツールで自動化してみた
Python製BDDツールで自動化してみたKeijiUehata1
 
Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!Masahito Zembutsu
 
DockerでWordPressサイトを開発してみよう
DockerでWordPressサイトを開発してみようDockerでWordPressサイトを開発してみよう
DockerでWordPressサイトを開発してみようmookjp
 
PostgreSQL Unconference #29 Unicode IVS
PostgreSQL Unconference #29 Unicode IVSPostgreSQL Unconference #29 Unicode IVS
PostgreSQL Unconference #29 Unicode IVSNoriyoshi Shinoda
 

Tendances (20)

GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホン
 
サーバーサイド Kotlin のテストフレームワーク事情
サーバーサイド Kotlin のテストフレームワーク事情サーバーサイド Kotlin のテストフレームワーク事情
サーバーサイド Kotlin のテストフレームワーク事情
 
性能測定道 実践編
性能測定道 実践編性能測定道 実践編
性能測定道 実践編
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコード
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法 ※講演は翻訳資料にて行います。 - Getting the Best...
PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法  ※講演は翻訳資料にて行います。 - Getting the Best...PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法  ※講演は翻訳資料にて行います。 - Getting the Best...
PySparkによるジョブを、より速く、よりスケーラブルに実行するための最善の方法 ※講演は翻訳資料にて行います。 - Getting the Best...
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解Docker道場オンライン#1 Docker基礎概念と用語の理解
Docker道場オンライン#1 Docker基礎概念と用語の理解
 
マイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCマイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPC
 
Building the Game Server both API and Realtime via c#
Building the Game Server both API and Realtime via c#Building the Game Server both API and Realtime via c#
Building the Game Server both API and Realtime via c#
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
TLS 1.3 と 0-RTT のこわ〜い話
TLS 1.3 と 0-RTT のこわ〜い話TLS 1.3 と 0-RTT のこわ〜い話
TLS 1.3 と 0-RTT のこわ〜い話
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
 
Googleのインフラ技術から考える理想のDevOps
Googleのインフラ技術から考える理想のDevOpsGoogleのインフラ技術から考える理想のDevOps
Googleのインフラ技術から考える理想のDevOps
 
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティスコンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
Python製BDDツールで自動化してみた
Python製BDDツールで自動化してみたPython製BDDツールで自動化してみた
Python製BDDツールで自動化してみた
 
Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!
 
DockerでWordPressサイトを開発してみよう
DockerでWordPressサイトを開発してみようDockerでWordPressサイトを開発してみよう
DockerでWordPressサイトを開発してみよう
 
PostgreSQL Unconference #29 Unicode IVS
PostgreSQL Unconference #29 Unicode IVSPostgreSQL Unconference #29 Unicode IVS
PostgreSQL Unconference #29 Unicode IVS
 

Similaire à 外部環境への依存をテストする

Building Go Web Apps
Building Go Web AppsBuilding Go Web Apps
Building Go Web AppsMark
 
HashiCorp Vault Plugin Infrastructure
HashiCorp Vault Plugin InfrastructureHashiCorp Vault Plugin Infrastructure
HashiCorp Vault Plugin InfrastructureNicolas Corrarello
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on AndroidSven Haiges
 
Net/http and the http.handler interface
Net/http and the http.handler interfaceNet/http and the http.handler interface
Net/http and the http.handler interfaceJoakim Gustin
 
Net/http and the http.handler interface
Net/http and the http.handler interfaceNet/http and the http.handler interface
Net/http and the http.handler interfaceEvolve
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good TestsTomek Kaczanowski
 
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackNelson Glauber Leal
 
Non Blocking I/O for Everyone with RxJava
Non Blocking I/O for Everyone with RxJavaNon Blocking I/O for Everyone with RxJava
Non Blocking I/O for Everyone with RxJavaFrank Lyaruu
 
Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...Docker, Inc.
 
The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185Mahmoud Samir Fayed
 
2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good Tests2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good TestsTomek Kaczanowski
 
Wprowadzenie do technologi Big Data i Apache Hadoop
Wprowadzenie do technologi Big Data i Apache HadoopWprowadzenie do technologi Big Data i Apache Hadoop
Wprowadzenie do technologi Big Data i Apache HadoopSages
 
Job Queue in Golang
Job Queue in GolangJob Queue in Golang
Job Queue in GolangBo-Yi Wu
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScriptQiangning Hong
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs偉格 高
 
Deep Dumpster Diving
Deep Dumpster DivingDeep Dumpster Diving
Deep Dumpster DivingRonnBlack
 
RESTful Web Applications with Google Go
RESTful Web Applications with Google GoRESTful Web Applications with Google Go
RESTful Web Applications with Google GoFrank Müller
 
NET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxNET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxpetabridge
 

Similaire à 外部環境への依存をテストする (20)

Building Go Web Apps
Building Go Web AppsBuilding Go Web Apps
Building Go Web Apps
 
HashiCorp Vault Plugin Infrastructure
HashiCorp Vault Plugin InfrastructureHashiCorp Vault Plugin Infrastructure
HashiCorp Vault Plugin Infrastructure
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
 
Net/http and the http.handler interface
Net/http and the http.handler interfaceNet/http and the http.handler interface
Net/http and the http.handler interface
 
Net/http and the http.handler interface
Net/http and the http.handler interfaceNet/http and the http.handler interface
Net/http and the http.handler interface
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Aplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & JetpackAplicações assíncronas no Android com
Coroutines & Jetpack
Aplicações assíncronas no Android com
Coroutines & Jetpack
 
Non Blocking I/O for Everyone with RxJava
Non Blocking I/O for Everyone with RxJavaNon Blocking I/O for Everyone with RxJava
Non Blocking I/O for Everyone with RxJava
 
Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...Online Meetup: Why should container system / platform builders care about con...
Online Meetup: Why should container system / platform builders care about con...
 
The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185
 
2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good Tests2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good Tests
 
Wprowadzenie do technologi Big Data i Apache Hadoop
Wprowadzenie do technologi Big Data i Apache HadoopWprowadzenie do technologi Big Data i Apache Hadoop
Wprowadzenie do technologi Big Data i Apache Hadoop
 
Job Queue in Golang
Job Queue in GolangJob Queue in Golang
Job Queue in Golang
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
 
Deep Dumpster Diving
Deep Dumpster DivingDeep Dumpster Diving
Deep Dumpster Diving
 
RESTful Web Applications with Google Go
RESTful Web Applications with Google GoRESTful Web Applications with Google Go
RESTful Web Applications with Google Go
 
NET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxNET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptx
 

Plus de Shunsuke Maeda

私とOSS活動とPerl
私とOSS活動とPerl私とOSS活動とPerl
私とOSS活動とPerlShunsuke Maeda
 
テストを書いてGradleプラグインの開発効率を改善しよう
テストを書いてGradleプラグインの開発効率を改善しようテストを書いてGradleプラグインの開発効率を改善しよう
テストを書いてGradleプラグインの開発効率を改善しようShunsuke Maeda
 
iOSにおけるコードレビューを一歩先へ進める
iOSにおけるコードレビューを一歩先へ進めるiOSにおけるコードレビューを一歩先へ進める
iOSにおけるコードレビューを一歩先へ進めるShunsuke Maeda
 
iOSで利用できるデバイスファームのメリット・デメリットの紹介
iOSで利用できるデバイスファームのメリット・デメリットの紹介iOSで利用できるデバイスファームのメリット・デメリットの紹介
iOSで利用できるデバイスファームのメリット・デメリットの紹介Shunsuke Maeda
 
Pull request時の画面差分取得の自動化
Pull request時の画面差分取得の自動化Pull request時の画面差分取得の自動化
Pull request時の画面差分取得の自動化Shunsuke Maeda
 
Dangerでpull requestレビューの指摘事項を減らす
Dangerでpull requestレビューの指摘事項を減らすDangerでpull requestレビューの指摘事項を減らす
Dangerでpull requestレビューの指摘事項を減らすShunsuke Maeda
 

Plus de Shunsuke Maeda (7)

私とOSS活動とPerl
私とOSS活動とPerl私とOSS活動とPerl
私とOSS活動とPerl
 
テストを書いてGradleプラグインの開発効率を改善しよう
テストを書いてGradleプラグインの開発効率を改善しようテストを書いてGradleプラグインの開発効率を改善しよう
テストを書いてGradleプラグインの開発効率を改善しよう
 
DroidKaigi_devicefarm
DroidKaigi_devicefarmDroidKaigi_devicefarm
DroidKaigi_devicefarm
 
iOSにおけるコードレビューを一歩先へ進める
iOSにおけるコードレビューを一歩先へ進めるiOSにおけるコードレビューを一歩先へ進める
iOSにおけるコードレビューを一歩先へ進める
 
iOSで利用できるデバイスファームのメリット・デメリットの紹介
iOSで利用できるデバイスファームのメリット・デメリットの紹介iOSで利用できるデバイスファームのメリット・デメリットの紹介
iOSで利用できるデバイスファームのメリット・デメリットの紹介
 
Pull request時の画面差分取得の自動化
Pull request時の画面差分取得の自動化Pull request時の画面差分取得の自動化
Pull request時の画面差分取得の自動化
 
Dangerでpull requestレビューの指摘事項を減らす
Dangerでpull requestレビューの指摘事項を減らすDangerでpull requestレビューの指摘事項を減らす
Dangerでpull requestレビューの指摘事項を減らす
 

Dernier

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...masabamasaba
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburgmasabamasaba
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024VictoriaMetrics
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareJim McKeeth
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...masabamasaba
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 

Dernier (20)

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 

外部環境への依存をテストする

  • 11. ( ) • Incoming HTTP Requests • Git Docker ( ) • • • Outgoing HTTP Requests
  • 12.
  • 13. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  • 14. Incoming HTTP Requests func HandlerFunc(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) body, _ := ioutil.ReadAll(r.Body) w.Write([]byte(fmt.Sprintf("Hello %s.", body))) } • HandlerFunc , . "
  • 15. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } }
  • 16. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } } http.ResponseWriter (*httptest.ResponseRecorder) *http.Request
  • 17. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } }
  • 18. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } } *httptest.ResponseRecorder
  • 19. chi HandlerFunc • URL ("/{param}") HandlerFunc go-chi/chi // "/{param}" func ChiHandlerFunc(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) param := chi.URLParam(r.Context(), "param") w.Write([]byte(fmt.Sprintf("Hello %s.", param))) }
  • 20. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec }
  • 21. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec } context.Context
  • 22. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec } *http.Request context.Context
  • 23. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec }
  • 24. Incoming HTTP Requests • httptest • URL
  • 25. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  • 26. Git Docker (3rd ) • src-d/go-git moby/moby • •
  • 27. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  • 28. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  • 29. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  • 30. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  • 31. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  • 32. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } . •
  • 33. Mock • *client.Client type Moby interface { ImagePull(...) (...) ContainerCreate(...) (container.ContainerCreateCreatedBody, error) ContainerStart(...) error } type docker struct { moby Moby }
  • 34. Mock • google/mock/gomock func TestDocker_Run(t *testing.T) { ctrl := gomock.NewController(t) mockMoby := NewMockMoby(ctrl) mockMoby.EXPECT(). ImagePull(gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes(). Return(nil, errors.New("error image pull")) docker := &docker{moby: mockMoby} docker.Run("centos", "echo", "hello world") }
  • 35. Git Docker (3rd ) • • gomock • ( mock )
  • 36. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  • 37. package logger var Writer io.Writer = os.Stdout func Debug(message string) { fmt.Fprintf(Writer, "[DEBUG] %sn", message) } • • ( os.Stdout ) io.Writer (*os.File )
  • 38. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  • 39. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } } ) ( ).( 1 ( ( 2
  • 40. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  • 41. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  • 42. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  • 44. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  • 45. • func Debug(message string) { now := time.Now().Format("2006-01-02 15:04:05") fmt.Fprintf(Writer, "%s [DEBUG] %sn", now, message) }
  • 46. time.Now() • func Debug(message string) { now := clock.Now().Format("2006-01-02 15:04:05") fmt.Fprintf(Writer, "%s [DEBUG] %sn", now, message) } package clock import "time" var Now = func () time.Time { return time.Now() }
  • 47. func TestDebug(t *testing.T) { // given jst, _ := time.LoadLocation("Asia/Tokyo") clock.Now = func() time.Time { return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst) } // and reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then // ... }
  • 48. func TestDebug(t *testing.T) { // given jst, _ := time.LoadLocation("Asia/Tokyo") clock.Now = func() time.Time { return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst) } // and reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then // ... } .
  • 49. func TestDebug(t *testing.T) { // given jst, _ := time.LoadLocation("Asia/Tokyo") clock.Now = func() time.Time { return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst) } // and reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then // ... }
  • 50. • ( ) • time.Now
  • 51. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  • 52. Outgoing HTTP Requests • EO • IL P • H / D G RA
  • 53. Outgoing HTTP Requests • 2 ) ) / ( ( func TestRequest(t *testing.T) { defer gock.Off() gock.New("http://example.com"). Post("/"). Reply(http.StatusOK) resp, _ := http.Post("http://example.com/", "application/json", nil) if resp.StatusCode != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode) } }
  • 54. Outgoing HTTP Requests • 2 ) ) / ( ( func TestRequest(t *testing.T) { defer gock.Off() gock.New("http://example.com"). Post("/"). Reply(http.StatusOK) resp, _ := http.Post("http://example.com/", "application/json", nil) if resp.StatusCode != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode) } }
  • 55. Outgoing HTTP Requests • 2 ) ) / ( ( func TestRequest(t *testing.T) { defer gock.Off() gock.New("http://example.com"). Post("/"). Reply(http.StatusOK) resp, _ := http.Post("http://example.com/", "application/json", nil) if resp.StatusCode != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode) } }
  • 63. • oc • H • ) ( k • P T • p • he ) (M