# ActionMailer, unit testing and multipart mails

**URL:** <https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605>\
**Category:** Rails\
**Created:** [December 2, 2008, 3:33am UTC](https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605 "2008-12-02T03:33:02Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![trejkaz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/8797f3/32.png) [@trejkaz](https://www.ruby-forum.com/u/trejkaz)\
**Post date:** [December 2, 2008, 3:33am UTC](https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605/1 "2008-12-02T03:33:02Z")

</div>

Hi all.

Is there a “correct” way of writing a unit test for a mailer which  
sends attachments?

I tried using the @expected variable as provided in  
ActionMailer::TestCase, but it led to various problems.

Here’s what I’m attempting…

def test\_notification  
@expected.from = ‘’  
@expected.to = ‘’  
@expected.subject = ‘’  
@expected.content\_type = ‘multipart/mixed; boundary=“something”’

```
body_part = TMail::Mail.new
body_part.content_type = 'text/plain'
body_part.body = read_fixture('notification')
@expected.parts << body_part # <=== ERROR HERE

attach_part = TMail::Mail.new
attach_part.content_type = 'application/octet-stream'
attach_part.encoding = 'base64'
attach_part.body = 'abc'
@expected.parts << attach_part

mail = Notifier.create_notification()

assert_equal @expected.encoded, mail.encoded

```

end

This gives the following error:

TypeError: can’t convert nil into String  
C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/action\_mailer/  
vendor/tmail-1.2.3/tmail/mail.rb:551:in `quote' C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/ action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:551:in`read\_multipart’  
C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/  
action\_mailer/vendor/tmail-1.2.3/tmail/mail.rb:540:in `parse_body_0' C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/ action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:526:in`parse\_body’  
C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/  
action\_mailer/vendor/tmail-1.2.3/tmail/stringio.rb:43:in `open' C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/ action_mailer/vendor/tmail-1.2.3/tmail/port.rb:340:in`ropen’  
C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/  
action\_mailer/vendor/tmail-1.2.3/tmail/mail.rb:524:in `parse_body' C:/ruby/lib/ruby/gems/1.8/gems/actionmailer-2.1.2/lib/ action_mailer/vendor/tmail-1.2.3/tmail/mail.rb:497:in`parts’  
C:/Projects/portal/trunk/test/unit/notifier\_test.rb:16:in  
`test\_notification’

Line 16 is commented in the above code. Looking at mail.rb:551  
suggests that body is nil, and I can confirm that the body passed in  
was not nil.

I know I can just not use @expected and check each bit of the mail  
separately, but I figure @expected is there for a reason (and also,  
checking each bit of the mail only verifies what is supposed to be  
there, not what isn’t.)

Maybe I’m just doing things completely wrong, as I’m not 100% used to  
Rails 2.x yet.

TX

---

<div class="post-metadata">

**Author:** ![trejkaz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/8797f3/32.png) [@trejkaz](https://www.ruby-forum.com/u/trejkaz)\
**Post date:** [December 12, 2008, 1:06am UTC](https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605/2 "2008-12-12T01:06:14Z")

</div>

So… nobody is doing mails with attachments? Or anyone who is, isn’t  
unit testing their code?

TX

---

<div class="post-metadata">

**Author:** ![trejkaz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/8797f3/32.png) [@trejkaz](https://www.ruby-forum.com/u/trejkaz)\
**Post date:** [December 12, 2008, 2:28am UTC](https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605/3 "2008-12-12T02:28:41Z")

</div>

if you commented out this line (and any other line that squawks)…

> ```
> @expected.parts << body_part # <=== ERROR HERE
> 
> ```

and printed out the mail’s parts before asserting them…

> ```
> mail = Notifier.create_notification()
> 
> ```

```
p mail.parts

```

…what would you see?

We TDD e-mails all the time, but we don’t write huge bulk assertions.  
Sometimes  
to test\_first\_, we cheat a little, write the correct code, print out  
what it  
does, and then write assertions which trap the important details.

To test-first, when you need to change a mail, you clone that test,  
change the  
input, change the assertions to expect different output, fail the test,  
then  
pass it in the code. This shows how cheating, especially in  
high-bandwidth  
situations like GUIs, can lead to better TDD…

–  
Phlip

---

<div class="post-metadata">

**Author:** ![trejkaz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/8797f3/32.png) [@trejkaz](https://www.ruby-forum.com/u/trejkaz)\
**Post date:** [December 15, 2008, 2:57am UTC](https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605/4 "2008-12-15T02:57:48Z")

</div>

On Dec 12, 12:27 pm, Phlip [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> …what would you see?  
> It looks like this…

[#\<TMail::Mail port=#TMail::StringPort:id=0x2ea1e18  
bodyport=#TMail::StringPort:id=0x2ea1a12\>,  
#\<TMail::Mail port=#TMail::StringPort:id=0x2ea1486  
bodyport=#TMail::StringPort:id=0x2ea0f68\>]

Something I have just discovered today. If you delay setting  
content\_type to _after_ adding the parts, it gets rid of that  
exception. Now it appears to be at least not causing an error, but I  
get a failure because (of course) the content types are subtly  
different.

What’s truly bizarre about it is that the MIME boundary string is  
quoted for the actual mail but not quoted for the expected mail, even  
though it doesn’t need to be quoted in either case. TMail seems to be  
doing some weird things there.

I think if I make the test environment overwrite TMail’s new\_boundary  
method, it might give me a way to dodge that.

TX

---

<div class="post-metadata">

**Author:** ![trejkaz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/8797f3/32.png) [@trejkaz](https://www.ruby-forum.com/u/trejkaz)\
**Post date:** [December 15, 2008, 3:32am UTC](https://www.ruby-forum.com/t/actionmailer-unit-testing-and-multipart-mails/154605/5 "2008-12-15T03:32:18Z")

</div>

Okay, solution found. Requires a combination of two tricks:

1. content\_type has to be set after the parts, as you get the error  
above otherwise. Maybe a bug in TMail, who knows. At least there is  
a workaround.

2. Boundary strings need mangling. I created myself a convenience  
method which will ultimately end up in my test helper if I have more  
than one notification model later.

def assert\_mail\_equal(expected\_mail, actual\_mail)  
assert\_equal replace\_boundary\_strings(expected\_mail.encoded),  
replace\_boundary\_strings(actual\_mail.encoded)  
end

def replace\_boundary\_strings(str)  
boundary\_pattern = ‘mimepart\_[0-9a-f]+\_[0-9a-f]+’  
str = str.gsub(/(Content-Type: multipart/mixed; boundary=)"?#  
{boundary\_pattern}"?/, “\1replaced”)  
str = str.gsub(/–#{boundary\_pattern}/, “–replaced”)  
end

I had suspected there was a second solution in tricking TMail into  
generating the same boundary string on calls to new\_boundary, but I  
couldn’t get that to work.

TX
