Sending and Faking Emails with Custom Templates in Golang.

victor steven
6 min readSep 20, 2020

--

I will be showing you some techniques for sending emails in Golang using different services. I will also show you how you can fake email sending when writing unit tests.

We will consider two SMTP services in this article:

  • Gmail
  • Sendgrid

You can get the complete code for this article on github here

It is very possible to use more than one service to achieve a particular purpose in an application. I will give two instances:

  • You may want all yahoo mails to be sent using Amazon SES, all hot mails to be sent using Sendgrid service, and every other mail to be sent using Gmail service.
  • You may want AWS S3 to handle image storage and DigitalOcean Spaces to store all pdf files.

This is 100% possible using the “adapter like” design pattern. This will be explained further with examples as we proceed.

We can jump into code now.

We will be seeing a case where you need to send a welcome mail to your new subscribers.

From your go projects directory, create the mail-sending directory. Then initialize go modules:

mkdir mail-sendingcd mail-sendinggo mod init mail-sending

You will need to define a .env file that will house all secret credentials

From the repository, rename the .env.example file to .env and update accordingly:

In course of this tutorial, I will explain how you can get the credentials above.

The Mail Templates

Let’s start by building the template.

From the root directory, create the templates directory, then the welcome_mail.html file

mkdir templatescd templatestouch welcome_mail.html

This is the template that will be used for the welcome mail. Normal HTML and CSS. Do observe that we passed some dynamic variables in the template. Such as Subject and Name. This will be passed from the mail package much later.

The Mailing Package

Create the emails directory from the root directory of your application, then create the welcome_mail package, and the welcome_mail.go file inside it:

mkdir emailscd emailsmkdir welcome_mailcd welcome_mailtouch welcome_mail.go

We have the SendWelcomeMail function that is exposed to the outside world.

Remember I mentioned that we will be using two email services to demonstrate.

We invoked either of the methods(sendEmailUsingGmail, sendEmailUsingSendGrid) by checking the mail type.

I have observed that SendGrid is not too suitable for yahoo mails. So we directed all yahoo mails to be sent using the Gmail service, while every other mail is sent using SendGrid, including gmails.

The sendEmailUsingGmail method:

You will have to use the authenticated details of your gmail account.

Also for development purposes, you will need to

You will also need to turn on Less secure app access from the settings(Manage your Google Account > Security > Less secure app access)

Take note that you should not do this in production. You might need to use GSuite services instead.

Going back to the method,

  • We pulled in the authenticated details from our environmental variables,
  • We authenticated with smtp
  • We got the template file we want to use(welcome_mail.html)
  • We executed the template with the data we are interested in
  • We then sent the mail using smtp.

The sendEmailUsingSendGrid method:

Signup with Sendgrid if you have already done so. Once you are in, follow this guide to create your mail API key, authenticate the Sender mail to use, and so on.

So, if you have done the above setup, going back to the method,

  • We pulled in the authenticated details from our environmental variables,
  • We got the template file we want to use(welcome_mail.html)
  • We executed the template with the data we are interested in
  • Instantiated a SendGrid's NewSendClient passing the API key.
  • We then sent the mail.

The Mail Handler

Now we have defined the mail template, the mail package, let’s now define a handler that will get the request(email, name, etc), from the user.

From the root directory, create the handlers directory, then the welcome directory and the welcome.go file inside:

mkdir handlerscd handlersmkdir welcomecd welcometouch welcome.gotouch welcome_test.go

The content of the welcome.go file:

This file above does basic HTTP stuff.

  • We got the request we include the name and email
  • We checked if it conformed to the WelcomeModel struct
  • We validated the input
  • We sent the result to the SendWelcomeMail method which we required using dependency injection.
  • We returned a favorable response if all goes well or an error otherwise.

Gin is used as the HTTP framework for this.

The validation functionalities and the structs defined can be found in the helpers/helpers.go file. From the repository.

Running the App.

Lets now put the application to a test.

Create the main.go file from the root directory.

touch main.go

You can see how we instantiated both the mail package and the handler’s constructors. Since we used dependency injection to require the mail package in the welcome package, inside the handler, we passed in the result of instantiating the mail package constructor inside the welcome package constructor:

sendWelcomeMail := welcome_mail.NewService()

welcomeMail := welcome.NewWelcome(sendWelcomeMail)

We then defined the post route and passed the WelcomeMail method from the handler.

Start the application using:

go run main.go

Before testing the post endpoint, ensure that you have updated your .env file with the correct details.

Using your favorite API tool, visit the /welcome endpoint:

Yeah!! Just received this mail:

Well, there you have it!

Faking Email Sending While Writing Test Cases

Is now time to write test cases.

We will be unit testing the handler’s WelcomeMail method. For us to do that, we must mock the SendWelcomeMail method from the Mail Package.

If you look at the mail package again, you will observe that the SendWelcomeMail package is defined in an interface. This will help us have a fake implementation of that method that returns whatever we wish.

We had already created the welcome_test.go above. Let’s now flesh it out.

From the above:

  • We defined a fake struct fakeWelcomeMailService that has the SendWelcomeMailFn which is of a function type that matches the signature of the real SendWelcomeMail method.
  • We then defined the SendWelcomeMail using the fake struct as a receiver:
func (u *fakeWelcomeMailService) SendWelcomeMail(cred *helpers.WelcomeMail) (bool, error) {
return u.SendWelcomeMailFn(cred)
}

With this, we have a fake copy of the SendWelcomeMail method. How sweet😍.

  • We then instantiated the welcome handler constructor, passing it the fake mail implementation:
var (
fakeWelcomeMail fakeWelcomeMailService

w = welcome.NewWelcome(&fakeWelcomeMail)
)

With the above, we can start unit-testing the handler method.

Very Important!!!

We didn't need to add the SendWelcomeMail in the first two tests because we will never get that line of the code. we will be stopped for wrong validation.

From the third test, We started using the fake SendWelcomeMail method and returning whatever we wish:

fakeWelcomeMail.SendWelcomeMailFn = func(cred *helpers.WelcomeMail) (bool, error) {
return true, nil
}

You can run the test using:

go test ./...

And we are done!

Get the source code from github

You can follow me on twitter for any update.

Happy Mailing.

--

--