smtpmock
Lightweight configurable multithreaded fake SMTP server. Mimic any SMTP behaviour for your test environment
About smtpmock
This large README is shown as a plain-text preview. Read the full README at the source
# 
[](https://goreportcard.com/report/github.com/mocktools/go-smtp-mock/v2)
[](https://codecov.io/gh/mocktools/go-smtp-mock)
[](https://circleci.com/gh/mocktools/go-smtp-mock/tree/master)
[](https://github.com/mocktools/go-smtp-mock/releases)
[](https://pkg.go.dev/github.com/mocktools/go-smtp-mock/v2)
[](https://github.com/avelino/awesome-go)
[](LICENSE.txt)
[](CODE_OF_CONDUCT.md)
`smtpmock` is lightweight configurable multithreaded fake SMTP server written in Go. It meets the minimum requirements specified by [RFC 2821](https://datatracker.ietf.org/doc/html/rfc2821) & [RFC 5321](https://datatracker.ietf.org/doc/html/rfc5321). Allows to mimic any SMTP server behavior for your test environment and even more 🚀
## Table of Contents
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Inside of Golang ecosystem](#inside-of-golang-ecosystem)
- [Configuring](#configuring)
- [Manipulation with server](#manipulation-with-server)
- [Using a custom logger](#using-a-custom-logger)
- [Inside of Ruby ecosystem](#inside-of-ruby-ecosystem)
- [Example of usage](#example-of-usage)
- [Inside of any ecosystem](#inside-of-any-ecosystem)
- [Configuring with command line arguments](#configuring-with-command-line-arguments)
- [Other options](#other-options)
- [Stopping server](#stopping-server)
- [Implemented SMTP commands](#implemented-smtp-commands)
- [Contributing](#contributing)
- [License](#license)
- [Code of Conduct](#code-of-conduct)
- [Credits](#credits)
- [Versioning](#versioning)
- [Changelog](CHANGELOG.md)
## Features
- Configurable multithreaded RFC compatible SMTP server
- Implements the minimum command set, responds to commands and adds a valid received header to messages as specified in [RFC 2821](https://datatracker.ietf.org/doc/html/rfc2821) & [RFC 5321](https://datatracker.ietf.org/doc/html/rfc5321)
- Ability to configure behavior for each SMTP command
- Comes with default settings out of the box, configure only what you need
- Ability to override previous SMTP commands
- Fail fast scenario (ability to close client session for case when command was inconsistent or failed)
- Multiple receivers (ability to configure multiple `RCPT TO` commands receiving during one session)
- Multiple message receiving (ability to configure multiple message receiving during one session)
- Mock-server activity logger
- Ability to do graceful/force shutdown of SMTP mock server
- No authentication support
- Zero runtime dependencies
- Ability to access to server messages
- Simple and intuitive DSL
- Ability to run server as binary with command line arguments
## Requirements
Golang 1.15+
## Installation
Install `smtpmock`:
```bash
go get github.com/mocktools/go-smtp-mock/v2
go install github.com/mocktools/go-smtp-mock/v2
```
Import `smtpmock` dependency into your code:
```go
package main
import smtpmock "github.com/mocktools/go-smtp-mock/v2"
```
## Usage
- [Inside of Golang ecosystem](#inside-of-golang-ecosystem)
- [Configuring](#configuring)
- [Manipulation with server](#manipulation-with-server)
- [Using a custom logger](#using-a-custom-logger)
- [Inside of Ruby ecosystem](#inside-of-ruby-ecosystem)
- [Example of usage](#example-of-usage)
- [Inside of any ecosystem](#inside-of-any-ecosystem)
- [Configuring with command line arguments](#configuring-with-command-line-arguments)
- [Other options](#other-options)
- [Stopping server](#stopping-server)
- [Implemented SMTP commands](#implemented-smtp-commands)
### Inside of Golang ecosystem
You have to create your SMTP mock server using `smtpmock.New()` and `smtpmock.ConfigurationAttr{}` to start interaction with it.
#### Configuring
`smtpmock` is SMTP server for test environment with configurable behavior. It comes with default settings out of the box. But you can override any default behavior if you need.
```go
smtpmock.ConfigurationAttr{
// Customizing server behavior
// ---------------------------------------------------------------------
// Host address where smtpmock will run, it's equal to "127.0.0.1" by default
HostAddress: "[::]",
// Port number on which the server will bind. If it not specified, it will be
// assigned dynamically after server.Start() by default
PortNumber: 2525,
// Enables/disables log to stdout. It's equal to false by default
LogToStdout: true,
// Enables/disables log server activity. It's equal to false by default
LogServerActivity: true,
// Ability to specify session timeout. It's equal to 30 seconds by default
SessionTimeout: 42,
// Ability to specify graceful shutdown timeout. It's equal to 1 second by default
ShutdownTimeout: 5,
// Customizing SMTP command handlers behavior
// ---------------------------------------------------------------------
// Ability to configure fail fast scenario. It means that server will
// close client session for case when command was inconsistent or failed.
// It's equal to false by default
IsCmdFailFast: true,
// Ability to configure multiple RCPT TO command receiving during one session.
// It means that server will handle and save all RCPT TO command request-response
// pairs until receive successful response and next SMTP command has been passed.
// Please note, by default will be saved only one, the last RCPT TO command
// request-response pair. It's equal to false by default
MultipleRcptto: true,
// Ability to configure multiple message receiving during one session. It means that server
// will create another message during current SMTP session in case when RSET
// command has been used after successful commands chain: MAIL FROM, RCPT TO, DATA.
// Please note, by default RSET command flushes current message in any case.
// It's equal to false by default
MultipleMessageReceiving: true,
// Ability to specify blacklisted HELO domains. It's equal to empty []string
BlacklistedHeloDomains: []string{"example1.com", "example2.com", "localhost"},
// Ability to specify blacklisted MAIL FROM emails. It's equal to empty []string
BlacklistedMailfromEmails: []string{"[email protected]", "[email protected]"},
// Ability to specify blacklisted RCPT TO emails. It's equal to empty []string
BlacklistedRcpttoEmails: []string{"[email protected]", "[email protected]"},
// Ability to specify not registered (non-existent) RCPT TO emails.
// It's equal to empty []string
NotRegisteredEmails: []string{"[email protected]", "[email protected]"},
// Ability to specify HELO response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayHelo: 2,
// Ability to specify MAIL FROM response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayMailfrom: 2,
// Ability to specify RCPT TO response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayRcptto: 2,
// Ability to specify DATA response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayData: 2,
// Ability to specify message response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayMessage: 2,
// Ability to specify RSET response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayRset: 2,
// Ability to specify NOOP response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayNoop: 2,
// Ability to specify QUIT response delay in seconds. It runs immediately,
// equals to 0 seconds by default
ResponseDelayQuit: 2,
// Ability to specify message body size limit. It's equal to 10485760 bytes (10MB) by default
MsgSizeLimit: 5,
// Customizing SMTP command handler messages context
// ---------------------------------------------------------------------
// Custom server greeting message. Base on defaultGreetingMsg by default
MsgGreeting: "msgGreeting",
// Custom invalid command message. Based on defaultInvalidCmdMsg by default
MsgInvalidCmd: "msgInvalidCmd",
// Custom invalid command HELO sequence message.
// Based on defaultInvalidCmdHeloSequenceMsg by default
MsgInvalidCmdHeloSequence: "msgInvalidCmdHeloSequence",
// Custom invalid command HELO argument message.
// Based on defaultInvalidCmdHeloArgMsg by default
MsgInvalidCmdHeloArg: "msgInvalidCmdHeloArg",
// Custom HELO blacklisted domain message. Based on defaultQuitMsg by default
MsgHeloBlacklistedDomain: "msgHeloBlacklistedDomain",
// Custom HELO received message. Based on defaultReceivedMsg by default
MsgHeloReceived: "msgHeloReceived",
// Custom invalid command MAIL FROM sequence message.
// Based on defaultInvalidCmdMailfromSequenceMsg by default
MsgInvalidCmdMailfromSequence: "msgInvalidCmdMailfromSequence",
// Custom invalid command MAIL FROM argument message.
// Based on defaultInvalidCmdMailfromArgMsg by default
MsgInvalidCmdMailfromArg: "msgInvalidCmdMailfromArg",
// Custom MAIL FROM blacklisted email message. Based on defaultQuitMsg by default
MsgMailfromBlacklistedEmail: "msgMailfromBlacklistedEmail",
// Custom MAIL FROM received message. Based on defaultReceivedMsg by default
MsgMailfromReceived: "msgMailfromReceived",
// Custom invalid command RCPT TO sequence message.
// Based on defaultInvalidCmdRcpttoSequenceMsg by default
MsgInvalidCmdRcpttoSequence: "msgInvalidCmdRcpttoSequence",
// Custom invalid command RCPT TO argument message.
// Based on defaultInvalidCmdRcpttoArgMsg by default
MsgInvalidCmdRcpttoArg: "msgInvalidCmdRcpttoArg",
// Custom RCPT TO not registered email message.
// Based on defaultNotRegisteredRcpttoEmailMsg by default
MsgRcpttoNotRegisteredEmail: "msgRcpttoNotRegisteredEmail",
// Custom RCPT TO blacklisted email message. Based on defaultQuitMsg by default
MsgRcpttoBlacklistedEmail: "msgRcpttoBlacklistedEmail",
// Custom RCPT TO received message. Based on defaultReceivedMsg by default
MsgRcpttoReceived: "msgRcpttoReceived",
// Custom invalid command DATA sequence message.
// Based on defaultInvalidCmdDataSequenceMsg by default
MsgInvalidCmdDataSequence: "msgInvalidCmdDataSequence",
// Custom DATA received message. Based on defaultReadyForReceiveMsg by default
MsgDataReceived: "msgDataReceived",
// Custom size is too big message. Based on defaultMsgSizeIsTooBigMsg by default
MsgMsgSizeIsTooBig: "msgMsgSizeIsTooBig",
// Custom received message body message. Based on defaultReceivedMsg by default
MsgMsgReceived: "msgMsgReceived",
// Custom invalid command RSET sequence message.
// Based on defaultInvalidCmdHeloSequenceMsg by default
MsgInvalidCmdRsetSequence: "msgInvalidCmdRsetSequence",
// Custom invalid command RSET message. Based on defaultInvalidCmdMsg by default
MsgInvalidCmdRsetArg: "msgInvalidCmdRsetArg",
// Custom RSET received message. Based on defaultOkMsg by default
MsgRsetReceived: "msgRsetReceived",
// Custom NOOP received message. Based on defaultOkMsg by default
MsgNoopReceived: "msgNoopReceived",
// Custom quit command message. Based on defaultQuitMsg by default
MsgQuitCmd: "msgQuitCmd",
}
```
#### Manipulation with server
```go
package main
import (
"fmt"
"net"
"net/smtp"
smtpmock "github.com/mocktools/go-smtp-mock/v2"
)
func main() {
// You can pass empty smtpmock.ConfigurationAttr{}. It means that smtpmock will use default settings
server := smtpmock.New(smtpmock.ConfigurationAttr{
LogToStdout: true,
LogServerActivity: true,
})
// To start server use Start() method
if err := server.Start(); err != nil {
fmt.Println(err)
}
// Server's port will be assigned dynamically after server.Start()
// for case when portNumber wasn't specified
hostAddress, portNumber := "127.0.0.1", server.PortNumber()
// Possible SMTP-client stuff for iteration with mock server
address := fmt.Sprintf("%s:%d", hostAddress, portNumber)
timeout := time.Duration(2) * time.Second
connection, _ := net.DialTimeout("tcp", address, timeout)
client, _ := smtp.NewClient(connection, hostAddress)
client.Hello("example.com")
client.Quit()
client.Close()
// Each result of SMTP session will be saved as message.
// To get access for server messages copies use Messages() method
server.Messages()
// To get access for server messages copies and purge it on server after
// use MessagesAndPurge() method
server.MessagesAndPurge()
// In case with flaky test environment you can wait for the specified number
// of messages to arrive or until timeout is reached use WaitForMessages() method
server.WaitForMessages(42, 1 * time.Millisecond)
// In case with flaky test environment you can wait for the specified number
// of messages to arrive or until timeout is reached and purge it on server
// after use WaitForMessagesAndPurge() method
server.WaitForMessagesAndPurge(42, 1 * time.Millisecond)
// To stop the server use Stop() method. Please note, smtpmock uses graceful shutdown.
// It means that smtpmock will end all sessions after client responses or by session
// timeouts immediately.
if err := server.Stop(); err != nil {
fmt.Println(err)
}
}
```
Code from example above will produce the following output to the configured logger:
```code
INFO: 2021/11/30 22:07:30.554827 SMTP mock server started on port: 2525
INFO: 2021/11/30 22:07:30.554961 SMTP session started
INFO: 2021/11/30 22:07:30.554998 SMTP response: 220 Welcome
INFO: 2021/11/30 22:07:30.555059 SMTP request: EHLO example.com
INFO: 2021/11/30 22:07:30.555648 SMTP response: 250 Received
INFO: 2021/11/30 22:07:30.555686 SMTP request: QUIT
INFO: 2021/11/30 22:07:30.555722 SMTP response: 221 Closing connection
INFO: 2021/11/30 22:07:30.555732 SMTP session finished
WARNING: 2021/11/30 22:07:30.555801 SMTP mock server is in the shutdown mode and won't accept new connections
INFO: 2021/11/30 22:07:30.555808 SMTP mock server was stopped successfully
```
#### Using a custom logger
```go
package main
import (
"bytes"
"fmt"
"net"
"net/smtp"
smtpmock "github.com/mocktools/go-smtp-mock/v2"
)
// User-defined loggers can be defined by implementing the Logger interface.
// For example, this custom logger writes to byte buffers instead of os.Stdout/Stderr.
type customLogger struct {
out *bytes.Buffer
err *bytes.Buffer
}
func (logger *customLogger) InfoActivity(message string) {
logger.out.WriteString(message)
}
func (logger *customLogger) Info(message string) {
logger.out.WriteString(message)
}
func (logger *customLogger) Warning(message string) {
logger.out.WriteString(message)
}
func (logger *customLogger) Error(message string) {
logger.err.WriteString(message)
}
func main() {
// You can pass empty smtpmock.ConfigurationAttr{}. It means that smtpmock will use default settings
server := smtpmock.New(smtpmock.ConfigurationAttr{
LogToStdout: true,
LogServerActivity: true,
})
// The default logger for the server can be substituted with a custom logging implementation.
// This can be useful in tests where the server logs need to be programatically examined.
outBuf := &bytes.Buffer{}
errBuf := &bytes.Buffer{}
server.WithLogger(&customLogger{
out: outBuf,
err: errBuf,
})
// To start server use Start() method
if err := server.Start(); err != nil {
fmt.Println(err)
}
// To stop the server use Stop() method. Please note, smtpmock uses graceful shutdown.
// It means that smtpmock will end all sessions after client responses or by session
// timeouts immediately.
if err := server.Stop(); err != nil {
fmt.Println(err)
}
}
```
### Inside of Ruby ecosystem
In Ruby ecosystem `smtpmock` is available as [`smtp_mock`](https://github.com/mocktools/ruby-smtp-mock) gem. It's flexible Ruby wrapper over `smtpmock` binary.
#### Example of usage
First, you should install `smtp_mock` gem and `smtpmock` as system dependency:
```bash
gem install smtp_mock
bundle exec smtp_mock -i ~
```
Now, you can create and interact with your `smtpmock` instance natively from Ruby ecosystem. It comes with default settings out of the box. Configure only what you need, for example:
```ruby
require 'smtp_mock'
# List of all available server options:
# https://github.com/mocktools/ruby-smtp-mock#available-server-options
smtp_mock_server = SmtpMock.start_server(not_registered_emails: %w[[email protected] [email protected]])
# returns current smtp mock server port
smtp_mock_server.port # => 55640
# interface for force shutdown current smtp mock server
smtp_mock_server.stop! # => true
```
### Inside of any ecosystem
You can use `smtpmock` as binary. Just download the pre-compiled binary from the [releases page](https://github.com/mocktools/go-smtp-mock/releases) and copy them to the desired location. For start server run command with needed arguments. You can use our bash script for automation this process like in the example below:
```bash
curl -sL https://raw.githubusercontent.com/mocktools/go-smtp-mock/master/script/download.sh | bash
./smtpmock -port=2525 -log
```
#### Configuring with command line arguments
`smtpmock` configuration is available as command line arguments specified in the list below:
| Flag description | Example of usage |
| --- | --- |
| `-host` - host address where smtpmock will run. It's equal to `127.0.0.1` by default | `-host=localhost` |
| `-port` - server port number. If not specified it will be assigned dynamically | `-port=2525` |
| `-log` - enables log server activity. Disabled by default | `-log` |
| `-sessionTimeout` - session timeout in seconds. It's equal to 30 seconds by default | `-sessionTimeout=60` |
| `-shutdownTimeout` - graceful shutdown timeout in seconds. It's equal to 1 second by default | `-shutdownTimeout=5` |
| `-failFast` - enables fail fast scenario. Disabled by default | `-failFast` |
| `-multipleRcptto` - enables multiple `RCPT TO` receiving scenario. Disabled by default | `-multipleRcptto` |
| `-multipleMessageReceiving` - enables multiple message receiving scenario. Disabled by default | `-multipleMessageReceiving` |
| `-blacklistedHeloDomains` - blacklisted `HELO` domains, separated by commas | `-blacklistedHeloDomains="example1.com,example2.com"` |
| `-blacklistedMailfromEmails` - blacklisted `MAIL FROM` emails, separated by commas | `-blacklistedMailfromEmails="[email protected],[email protected]"` |Frequently Asked Questions
What is smtpmock?
smtpmock is a Email library for the Go programming language. Lightweight configurable multithreaded fake SMTP server. Mimic any SMTP behaviour for your test environment
How do I install smtpmock?
Install smtpmock with the Go module system using `go get mocktools/go-smtp-mock`. Check the repository for the current installation instructions.
What category does smtpmock belong to?
smtpmock is listed under Email, specifically Email.