Testar envio de emails em desenvolvimento é complicado: você precisa de um servidor SMTP configurado, corre o risco de enviar emails reais por acidente, os testes são lentos, há chance de disparar muitas requisições sem querer durante o desenvolvimento, e ainda existe o risco de vazar credenciais sensíveis em commits ou configurações temporárias usadas durante o desenvolvimento.
Neste tutorial, você vai criar um bean substituto para o JavaMailSender que loga os emails no console ao invés de enviá-los, sem precisar alterar nenhuma linha do restante do seu código.
Analisando a implementação do bean padrão do JavaMailSender podemos notar que qualquer bean que definirmos irá substituir o bean padrão.
@Bean
@ConditionalOnMissingBean({JavaMailSender.class})
JavaMailSenderImpl mailSender(
MailProperties properties,
bjectProvider<SslBundles> sslBundles
) {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
this.applyProperties(
properties,
sender,
(SslBundles)sslBundles.getIfAvailable()
);
return sender;
}
Isso facilita nosso trabalho, visto que é só criarmos então um bean condicional que envia o conteúdo dos emails no console, dessa forma quando o bean condicional não existir então será usado o bean padrão do JavaMailSender, que é o realmente irá enviar os emails em produção.
Agora podemos criar o nosso bean condicional:
package kaiquebt.dev.example.config;
import java.util.Arrays;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import jakarta.mail.BodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
@Configuration
public class MockMailSenderConfig {
private static final Logger logger = LoggerFactory.getLogger(MockMailSenderConfig.class);
@ConditionalOnProperty(prefix = "spring.mail", name = "mock.enabled", havingValue = "true", matchIfMissing = false)
@Bean
public JavaMailSender mockMailSender() {
return new JavaMailSenderImpl() {
@Override
public void send(SimpleMailMessage simpleMessage) {
logger.info("=== Mock Email Sent ===");
logger.info("From: {}", simpleMessage.getFrom());
logger.info("To: {}", Arrays.toString(simpleMessage.getTo()));
logger.info("Subject: {}", simpleMessage.getSubject());
logger.info("Text: {}", simpleMessage.getText());
logger.info("=======================");
}
@Override
public void send(MimeMessage mimeMessage) throws MailException {
try {
logger.info("=== Mock MIME Email Sent ===");
logger.info("From: {}", Arrays.toString(mimeMessage.getFrom()));
logger.info("To: {}", Arrays.toString(mimeMessage.getAllRecipients()));
logger.info("Subject: {}", mimeMessage.getSubject());
logger.info("--- Email Body ---");
printContent(mimeMessage.getContent(), 0);
logger.info("============================");
} catch (Exception e) {
logger.error("Error sending MIME email", e);
}
}
@Override
public void send(MimeMessage...mimeMessages) throws MailException {
for (MimeMessage msg: mimeMessages) {
send(msg);
}
}
private void printContent(Object content, int level) throws Exception {
String indent = " ".repeat(level);
if (content instanceof MimeMultipart) {
MimeMultipart multipart = (MimeMultipart) content;
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
logger.info("{}Part {} [{}]:", indent, (i + 1), bodyPart.getContentType());
printContent(bodyPart.getContent(), level + 1);
}
} else if (content instanceof String) {
logger.info("{}{}", indent, content);
} else {
logger.info("{}Non-text content: {}", indent, content.getClass().getName());
}
}
};
}
}E para ativarmos no nosso application.properties:
spring.application.name=treinomax
server.port=8080
spring.mail.username=your-email@gmail.com
spring.mail.mock.enabled=trueou caso esteja usando yaml:
spring:
application:
name: treinomax
mail:
username: your-email@gmail.com
mock:
enabled: true
server:
port: 8080Agora em qualquer momento em que seu código disparar um email, como no exemplo abaixo, o conteúdo do email será mostrado no seu console.
private void enviarEmail(String to, String subject, String htmlContent) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setFrom(fromEmail);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
mailSender.send(message);
}Um exemplo de log de uma aplicação que usou este bean, os logs foram registrados ao tentar criar uma nova conta – no momento em que é disparado um email de verificação de conta.
treinomax-backend | 2025-11-14T07:52:32.929Z INFO 90 --- [treinomax] [nio-8080-exec-2] s.t.api.auth.service.TokenService : Token de verificação de email gerado para usuário: testeemail@gmail.com
treinomax-backend | === Mock MIME Email Sent ===
treinomax-backend | From: [your-email@gmail.com]
treinomax-backend | To: [testeemail@gmail.com]
treinomax-backend | Subject: Verifique seu email - TreinoMax
treinomax-backend | --- Email Body ---
treinomax-backend | Part 1 [text/plain]:
treinomax-backend | Part 1 [text/plain]:
treinomax-backend | <!DOCTYPE html>
treinomax-backend | <html>
treinomax-backend | <head>
treinomax-backend | ...
treinomax-backend | <style>
treinomax-backend | ...
treinomax-backend | </style>
treinomax-backend | </head>
treinomax-backend | <body>
treinomax-backend | <div class="container">
treinomax-backend | <div class="header">
treinomax-backend | <h1>Bem-vindo ao TreinoMax!</h1>
treinomax-backend | </div>
treinomax-backend |
treinomax-backend | <div class="content">
treinomax-backend | <p>Olá, <strong>testeemail</strong>!</p>
treinomax-backend |
treinomax-backend | <p>Obrigado por se cadastrar no TreinoMax - sua plataforma completa para gestão de academia e acompanhamento de treinos!</p>
treinomax-backend |
treinomax-backend | <p>Para começar a usar todos os recursos da nossa plataforma, precisamos que você verifique seu endereço de email.</p>
treinomax-backend |
treinomax-backend | <p style="text-align: center;">
treinomax-backend | <a href="http://localhost:8080/verify-email?token=a2955c26-f566-4673-85ab-75b7d5e89fa6" class="button">Verificar Email</a>
treinomax-backend | </p>
treinomax-backend |
treinomax-backend | <p>Se o botão acima não funcionar, copie e cole o seguinte link no seu navegador:</p>
treinomax-backend |
treinomax-backend | <div class="code">http://localhost:8080/verify-email?token=a2955c26-f566-4673-85ab-75b7d5e89fa6</div>
treinomax-backend |
treinomax-backend | <p>Ou use o código abaixo para verificar manualmente:</p>
treinomax-backend |
treinomax-backend | <div class="code">a2955c26-f566-4673-85ab-75b7d5e89fa6</div>
treinomax-backend |
treinomax-backend | <p><strong>Atenção:</strong> Este link expirará em 24 horas.</p>
treinomax-backend |
treinomax-backend | <p>Se você não se cadastrou em nossa plataforma, por favor ignore este email.</p>
treinomax-backend | </div>
treinomax-backend |
treinomax-backend | <div class="footer">
treinomax-backend | ...
treinomax-backend | </div>
treinomax-backend | </div>
treinomax-backend | </body>
treinomax-backend | </html>
treinomax-backend | ============================
treinomax-backend | 2025-11-14T07:52:33.143Z INFO 90 --- [treinomax] [nio-8080-exec-2] s.t.api.auth.service.EmailService : Email de verificação enviado para: testeemail@gmail.comNeste exemplo, o desenvolvedor já teria à disposição facilmente o link de confirmação para caso ele queira testar a confirmação de conta, além da confirmação de que caso o JavaMailSender estivesse configurado, este email teria sido lançado.
Agora você tem uma solução simples e eficaz para testar emails durante o desenvolvimento.