Skip to content

Commit

Permalink
Guide on how to test emails with Mailhog API
Browse files Browse the repository at this point in the history
  • Loading branch information
amitaibu committed Jul 29, 2023
1 parent 6935e0f commit 086cbaf
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Guide/mail.markdown
Expand Up @@ -135,7 +135,7 @@ config = do
}
```

### Local (For Debugging)
### Local SMTP with Mailhog

A convinient way to see sent mails is to use a local mail testing such as [MailHog](https://github.com/mailhog/MailHog). This service will catch all outgoing emails, and show their HTML to you - which is handy while developing.

Expand All @@ -154,7 +154,7 @@ config :: ConfigBuilder
config = do
-- other options here, then add:
option $ SMTP
{ host = "127.0.1.1"
{ host = "127.0.1.1" -- On some computers may need `127.0.0.1` instead.
, port = 1025
, credentials = Nothing
, encryption = Unencrypted
Expand Down
129 changes: 128 additions & 1 deletion Guide/testing.markdown
Expand Up @@ -10,7 +10,7 @@ This section provides some guidelines for testing your IHP applications. It is h

The following setup and tests can be viewed in the [Blog example](https://github.com/digitallyinduced/ihp-blog-example-app).

1. Add `hspec` in `default.nix`
1. Add `hspec` in `flake.nix`
```nix
haskellDeps = p: with p; [
cabal-install
Expand Down Expand Up @@ -160,6 +160,133 @@ tests = aroundAll (withIHPApp WebApplication config) do
post.body `shouldBe` "Body of post"
```

## Test outgoing emails with Mailhog

Mailhog is a tool that allows you to test outgoing emails. It runs a fake SMTP server and a web interface to view the emails. We can use its API to to test your email sending logic, and assert the contents of the emails.

Install Mailhog by adding to `flake.nix`:

```nix
# flake.nix
# ...
perSystem = { pkgs, ... }: {
ihp = {
enable = true;
projectPath = ./.;
packages = with pkgs; [
# Native dependencies, e.g. imagemagick
# Used for local development
mailhog
];
haskellPackages = p: with p; [
# Haskell dependencies go here
p.ihp
cabal-install
base
wai
text
hlint
hspec
];
};
# Start mailhog on `devenv up`.
devenv.shells.default = {
services.mailhog.enable = true;
};
};
```

Notice we've also enabled the `mailhog` service in `devenv.shells.default`. This will start Mailhog when you run `devenv up`, which is useful for local development.

Follow the instructions on how to add a Mail action, and how to configure the SMTP on the [Mail page](https://ihp.digitallyinduced.com/Guide/mail.html).

Let's see how we can test a Mail that is sent every time a `Post` is being shown.

```haskell
-- Web/Mail/Posts/PostView.hs

module Web.Mail.Posts.PostView where
import Web.View.Prelude
import IHP.MailPrelude

data PostViewMail = PostViewMail { post :: Post }

instance BuildMail PostViewMail where
subject = "Showing Post " <> post.title
where post = ?mail.post
to PostViewMail { .. } = Address { addressName = Just "Firstname Lastname", addressEmail = "fname.lname@example.com" }
from = "hi@example.com"
html PostViewMail { .. } = [hsx|
A post was just viewed
|]
```

And let's trigger this email on the `Post` show:

```haskell
-- Web/Controller/Posts.hs

action ShowPostAction { .. } = do
post <- fetch postId

-- Send mail.
sendMail $ PostViewMail post

render ShowView { .. }
```

Now we can test is the email is being sent by using the Mailhog API:

```haskell
-- Test/PostSpec.hs

module Test.PostSpec where

import IHP.Prelude
import IHP.FrameworkConfig
import IHP.Test.Mocking
import IHP.HaskellSupport
import IHP.ModelSupport
import Test.Hspec
import Config

import Generated.Types
import Web.Routes
import Web.Types
import Web.FrontController
import Network.Wai
import IHP.ControllerPrelude
import IHP.ViewPrelude hiding (query)
import Data.Text as Text
import Network.HTTP.Types.Status
import Network.HTTP.Client
import qualified Network.Wreq as Wreq
import Control.Lens ((^.))


tests :: Spec
tests = aroundAll (withIHPApp WebApplication config) do
describe "Post" do
it "should send an email on each page view" $ withContext do
-- Get random title.
title <- generateAuthenticationToken

-- Create a Post.
post <- newRecord @Post
|> set #title title
|> createRecord

response <-
callAction $ ShowPostAction post.id

-- Assert email was sent, and caught by Mailhog.
documentBody <- do
response <- Wreq.get "http://0.0.0.0:8025/api/v1/messages"
pure (response ^. Wreq.responseBody)

cs documentBody `shouldContain` ("Post " <> cs post.title)
```

## Advanced
For more details on how to structure test suites see the [Hspec manual](http://hspec.github.io/) (a Haskell testing library). You also might want to check out the cool [Hedgehog](https://hedgehog.qa/) library for automated property tests.

0 comments on commit 086cbaf

Please sign in to comment.