rails-mailers
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEmail with ActionMailer
使用ActionMailer发送邮件
Send transactional and notification emails using ActionMailer, integrated with SolidQueue for async delivery. Create HTML and text templates, preview emails in development, and test thoroughly.
<when-to-use>
- Sending transactional emails (password resets, confirmations, receipts)
- Sending notification emails (updates, alerts, digests)
- Delivering emails asynchronously via background jobs
- Creating email templates with HTML and text versions
- Testing email delivery and content
</when-to-use>
<benefits>
- **Async Delivery** - ActionMailer integrates with SolidQueue for non-blocking email sending
- **Template Support** - ERB templates for HTML and text email versions
- **Preview in Development** - See emails without sending via /rails/mailers
- **Testing Support** - Full test suite for delivery and content
- **Layouts** - Shared layouts for consistent email branding
- **Attachments** - Send files (PDFs, images) with emails
</benefits>
<verification-checklist>
Before completing mailer work:
- ✅ Async delivery used (deliver_later, not deliver_now)
- ✅ Both HTML and text templates provided
- ✅ URL helpers used (not path helpers)
- ✅ Email previews created for development
- ✅ Mailer tests passing (delivery and content)
- ✅ SolidQueue configured for background delivery
</verification-checklist>
<standards>
- ALWAYS deliver emails asynchronously with deliver_later (NOT deliver_now)
- Provide both HTML and text email templates
- Use *_url helpers (NOT *_path) for links in emails
- Set default 'from' address in ApplicationMailer
- Create email previews for development (/rails/mailers)
- Configure default_url_options for each environment
- Use inline CSS for email styling (email clients strip external styles)
- Test email delivery and content
- Use parameterized mailers (.with()) for cleaner syntax
</standards>
使用ActionMailer发送事务性和通知类邮件,集成SolidQueue实现异步投递。创建HTML和文本模板,在开发环境中预览邮件,并进行全面测试。
<when-to-use>
- 发送事务性邮件(密码重置、确认通知、收据)
- 发送通知类邮件(更新、告警、摘要)
- 通过后台任务异步投递邮件
- 创建HTML和文本版本的邮件模板
- 测试邮件投递和内容
</when-to-use>
<benefits>
- **异步投递** - ActionMailer集成SolidQueue实现非阻塞邮件发送
- **模板支持** - 支持HTML和文本邮件版本的ERB模板
- **开发环境预览** - 通过/rails/mailer路径无需发送即可查看邮件
- **测试支持** - 完整的投递和内容测试套件
- **布局** - 共享布局实现统一的邮件品牌风格
- **附件** - 支持随邮件发送文件(PDF、图片)
</benefits>
<verification-checklist>
完成邮件开发工作前:
- ✅ 使用异步投递(deliver_later,而非deliver_now)
- ✅ 同时提供HTML和文本模板
- ✅ 使用URL助手(而非路径助手)
- ✅ 为开发环境创建邮件预览
- ✅ 邮件测试通过(投递和内容)
- ✅ 配置SolidQueue实现后台投递
</verification-checklist>
<standards>
- 始终使用deliver_later异步投递邮件(不要使用deliver_now)
- 同时提供HTML和文本邮件模板
- 邮件中的链接使用*_url助手(不要使用*_path)
- 在ApplicationMailer中设置默认发件人地址
- 为开发环境创建邮件预览(/rails/mailers)
- 为每个环境配置default_url_options
- 使用内联CSS设置邮件样式(邮件客户端会剥离外部样式)
- 测试邮件投递和内容
- 使用参数化邮件器(.with())获得更简洁的语法
</standards>
ActionMailer Setup
ActionMailer配置
<pattern name="actionmailer-basic-setup">
<description>Configure ActionMailer for email delivery</description>
Mailer Class:
ruby
undefined<pattern name="actionmailer-basic-setup">
<description>配置ActionMailer用于邮件投递</description>
**邮件类:
ruby
undefinedapp/mailers/application_mailer.rb
app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "noreply@example.com"
layout "mailer"
end
class ApplicationMailer < ActionMailer::Base
default from: "noreply@example.com"
layout "mailer"
end
app/mailers/notification_mailer.rb
app/mailers/notification_mailer.rb
class NotificationMailer < ApplicationMailer
def welcome_email(user)
@user = user
@login_url = login_url
mail(to: user.email, subject: "Welcome to Our App")
end
def password_reset(user)
@user = user
@reset_url = password_reset_url(user.reset_token)
mail(to: user.email, subject: "Password Reset Instructions")
end
end
**HTML Template:**
```erb
<%# app/views/notification_mailer/welcome_email.html.erb %>
<h1>Welcome, <%= @user.name %>!</h1>
<p>Thanks for signing up. Get started by logging in:</p>
<%= link_to "Login Now", @login_url, class: "button" %>Text Template:
erb
<%# app/views/notification_mailer/welcome_email.text.erb %>
Welcome, <%= @user.name %>!
Thanks for signing up. Get started by logging in:
<%= @login_url %>Usage (Async with SolidQueue):
ruby
undefinedclass NotificationMailer < ApplicationMailer
def welcome_email(user)
@user = user
@login_url = login_url
mail(to: user.email, subject: "Welcome to Our App")
end
def password_reset(user)
@user = user
@reset_url = password_reset_url(user.reset_token)
mail(to: user.email, subject: "Password Reset Instructions")
end
end
**HTML模板:**
```erb
<%# app/views/notification_mailer/welcome_email.html.erb %>
<h1>Welcome, <%= @user.name %>!</h1>
<p>Thanks for signing up. Get started by logging in:</p>
<%= link_to "Login Now", @login_url, class: "button" %>文本模板:
erb
<%# app/views/notification_mailer/welcome_email.text.erb %>
Welcome, <%= @user.name %>!
Thanks for signing up. Get started by logging in:
<%= @login_url %>使用方式(通过SolidQueue异步):
ruby
undefinedIn controller or service
在控制器或者服务中
NotificationMailer.welcome_email(@user).deliver_later
NotificationMailer.password_reset(@user).deliver_later(queue: :mailers)
**Why:** ActionMailer integrates seamlessly with SolidQueue for async delivery. Always use deliver_later to avoid blocking requests. Provide both HTML and text versions for compatibility.
</pattern>
<antipattern>
<description>Using deliver_now in production (blocks HTTP request)</description>
<bad-example>
```rubyNotificationMailer.welcome_email(@user).deliver_later
NotificationMailer.password_reset(@user).deliver_later(queue: :mailers)
**原因:** ActionMailer可以无缝集成SolidQueue实现异步投递。始终使用deliver_later避免阻塞请求。同时提供HTML和文本版本保证兼容性。
</pattern>
<antipattern>
<description>在生产环境使用deliver_now(会阻塞HTTP请求)</description>
<bad-example>
```ruby❌ WRONG - Blocks HTTP request thread
❌ 错误 - 会阻塞HTTP请求线程
def create
@user = User.create!(user_params)
NotificationMailer.welcome_email(@user).deliver_now # Blocks!
redirect_to @user
end
</bad-example>
<good-example>
```rubydef create
@user = User.create!(user_params)
NotificationMailer.welcome_email(@user).deliver_now # 阻塞!
redirect_to @user
end
</bad-example>
<good-example>
```ruby✅ CORRECT - Async delivery via SolidQueue
✅ 正确 - 通过SolidQueue异步投递
def create
@user = User.create!(user_params)
NotificationMailer.welcome_email(@user).deliver_later # Non-blocking
redirect_to @user
end
</good-example>
**Why bad:** deliver_now blocks the HTTP request until SMTP completes, creating slow response times and poor user experience. deliver_later uses SolidQueue to send email in background.
</antipattern>
<pattern name="parameterized-mailers">
<description>Use .with() to pass parameters cleanly to mailers</description>
```ruby
class NotificationMailer < ApplicationMailer
def custom_notification
@user = params[:user]
@message = params[:message]
mail(to: @user.email, subject: params[:subject])
end
enddef create
@user = User.create!(user_params)
NotificationMailer.welcome_email(@user).deliver_later # 非阻塞
redirect_to @user
end
</good-example>
**错误原因:** deliver_now会阻塞HTTP请求直到SMTP执行完成,导致响应时间变长,用户体验差。deliver_later使用SolidQueue在后台发送邮件。
</antipattern>
<pattern name="parameterized-mailers">
<description>使用.with()向邮件器清晰传递参数</description>
```ruby
class NotificationMailer < ApplicationMailer
def custom_notification
@user = params[:user]
@message = params[:message]
mail(to: @user.email, subject: params[:subject])
end
endUsage
使用方式
NotificationMailer.with(
user: user,
message: "Update available",
subject: "System Alert"
).custom_notification.deliver_later
**Why:** Cleaner syntax, easier to read and modify, and works seamlessly with background jobs.
</pattern>
---NotificationMailer.with(
user: user,
message: "Update available",
subject: "System Alert"
).custom_notification.deliver_later
**原因:** 语法更简洁,更容易阅读和修改,并且可以和后台任务无缝兼容。
</pattern>
---Email Templates
邮件模板
<pattern name="email-layouts">
<description>Shared layouts for consistent email branding</description>
HTML Layout:
erb
<%# app/views/layouts/mailer.html.erb %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
color: #333;
}
.header {
background-color: #4F46E5;
color: white;
padding: 20px;
text-align: center;
}
.content {
padding: 20px;
}
.button {
display: inline-block;
padding: 12px 24px;
background-color: #4F46E5;
color: white;
text-decoration: none;
border-radius: 4px;
}
.footer {
padding: 20px;
text-align: center;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div class="header">
<h1>Your App</h1>
</div>
<div class="content">
<%= yield %>
</div>
<div class="footer">
<p>© 2025 Your Company. All rights reserved.</p>
</div>
</body>
</html>Text Layout:
erb
<%# app/views/layouts/mailer.text.erb %>
================================================================================
YOUR APP
================================================================================
<%= yield %>
--------------------------------------------------------------------------------
© 2025 Your Company. All rights reserved.Why: Consistent branding across all emails. Inline CSS ensures styling works across email clients.
</pattern>
<pattern name="email-attachments">
<description>Attach files to emails (PDFs, CSVs, images)</description>
ruby
class ReportMailer < ApplicationMailer
def monthly_report(user, data)
@user = user
# Regular attachment
attachments["report.pdf"] = {
mime_type: "application/pdf",
content: generate_pdf(data)
}
# Inline attachment (for embedding in email body)
attachments.inline["logo.png"] = File.read(
Rails.root.join("app/assets/images/logo.png")
)
mail(to: user.email, subject: "Monthly Report")
end
endIn template:
erb
<%# Reference inline attachment %>
<%= image_tag attachments["logo.png"].url %>Why: Attach reports, exports, or inline images. Inline attachments can be referenced in email body with image_tag.
</pattern>
<antipattern>
<description>Using *_path helpers instead of *_url in emails (broken links)</description>
<bad-example>
ruby
undefined<pattern name="email-layouts">
<description>共享布局实现统一的邮件品牌风格</description>
HTML布局:
erb
<%# app/views/layouts/mailer.html.erb %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
color: #333;
}
.header {
background-color: #4F46E5;
color: white;
padding: 20px;
}
.content {
padding: 20px;
}
.button {
display: inline-block;
padding: 12px 24px;
background-color: #4F46E5;
color: white;
text-decoration: none;
border-radius: 4px;
}
.footer {
padding: 20px;
text-align: center;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div class="header">
<h1>Your App</h1>
</div>
<div class="content">
<%= yield %>
</div>
<div class="footer">
<p>© 2025 Your Company. All rights reserved.</p>
</div>
</body>
</html>文本布局:
erb
<%# app/views/layouts/mailer.text.erb %>
================================================================================
YOUR APP
================================================================================
<%= yield %>
--------------------------------------------------------------------------------
© 2025 Your Company. All rights reserved.原因: 所有邮件保持一致的品牌风格。内联CSS确保样式在所有邮件客户端都能正常显示。
</pattern>
<pattern name="email-attachments">
<description>在邮件中附加文件(PDF、CSV、图片)</description>
ruby
class ReportMailer < ApplicationMailer
def monthly_report(user, data)
@user = user
# 普通附件
attachments["report.pdf"] = {
mime_type: "application/pdf",
content: generate_pdf(data)
}
# 内联附件(用于嵌入邮件正文)
attachments.inline["logo.png"] = File.read(
Rails.root.join("app/assets/images/logo.png")
)
mail(to: user.email, subject: "Monthly Report")
end
end**在模板中:
erb
<%# 引用内联附件 %>
<%= image_tag attachments["logo.png"].url %>原因: 附加报告、导出文件或者内联图片。内联附件可以通过image_tag在邮件正文中引用。
</pattern>
<antipattern>
<description>在邮件中使用*_path助手而非*_url(链接失效)</description>
<bad-example>
ruby
undefined❌ WRONG - Relative path doesn't work in emails
❌ 错误 - 相对路径在邮件中无法使用
def welcome_email(user)
@user = user
@login_url = login_path # => "/login" (relative path)
mail(to: user.email, subject: "Welcome")
end
</bad-example>
<good-example>
```rubydef welcome_email(user)
@user = user
@login_url = login_path # => "/login" (相对路径)
mail(to: user.email, subject: "Welcome")
end
</bad-example>
<good-example>
```ruby✅ CORRECT - Full URL works in emails
✅ 正确 - 完整URL在邮件中可以正常使用
def welcome_email(user)
@user = user
@login_url = login_url # => "https://example.com/login" (absolute URL)
mail(to: user.email, subject: "Welcome")
end
def welcome_email(user)
@user = user
@login_url = login_url # => "https://example.com/login" (绝对URL)
mail(to: user.email, subject: "Welcome")
end
Required configuration
必需配置
config/environments/production.rb
config/environments/production.rb
config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
</good-example>
**Why bad:** Emails are viewed outside your application context, so relative paths don't work. Always use *_url helpers to generate absolute URLs.
</antipattern>
---config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
</good-example>
**错误原因:** 邮件是在你的应用上下文之外查看的,所以相对路径无法工作。始终使用*_url助手生成绝对URL。
</antipattern>
---Email Testing
邮件测试
<pattern name="letter-opener-setup">
<description>Preview emails in browser during development without sending</description>
Configuration:
ruby
undefined<pattern name="letter-opener-setup">
<description>开发过程中在浏览器中预览邮件,无需实际发送</description>
**配置:
ruby
undefinedGemfile
Gemfile
group :development do
gem "letter_opener"
end
group :development do
gem "letter_opener"
end
config/environments/development.rb
config/environments/development.rb
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
config/environments/production.rb
config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.sendgrid.net",
port: 587,
user_name: Rails.application.credentials.dig(:smtp, :username),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true
}
config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
**Why:** letter_opener opens emails in browser during development - no SMTP setup needed. Test email appearance without actually sending.
</pattern>
<pattern name="mailer-previews">
<description>Preview all email variations at /rails/mailers</description>
```rubyconfig.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.sendgrid.net",
port: 587,
user_name: Rails.application.credentials.dig(:smtp, :username),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true
}
config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
**原因:** 开发过程中letter_opener会在浏览器中打开邮件 - 无需配置SMTP。无需实际发送即可测试邮件外观。
</pattern>
<pattern name="mailer-previews">
<description>在/rails/mailers路径预览所有邮件变体</description>
```rubytest/mailers/previews/notification_mailer_preview.rb
test/mailers/previews/notification_mailer_preview.rb
class NotificationMailerPreview < ActionMailer::Preview
Preview at http://localhost:3000/rails/mailers/notification_mailer/welcome_email
def welcome_email
user = User.first || User.new(name: "Test User", email: "test@example.com")
NotificationMailer.welcome_email(user)
end
def password_reset
user = User.first || User.new(name: "Test User", email: "test@example.com")
user.reset_token = "sample_token_123"
NotificationMailer.password_reset(user)
end
Preview with different data
def welcome_email_long_name
user = User.new(name: "Christopher Alexander Montgomery III", email: "long@example.com")
NotificationMailer.welcome_email(user)
end
end
**Why:** Mailer previews at /rails/mailers let you see all email variations without sending. Test different edge cases (long names, missing data, etc.).
</pattern>
<pattern name="mailer-testing">
<description>Test email delivery and content with ActionMailer::TestCase</description>
```rubyclass NotificationMailerPreview < ActionMailer::Preview
预览地址:http://localhost:3000/rails/mailers/notification_mailer/welcome_email
def welcome_email
user = User.first || User.new(name: "Test User", email: "test@example.com")
NotificationMailer.welcome_email(user)
end
def password_reset
user = User.first || User.new(name: "Test User", email: "test@example.com")
user.reset_token = "sample_token_123"
NotificationMailer.password_reset(user)
end
使用不同数据预览
def welcome_email_long_name
user = User.new(name: "Christopher Alexander Montgomery III", email: "long@example.com")
NotificationMailer.welcome_email(user)
end
end
**原因:** /rails/mailers路径下的邮件预览让你无需发送即可查看所有邮件变体。测试不同的边缘场景(长名字、缺失数据等)。
</pattern>
<pattern name="mailer-testing">
<description>使用ActionMailer::TestCase测试邮件投递和内容</description>
```rubytest/mailers/notification_mailer_test.rb
test/mailers/notification_mailer_test.rb
class NotificationMailerTest < ActionMailer::TestCase
test "welcome_email sends with correct attributes" do
user = users(:alice)
email = NotificationMailer.welcome_email(user)
# Test delivery
assert_emails 1 do
email.deliver_now
end
# Test attributes
assert_equal [user.email], email.to
assert_equal ["noreply@example.com"], email.from
assert_equal "Welcome to Our App", email.subject
# Test content
assert_includes email.html_part.body.to_s, user.name
assert_includes email.text_part.body.to_s, user.name
assert_includes email.html_part.body.to_s, "Login Now"end
test "delivers via background job" do
user = users(:alice)
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
NotificationMailer.welcome_email(user).deliver_later(queue: :mailers)
endend
test "password_reset includes reset link" do
user = users(:alice)
user.update!(reset_token: "test_token_123")
email = NotificationMailer.password_reset(user)
assert_includes email.html_part.body.to_s, "test_token_123"
assert_includes email.html_part.body.to_s, "password_reset"end
end
**Why:** Test email delivery, content, and background job enqueuing. Verify recipients, subjects, and that emails are queued properly.
</pattern>
---class NotificationMailerTest < ActionMailer::TestCase
test "welcome_email sends with correct attributes" do
user = users(:alice)
email = NotificationMailer.welcome_email(user)
# 测试投递
assert_emails 1 do
email.deliver_now
end
# 测试属性
assert_equal [user.email], email.to
assert_equal ["noreply@example.com"], email.from
assert_equal "Welcome to Our App", email.subject
# 测试内容
assert_includes email.html_part.body.to_s, user.name
assert_includes email.text_part.body.to_s, user.name
assert_includes email.html_part.body.to_s, "Login Now"end
test "delivers via background job" do
user = users(:alice)
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
NotificationMailer.welcome_email(user).deliver_later(queue: :mailers)
endend
test "password_reset includes reset link" do
user = users(:alice)
user.update!(reset_token: "test_token_123")
email = NotificationMailer.password_reset(user)
assert_includes email.html_part.body.to_s, "test_token_123"
assert_includes email.html_part.body.to_s, "password_reset"end
end
**原因:** 测试邮件投递、内容和后台任务入队。验证收件人、主题,以及邮件是否正确入队。
</pattern>
---Email Configuration
邮件配置
<pattern name="environment-configuration">
<description>Configure ActionMailer for each environment</description>
Development:
ruby
undefined<pattern name="environment-configuration">
<description>为每个环境配置ActionMailer</description>
**开发环境:
ruby
undefinedconfig/environments/development.rb
config/environments/development.rb
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
**Test:**
```rubyconfig.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
**测试环境:
```rubyconfig/environments/test.rb
config/environments/test.rb
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: "example.com" }
**Production:**
```rubyconfig.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: "example.com" }
**生产环境:**
```rubyconfig/environments/production.rb
config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
config.action_mailer.default_url_options = {
host: ENV["APP_HOST"],
protocol: "https"
}
config.action_mailer.smtp_settings = {
address: ENV["SMTP_ADDRESS"],
port: ENV["SMTP_PORT"],
user_name: Rails.application.credentials.dig(:smtp, :username),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true
}
**Why:** Different configurations per environment. Development previews in browser, test stores emails in memory, production sends via SMTP.
</pattern>
---
<testing>
```rubyconfig.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
config.action_mailer.default_url_options = {
host: ENV["APP_HOST"],
protocol: "https"
}
config.action_mailer.smtp_settings = {
address: ENV["SMTP_ADDRESS"],
port: ENV["SMTP_PORT"],
user_name: Rails.application.credentials.dig(:smtp, :username),
password: Rails.application.credentials.dig(:smtp, :password),
authentication: :plain,
enable_starttls_auto: true
}
**原因:** 每个环境使用不同的配置。开发环境在浏览器中预览,测试环境将邮件存储在内存中,生产环境通过SMTP发送。
</pattern>
---
<testing>
```rubytest/mailers/notification_mailer_test.rb
test/mailers/notification_mailer_test.rb
class NotificationMailerTest < ActionMailer::TestCase
setup do
@user = users(:alice)
end
test "welcome_email" do
email = NotificationMailer.welcome_email(@user)
assert_emails 1 { email.deliver_now }
assert_equal [@user.email], email.to
assert_equal ["noreply@example.com"], email.from
assert_match @user.name, email.html_part.body.to_s
assert_match @user.name, email.text_part.body.to_send
test "enqueues for async delivery" do
assert_enqueued_with(job: ActionMailer::MailDeliveryJob) do
NotificationMailer.welcome_email(@user).deliver_later
end
end
test "uses correct queue" do
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
NotificationMailer.welcome_email(@user).deliver_later(queue: :mailers)
end
end
end
class NotificationMailerTest < ActionMailer::TestCase
setup do
@user = users(:alice)
end
test "welcome_email" do
email = NotificationMailer.welcome_email(@user)
assert_emails 1 { email.deliver_now }
assert_equal [@user.email], email.to
assert_equal ["noreply@example.com"], email.from
assert_match @user.name, email.html_part.body.to_s
assert_match @user.name, email.text_part.body.to_send
test "enqueues for async delivery" do
assert_enqueued_with(job: ActionMailer::MailDeliveryJob) do
NotificationMailer.welcome_email(@user).deliver_later
end
end
test "uses correct queue" do
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
NotificationMailer.welcome_email(@user).deliver_later(queue: :mailers)
end
end
end
test/system/email_delivery_test.rb
test/system/email_delivery_test.rb
class EmailDeliveryTest < ApplicationSystemTestCase
test "sends welcome email after signup" do
visit signup_path
fill_in "Email", with: "new@example.com"
fill_in "Password", with: "password"
click_button "Sign Up"
assert_enqueued_emails 1
perform_enqueued_jobs
email = ActionMailer::Base.deliveries.last
assert_equal ["new@example.com"], email.to
assert_match "Welcome", email.subjectend
end
</testing>
---
<related-skills>
- rails-ai:jobs - Background job processing with SolidQueue
- rails-ai:views - Email templates and layouts
- rails-ai:testing - Testing email delivery
- rails-ai:project-setup - Environment-specific email configuration
</related-skills>
<resources>
**Official Documentation:**
- [Rails Guides - Action Mailer Basics](https://guides.rubyonrails.org/action_mailer_basics.html)
**Gems & Libraries:**
- [letter_opener](https://github.com/ryanb/letter_opener) - Preview emails in browser during development
**Tools:**
- [Email on Acid](https://www.emailonacid.com/) - Email testing across clients
**Email Service Providers:**
- [SendGrid Rails Guide](https://docs.sendgrid.com/for-developers/sending-email/rubyonrails)
</resources>class EmailDeliveryTest < ApplicationSystemTestCase
test "sends welcome email after signup" do
visit signup_path
fill_in "Email", with: "new@example.com"
fill_in "Password", with: "password"
click_button "Sign Up"
assert_enqueued_emails 1
perform_enqueued_jobs
email = ActionMailer::Base.deliveries.last
assert_equal ["new@example.com"], email.to
assert_match "Welcome", email.subjectend
end
</testing>
---
<related-skills>
- rails-ai:jobs - 基于SolidQueue的后台任务处理
- rails-ai:views - 邮件模板和布局
- rails-ai:testing - 测试邮件投递
- rails-ai:project-setup - 特定环境的邮件配置
</related-skills>
<resources>
**官方文档:
- [Rails Guides - Action Mailer Basics](https://guides.rubyonrails.org/action_mailer_basics.html)
**Gem和库:**
- [letter_opener](https://github.com/ryanb/letter_opener) - 开发过程中在浏览器中预览邮件
**工具:**
- [Email on Acid](https://www.emailonacid.com/) - 跨客户端邮件测试
**邮件服务提供商:**
- [SendGrid Rails Guide](https://docs.sendgrid.com/for-developers/sending-email/rubyonrails)
</resources>