Search code examples
unit-testingtddrhino-mocks

How would this be useful


I'm still in the process of learning the how, when and wheres of Rhino Mocks. I just finished watching this tutorial: http://dimecasts.net/Content/WatchEpisode/12, I even typed it out using 3.5+ AAA syntax from Record/Playback but my question is how is this useful what can you do w/ this. I'm fundamentally missing something.

public class EmailerService
{
    public bool SendEmail(string email, string message)
    {
        return false;
    }
}

public class Emailer
{
    private IEmailerService _emailerService;
    public Emailer(IEmailerService emailerService)
    {
        this._emailerService = emailerService;
    }

    public void SendBatchEmails()
    {
        var emails = new Dictionary<string, string>
                        {
                            {"fred1@foo.com", "Hello1"},
                            {"fred2@foo.com", "Hello2"},
                            {"fred3@foo.com", "Hello3"},
                            {"fred4@foo.com", "Hello4"}
                        };
        foreach(KeyValuePair<string, string> email in emails)
        {
            if(!_emailerService.SendEmail(email.Key, email.Value))
            {
                throw new Exception(" You've Err'd");
            }
        }
    }
}

My Test are as follows:

        [TestMethod]
    public void EmailerServiceTest()
    {
        //Arrange
        var emailerServie = MockRepository.GenerateMock<IEmailerService>();
        emailerServie.Expect(x => x.SendEmail("", "")).IgnoreArguments().Return(true).Repeat.Any();

        //Act
        var emailer = new Emailer(emailerServie);
        emailer.SendBatchEmails();
    }


    [TestMethod]
    public void EmailerServiceTestException()
    {
        //Arrange
        var emailerServie = MockRepository.GenerateMock<IEmailerService>();
        emailerServie.Expect(x => x.SendEmail("", "")).IgnoreArguments().Throw(new Exception("You've Err'd"));

        //Act
        var emailer = new Emailer(emailerServie);
        emailer.SendBatchEmails();
    }

If you know when and where it will fail whats the point of writing a test where you already know the answer? Thanks


Solution

  • Your tests so far look like you are on the right path. If your tests seem obvious, that is a good thing. They are there to ensure expected behavior. If months from now you have forgotten about Emailer but you change EmailerService to throw an exception on a failure, instead of return false, your tests will remind you that Emailer is expecting different behavior and needs to be updated.

    When writing unit tests, I always do two things before any implementation: 1) writing a clear test function name, and divide the method into the three components Arrange, Act, Assert. This helps me to keep my expectations of the test clear, and to ensure that I don't forget anything.

    It appears as if you have forgotten to include the Assert portion of the test:

    [TestMethod]
    public void Emailer_IsCalled_SendEmailIsCalled()
    {
        //Arrange
        var emailerService = MockRepository.GenerateMock<IEmailerService>();
        emailerServie.Expect(x => x.SendEmail("", "")).IgnoreArguments().Return(true).Repeat.Any();
    
        //Act
        var emailer = new Emailer(emailerService);
        emailer.SendBatchEmails();
    
        //Assert
        emailer.VerifyAllExpectations();
    }
    

    In the second test, it seems that you want to test that if SendEmail fails an exception is thrown. In this case, emailerService is better suited as a Stub:

    [TestMethod, ExpectedException(typeof(Exception))]
    public void Emailer_SendEmailReturnsFalse_ThrowException()
    {
        //Arrange
        var emailerService = MockRepository.GenerateStub<IEmailerService>();
        emailerServie.Stub(x => x.SendEmail("", "")).IgnoreArguments().Return(false);
    
        //Act
        var emailer = new Emailer(emailerService);
        emailer.SendBatchEmails();
    
        //Assert
        //Expect Exception
        /* Even when I don't have and assert call here, I usually leave a note*/
    }