#go #mocking #smtp
#Вперед #насмешливый #smtp
Вопрос:
У меня есть тестовая логика электронной почты для отправки электронной почты, которая работает нормально.
Теперь, чтобы протестировать это, я скопировал код макета SMTP-сервера из https://golang.org/src/net/smtp/smtp_test.go , строка 639 функция => TestSendMailWithAuth
Почтовый код, который работает :
- Используемые типы
type Email struct {
Subject string
Body string
TemplateFile string
TemplateData interface{}
}
type pluginInput struct{
SMTPServer string
Username string
Password string
From string
To string
Cc string
}
email := amp;Email{
Subject: "SendMail test",
Body: `<!DOCTYPE html>
<html lang="en">
<head>
<title>IDP Execution Summary Email</title>
</head>
<body>test email</body>
</html>`,
}
- Код, отправляющий электронную почту
func (email *Email) SendMail(pluginInput *PluginInput) error {
// Connect to the remote SMTP server.
smtpClient, err := smtp.Dial(pluginInput.SMTPServer)
if err != nil {
logger.Error(err)
return err
}
//smtpServerHost
smtpServerHost, _, err := net.SplitHostPort(pluginInput.SMTPServer)
//start tls with no certificate check
if ok, _ := smtpClient.Extension("STARTTLS"); ok {
// #nosec G402
config := amp;tls.Config{ServerName: smtpServerHost, InsecureSkipVerify: true}
if err = smtpClient.StartTLS(config); err != nil {
return err
}
}
//set smtp client auth
if ok, authMechanism := smtpClient.Extension("AUTH"); ok {
userNameWithoutDomain := strings.Split(pluginInput.Username, "@")[0]
switch authMechanism {
case ``:
err = smtpClient.Auth(nil)
case `LOGIN`:
err = smtpClient.Auth(LoginAuth(userNameWithoutDomain, pluginInput.Password))
case `CRAM-MD5`:
err = smtpClient.Auth(smtp.CRAMMD5Auth(userNameWithoutDomain, pluginInput.Password))
case `PLAIN`:
err = smtpClient.Auth(smtp.PlainAuth("", userNameWithoutDomain, pluginInput.Password, smtpServerHost))
default:
err = smtpClient.Auth(smtp.PlainAuth("", userNameWithoutDomain, pluginInput.Password, smtpServerHost))
}
if err != nil {
return err
}
}
// From
if err = smtpClient.Mail(pluginInput.Username); err != nil {
logger.Error(err)
return err
}
// To amp; Cc
toArr := strings.Split(pluginInput.To, ",")
ccArr := strings.Split(pluginInput.Cc, ",")
toArr = append(toArr, ccArr...)
for _, addr := range toArr {
if err = smtpClient.Rcpt(addr); err != nil {
return err
}
}
//body
msg := "To: " pluginInput.To "rn"
"Cc: " pluginInput.Cc "rn"
"Subject: " email.Subject "rn"
mIMEHeaders "rn" email.Body
// send Data command tp smtp server
smtpWriterCloser, err := smtpClient.Data()
if err != nil {
return err
}
_, err = fmt.Fprintf(smtpWriterCloser, msg)
if err != nil {
return err
}
if err = smtpWriterCloser.Close(); err != nil {
return err
}
//send Quit command to SMTP server
if err = smtpClient.Quit(); err != nil {
return err
}
return nil
}
- Код модульного теста для этого:
func TestEmail_SendMail(t *testing.T) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to create listener: %v", err)
}
defer l.Close()
errCh := make(chan error)
go func() {
defer close(errCh)
conn, err := l.Accept()
if err != nil {
errCh <- fmt.Errorf("Accept: %v", err)
return
}
defer conn.Close()
tc := textproto.NewConn(conn)
tc.PrintfLine("220 hello world")
msg, err := tc.ReadLine()
if err != nil {
errCh <- fmt.Errorf("ReadLine error: %v", err)
return
}
const wantMsg = "EHLO localhost"
if msg != wantMsg {
errCh <- fmt.Errorf("unexpected response %q; want %q", msg, wantMsg)
return
}
err = tc.PrintfLine("250 mx.google.com at your service")
if err != nil {
errCh <- fmt.Errorf("PrintfLine: %v", err)
return
}
}()
type fields struct {
Subject string
Body string
TemplateFile string
TemplateData interface{}
}
type args struct {
pluginInput *PluginInput
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "without tls email send testing",
fields: fields{
Subject: "SendMail test",
Body: `<!DOCTYPE html>
<html lang="en">
<head>
<title>IDP Execution Summary Email</title>
</head>
<body>test email</body>
</html>`,
},
args: args{amp;PluginInput{
SMTPServer: l.Addr().String(),
Username: "test@example.com",
To: "other@example.com",
Cc: "another@example.com",
}},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
email := amp;Email{
Subject: tt.fields.Subject,
Body: tt.fields.Body,
TemplateFile: tt.fields.TemplateFile,
TemplateData: tt.fields.TemplateData,
}
if err := email.SendMail(tt.args.pluginInput); (err != nil) != tt.wantErr {
log.Print(err)
t.Errorf("Email.SendMail() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
err = <-errCh
if err != nil {
t.Fatalf("server error: %v", err)
}
}
После запуска модульного теста выдается ошибка «EOF».
Я не очень уверен в макете сервера, потому что у него нет переключателей, где на основе запроса клиента он отправляет ответ.
Ответ №1:
Это произошло потому, что ваша реализация не знает, когда она должна закрыть активное соединение.
Взгляните на smtpmock
пакет: https://github.com/mocktools/go-smtp-mock . Этот макет-сервер уже разработан для вашего случая или даже больше 🙂
Комментарии:
1. Спасибо. Отвечая через год, теперь я сменил компанию, эта проблема осталась в старой компании. Так что прямо сейчас я не смогу его протестировать.