Introduction

The GreenMail plugin runs a mock GreenMail SMTP server inside a Grails application, so mail the application sends during development and test is captured instead of delivered.

Captured messages can be asserted on from integration tests, or browsed in a running application through the view the plugin ships at /greenmail.

License

This plugin is released under the Apache License, Version 2.0

Source code

The full source code for this plugin can be found on GitHub.

For issues, improvements or new features go to the plugin’s GitHub issues.

Usage

Installation

Add the plugin to the dependencies block of your build.gradle. GreenMail is a development and test tool, so testImplementation is normally the right configuration:

dependencies {
    testImplementation 'io.github.gpc:greenmail:7.0.2'
}

If you also want to browse captured messages while running the application with bootRun, use developmentOnly in addition to testImplementation.

The plugin exposes com.icegreen:greenmail as an api dependency, so GreenMail and the GreenMailUtil helpers are available to your tests without declaring them yourself. If you want to declare types on your MimeMessage variables, add the mail API too:

dependencies {
    testImplementation 'jakarta.mail:jakarta.mail-api:2.1.3'
}

Requirements:

  • Grails 7.0.0 or higher

  • Java 17 or higher

Previous versions

For Grails 5 use version 5.0.0 of this plugin. For Grails 3 use version 2.0.0.

Configuration

The plugin assumes a Java mail provider is installed — for instance the Grails Mail plugin — and that the provider is pointed at the port the mock GreenMail SMTP server listens on.

Options

Property Default Description

grails.plugin.greenmail.disabled

false

Set to true to disable the plugin entirely. No greenMail bean is registered, no server is started, and the /greenmail URL mappings are not installed.

grails.plugin.greenmail.ports.smtp

3025

The port the mock SMTP server listens on. The default comes from GreenMail’s own ServerSetupTest.SMTP.port.

Example configuration

Using the Grails Mail plugin, wiring the provider to GreenMail is a matter of setting grails.mail.port:

--- # Mail and GreenMail configuration
environments:
    development:
        grails:
            mail:
                port: 3025 # Use the default GreenMail port
    test:
        grails:
            plugin:
                greenmail:
                    ports:
                        smtp: 2525 # Use a custom GreenMail port
            mail:
                port: "${grails.plugin.greenmail.ports.smtp}"
    production:
        grails:
            plugin:
                greenmail:
                    disabled: true # Never run GreenMail in production
            mail: # Your real SMTP server; see the mail plugin for options
                server: smtp.example.com
                port: 25
Always disable the plugin in production. It starts a real SMTP listener and retains every message it receives in memory.

Usage in integration tests

Inject the greenMail bean into an @Integration specification and assert on the messages the application sent. Clearing the mailbox between features keeps specifications independent:

import com.icegreen.greenmail.util.GreenMailUtil
import grails.plugin.greenmail.GreenMail
import grails.plugins.mail.MailService
import grails.testing.mixin.integration.Integration
import jakarta.mail.internet.MimeMessage
import spock.lang.Specification

@Integration
class GreenmailExampleSpec extends Specification {

    MailService mailService
    GreenMail greenMail

    void cleanup() {
        greenMail.deleteAllMessages()
    }

    void 'sent mail is captured by GreenMail'() {
        when:
        mailService.sendMail {
            to 'to@example.com'
            from 'from@example.com'
            subject 'subject'
            body 'hello world'
        }

        then:
        greenMail.messagesCount == 1

        and:
        MimeMessage message = greenMail.latestMessage
        message.subject == 'subject'
        message.to == 'to@example.com'
        GreenMailUtil.getBody(message) == 'hello world'
    }
}
Put each assertion on its own line in a then: or and: block. Assertions written inside a with closure or a helper closure are not evaluated as Spock conditions and will silently pass.

waitForIncomingEmail is useful when mail is sent asynchronously:

assert greenMail.waitForIncomingEmail(5000, 1)

Usage in a running application

The plugin ships a controller and view that list the messages the application has "sent". With the application running, browse to:

http://localhost:8080/greenmail

The list shows the sent date, subject and recipients of each captured message. Follow the Show link on a row to view the raw message, including headers.

The following URLs are mapped:

URL Description

/greenmail or /greenmail/list

Lists all captured messages.

/greenmail/show/$id

Shows the raw content of a single message by its index in the list.

/greenmail/clear

Deletes all captured messages.

JSON output

Append a .json extension to list or show to get the captured messages as JSON, which is handy for scripted checks:

http://localhost:8080/greenmail/list.json
http://localhost:8080/greenmail/show/0.json

Both respond with application/json. list.json returns a list of objects carrying id, sent, subject, to and body; show/$id.json returns a single such object.

The .js extension returns the same JSON and is retained for backwards compatibility.

When grails.plugin.greenmail.disabled is true these URL mappings are not installed.

Additional methods and extensions

The greenMail bean

grails.plugin.greenmail.GreenMail extends com.icegreen.greenmail.util.GreenMail and adds a few Groovy-friendly members on top of the inherited API:

Member Description

messages

The received messages as a List<MimeMessage> rather than an array.

messagesCount

The number of received messages.

getMessage(int index)

The message at the given index.

latestMessage

The most recently received message, or null if none were received.

deleteAllMessages()

Clears the mailbox. Call this from cleanup() in specifications.

MimeMessage extension properties

The plugin registers a Groovy extension module that adds recipient properties to jakarta.mail.internet.MimeMessage, so recipients can be read without going through getRecipients(RecipientType):

Property Description

to / cc / bcc

The first TO / CC / BCC recipient as a String, or null if there is none.

tos / ccs / bccs

All TO / CC / BCC recipients as a List<String>, or an empty list if there are none.

MimeMessage.getRecipients(RecipientType) returns null when the message has no recipient of that type. The plural properties normalise that to an empty list, so reading a recipient type the message does not carry needs no guard:

MimeMessage message = greenMail.latestMessage

message.to              // 'first@example.com'
message.tos             // ['first@example.com', 'second@example.com']
message.ccs             // ['copied@example.com']
message.bcc             // null, when the message has no BCC recipients
message.bccs            // []

Release Notes

  • 7.0.2

    • Migrated to the grails-plugin-template build structure

    • Guard against starting the GreenMail server twice when the application context is refreshed

    • The .json extension on /greenmail/list and /greenmail/show/$id now returns JSON instead of falling back to the HTML view

    • /greenmail/show/$id no longer prefixes the message headers with null, and the id field of /greenmail/show/$id.json is now a number, matching /greenmail/list.json

    • The MimeMessage tos / ccs / bccs properties now return an empty list instead of null when the message has no recipient of that type, and to / cc / bcc return null instead of throwing a NullPointerException

  • 7.0.1 - Package the precompiled GSP view so the /greenmail list renders in consuming applications

  • 7.0.0 - Support for Grails 7, Jakarta Mail and GreenMail 2.x

  • 5.0.0 - Support for Grails 5

  • 2.0.0 - Support for Grails 3

  • 1.3.0 - Support for Grails 2