Mocking & Spy

type Sender interface {
	Send(from, recipient, subject, body string) error
}

func WelcomeUser(from string, sender Sender) error {
	_ = sender.Send(
		from,
		"bob",
		"bob i have news",
		"i lied i dont actually",
	)

	return nil
}

We can implement a spy, which is a struct designed to record certain values. This struct gets passed in to the interface field.

type spySender struct {
	sendCallCount int
	from          string
	recipient     string
	subject       string
	body          string
}

func (s *spySender) Send(from, recipient, subject, body string) error {
	s.sendCallCount += 1

	s.from = from
	s.recipient = recipient
	s.subject = subject
	s.body = body

	return nil
}

func TestWelcomeUser(t *testing.T) {
	// Arrange
	email := "ben@benlewisjones.com"
	spy := spySender{
		sendCallCount: 0,
		recipient:     "",
		subject:       "",
		body:          "",
	}

	// Act
	err := WelcomeUser(email, &spy)

	// Assert
	if err != nil {
		t.Errorf("An error was returned %q", err.Error())
	}

	if spy.sendCallCount != 1 {
		t.Errorf("Expected to call send once, but it was called %v times", spy.sendCallCount)
	}

	expectedRecipient := "bob"
	if spy.recipient != expectedRecipient {
		t.Errorf("Expected recipient %q, but got %q", expectedRecipient, spy.recipient)
	}

	expectedSubject := "bob i have news"
	if spy.subject != expectedSubject {
		t.Errorf("Expected subject %q, but got %q", expectedSubject, spy.subject)
	}

	expectedBody := "i lied i dont actually"
	if spy.body != expectedBody {
		t.Errorf("Expected body %q, but got %q", expectedBody, spy.body)
	}
}