Closed gadelkareem closed 1 year ago
The best I could do is
package tests
import (
"bytes"
"io"
"regexp"
"strings"
"testing"
"github.com/go-gomail/gomail"
"github.com/stretchr/testify/assert"
)
type mockSender gomail.SendFunc
func (s mockSender) Send(from string, to []string, msg io.WriterTo) error {
return s(from, to, msg)
}
type mockSendCloser struct {
mockSender
close func() error
}
func (s *mockSendCloser) Close() error {
return s.close()
}
func newMockSenderCloser() *mockSendCloser {
return &mockSendCloser{
mockSender: func(from string, to []string, msg io.WriterTo) error { return nil },
close: func() error { return nil },
}
}
func ExpectEmail(t *testing.T, wantFrom string, wantTo []string, wantSubject, wantBody string) {
s := &mockSendCloser{
mockSender: stubSend(t, wantFrom, wantTo, wantSubject, wantBody),
close: func() error {
t.Error("Close() should not be called in Send()")
return nil
},
}
// Container -> email service -> sendercloser
C.EmailService.SenderCloser = s
}
var subjRegex = regexp.MustCompile(`(?m)^Subject: (.+)\r\n`)
func stubSend(t *testing.T, wantFrom string, wantTo []string, wantSubject, wantBody string) mockSender {
return func(from string, to []string, msg io.WriterTo) error {
assert.Equal(t, from, wantFrom)
assert.ElementsMatch(t, to, wantTo)
buf := new(bytes.Buffer)
_, err := msg.WriteTo(buf)
if err != nil {
t.Fatal(err)
}
b := buf.String()
if wantSubject != "" {
subject := subjRegex.FindStringSubmatch(b)
assert.Equal(t, wantSubject, subject[1])
}
if wantBody != "" {
compareBodies(t, b, wantBody)
}
return nil
}
}
func compareBodies(t *testing.T, got, want string) {
// We cannot do a simple comparison since the ordering of headers' fields
// is random.
gotLines := strings.Split(got, "\r\n")
wantLines := strings.Split(want, "\r\n")
// We only test for too many lines, missing lines are tested after
if len(gotLines) > len(wantLines) {
t.Fatalf("Message has too many lines, \ngot %d:\n%s\nwant %d:\n%s", len(gotLines), got, len(wantLines), want)
}
isInHeader := true
headerStart := 0
for i, line := range wantLines {
if line == gotLines[i] {
if line == "" {
isInHeader = false
} else if !isInHeader && len(line) > 2 && line[:2] == "--" {
isInHeader = true
headerStart = i + 1
}
continue
}
if !isInHeader {
missingLine(t, line, got, want)
}
isMissing := true
for j := headerStart; j < len(gotLines); j++ {
if gotLines[j] == "" {
break
}
if gotLines[j] == line {
isMissing = false
break
}
}
if isMissing {
missingLine(t, line, got, want)
}
}
}
func missingLine(t *testing.T, line, got, want string) {
t.Fatalf("Missing line %q\ngot:\n%s\nwant:\n%s", line, got, want)
}
There are 2 problems: 1- I have to send the EmailService both dialer and sendercloser, to able to use dialer in prod and sendcloser in tests
func NewEmailService(d *gomail.Dialer, sc gomail.SendCloser) *EmailService {
return &EmailService{Dialer: d, SenderCloser: sc}
}
2- no way to run tests in parallel, but not sure if this is a Golang limitation since there is no testing context.
Use github.com/mocktools/go-smtp-mock. Works like a charm.
Thanks!
Can you give me an example of how to mock the smtp server and verify the message after doing a request?