의존성 주입 = 클래스에 필요한 인자를 내부에서 생성하지 않고 외부에서 제공.

package notifications

import (
	"errors"
	"testing"

	"github.com/fteem/order-notifications/user"
)

func TestInformOrderShipped(t *testing.T) {
	cases := []struct {
		user         user.User
		orderID      string
		sendingError error
		name         string
		want         bool
	}{
		{
			user:         user.User{"Peggy", "+12 345 678 999"},
			orderID:      "12345",
			sendingError: nil,
			want:         true,
			name:         "Successful send",
		},
		{
			user:         user.User{"Peggy", "+12 345 678 999"},
			orderID:      "12345",
			sendingError: errors.New("Sending failed"),
			want:         false,
			name:         "Unsuccessful send",
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			**mockSend := func(user.User, string) error {
				return tc.sendingError
			}//스파이함수**

			got := InformOrderShipped(tc.user, tc.orderID, **mockSend**)

			if tc.want != got {
				t.Errorf("Want '%t', got '%t'", tc.want, got)
			}
		})
	}
}
package notifications

import (
	"fmt"
	"os/user"
)

func InformOrderShipped(receiver user.User, orderID string, **sendSMS func(user.User, string) error**) bool {
	message := fmt.Sprintf("Your order #%s is shipped!", orderID)
	//function closure argument
	err := sendSMS(receiver, message)

	if err != nil {
		return false
	}

	return true
}

함수를 고차함수의 인자로 전달하고, 해당 함수는 외부의 다른 인수에 접근

(function closure arg.)

⇒ 쉽게 mock & test가능,

원래는

package notifications

import (
	"fmt"

	"github.com/fteem/order-notifications/sms"
	"github.com/fteem/order-notifications/user"
)

func InformOrderShipped(receiver user.User, orderID string) bool {
	message := fmt.Sprintf("Your order #%s is shipped!", orderID)
	err := sms.Send(receiver, message)

	if err != nil {
		return false
	}

	return true
}
---
package sms

import (
	"time"

	"github.com/fteem/order-notifications/user"
)

func Send(receiver user.User, message string) error {
	// Simulating API call...
	time.Sleep(3 * time.Second)

	return nil
}

이와 같이 코드를 작성하고,

아래와 같이 테스트 코드를 썼다. 하지만 이러면 비동기 처리가 들어간 함수 테스팅의 경우엔 테스트 시간이 낭비된다는 문제점이있다.

package notifications

import "testing"

func TestInformOrderShipped(t *testing.T) {
	user := User{
		Name:  "Peggy",
		Phone: "+12 345 678 999",
	}
	orderID := "12345"

	got := InformOrderShipped(user, orderID) //에러나면 테스트할 방법없움
	want := true

	if want != got {
		t.Errorf("Want '%t', got '%t'", want, got)
	}
}