[rspec-users] Specing raising error, handling, and then not raising error

Ashley Moran work at ashleymoran.me.uk
Sun Nov 4 10:20:15 EST 2007


On Nov 04, 2007, at 9:55 am, Mikel Lindsaar wrote:

>  it "should only increase the sent emails counter if the email was  
> sent" do
>    @smtp = mock(Net::SMTP)
>     
> @smtp 
> .should_receive(:send_message).exactly(4).times.and_return(true,  
> IOError, true, true)
>    Net::SMTP.stub!(:start).and_yield(@smtp)
>    @sender = Mailer::Sender.new(valid_args(:emails => @emails))
>    @sender.sent_emails.should == 3
>  end


Mikel,

It looks like you are doing too much here.  You are specifying sending  
with the SMTP object in the same block you are specifying the  
algorithm for counting the sent emails.  Also, the way it interrogates  
the email object to extract data looks fragile.  I would prefer to  
make the Email object responsible for sending itself, although I will  
await the approval of an expert before I tell you my solution is better!

I had a go at this, and my solution is MUCH longer, but it breaks the  
behaviour down to a finer level.  (As you didn't show the code for  
Mailer::Sender, I wrote a quick implementation myself, possibly  
similar to yours.)  Also, I'm never quite sure how best to use  
"before" blocks, so don't take mine as a canonical example.  Anyway  
here is what I came up with:

   class Email
     def initialize(encoded_message, from, destination)
       @encoded_message, @from, @destination =
           encoded_message, from, destination
     end

     def send_via(smtp)
       begin
         smtp.send_message(@encoded_message, @from, @destination)
       rescue IOError => e
         0
       else
         1
       end
     end
   end

   describe Email, :shared => true do
     before(:each) do
       @smtp = mock("Net::SMTP")
       @email = Email.new("message", "from at mydomain", "to at destination")
     end

     it "send_via: should send :send_message to the SMTP object with  
the email details" do
       @smtp.should_receive(:send_message).
          with("message", "from at mydomain", "to at destination")
       @email.send_via(@smtp)
     end
   end

   describe Email, "sent via a working SMTP" do
     it_should_behave_like "Email"

     before(:each) do
       @smtp.stub!(:send_message)
     end

     it "send_via: should return 1 if the email sent successfully" do
       @email.send_via(@smtp).should == 1
     end
   end

   describe Email, "sent via a faulty SMTP" do
     it_should_behave_like "Email"

     before(:each) do
       @smtp.stub!(:send_message).and_raise(IOError.new)
     end

     it "send_via: should return 1 if the email sent successfully" do
       @email.send_via(@smtp).should == 0
     end
   end

   class Spammer
     def initialize(smtp)
       @smtp = smtp
     end

     def send(emails)
       emails.inject(0) { |success_count, email| success_count +  
email.send_via(@smtp) }
     end
   end

   describe Spammer, "created with an SMTP" do
     before(:each) do
       @email_successful_1 = mock(Email)
       @email_successful_1.stub!(:send_via).and_return(1)
       @email_successful_2 = mock(Email)
       @email_successful_2.stub!(:send_via).and_return(1)
       @email_unsuccessful_1 = mock(Email)
       @email_unsuccessful_1.stub!(:send_via).and_return(0)

       @emails = [ @email_successful_1, @email_successful_2,  
@email_unsuccessful_1]

       @spammer = Spammer.new(:smtp)
     end

     it "send: should send each email in turn with the SMTP and return  
the successful count" do
        
@email_successful_1.should_receive(:send_via).with(:smtp).and_return(1)
        
@email_successful_2.should_receive(:send_via).with(:smtp).and_return(1)
        
@email_unsuccessful_1 
.should_receive(:send_via).with(:smtp).and_return(2)

       @spammer.send(@emails)
     end

     it "send: should return the count of successful emails" do
       @spammer.send(@emails).should == 2
     end
   end

Let me know if this is helpful

Regards
Ashley


-- 
blog @ http://aviewfromafar.net/
linked-in @ http://www.linkedin.com/in/ashleymoran
currently @ home



More information about the rspec-users mailing list