-4

Trying to test a void method that sends an email whenever one action occurred. Most of the parameter in this method inside is hard coded. Based on the switch case mail subject also changes.

public void SendMail(ControllerContext, admin, userList)
{
    try
    {
        var templatePath = string.Empty;

        switch (pathid)
        {
            case 1:
                templatePath = "XXX";

            default:
                break;
        }

        switch (urlId)
        {
            case 1:
                viewModel.ItemUrl = "URL";
                break;
            default:
                break;

        }

        foreach (var user in userList)
        {
            viewModel.FirstName = user.FirstName;
            viewModel.LastName = user.LastName;
            string bodyOftheContent = Generator.TemplateGeneration(ControllerContext, tempatePath);
            Object.SendCustomMail(user.Email, bodyOftheContent, subject);
        }
    }
    catch (Exception ex)
    {
        LogError("Error in sending notification email", ex, MethodBase.GetCurrentMethod());
    }
}

Any possible way to unit test void method inside has lots of hard-coded values

  • 1
    Can you post your code? What edition of Microsoft Visual Studio do you have? – zaitsman Dec 27 '16 at 10:14
  • 1
    The class containing this method is not properly designed. In unit tests you really don't want to actually send the e-mail. But do want to test the template generation. You want to seperate those into seperate classes. – Wazner Dec 27 '16 at 10:20
  • @Wazner That is not necessarily correct. Read up on Microsoft Fakes. you can generate a template, and send the email, then in the unit test substitute the email client for a shim and voila - assert that the email sent matched expected template processed. – zaitsman Dec 27 '16 at 10:25
  • @zaitsman I did not know about Microsoft Fakes supporting rewriting framework code. Pretty cool. Nonetheless, giving classes a single thing to do (instead of multiple) is a good thing anyway. – Wazner Dec 27 '16 at 11:16
  • @Wazner, yup, that you're correct in. – zaitsman Dec 27 '16 at 11:20
  • First, can we unit test void method? If yes then this is good practice or not? – Rajadurai Azhagudurai Dec 27 '16 at 13:10
  • @Wazner I think you are correct – Rajadurai Azhagudurai Dec 27 '16 at 13:13
  • @zaitsman you are also correct. But In my method Template parameter all are generated inside the mail sending function based on the user permission got assigned differ person to person. – Rajadurai Azhagudurai Dec 27 '16 at 13:16
  • @user7013033 You should have an assertion for each line of the code that you write, that is a good practice. That creates a test harness that tells future developers when they make any changes to the code, if the behaviour of the class is preserved. You have not mentioned, what edition of VS do you use? – zaitsman Dec 27 '16 at 21:07
  • @zaitsman VS 2015 Pro – Rajadurai Azhagudurai Dec 28 '16 at 04:01
  • @user7013033 So in this case you will have to refactor your code a bit. First, have your db context hidden behind an interface, e.g. `IRepository`. Then, have the actual code sending the e-mail (`SmtpClient` or whatever you use) hidden behind another interface, e.g. `IEmailSender`. Then, use one of many mocking libraries (e.g. Moq/NSubstitute/RhinoMocks etc) and write a unit test that will assert that given whatever inputs from db, the `IEmailSender.SendEmail()` was called and that correct data was passed as parameters. – zaitsman Dec 28 '16 at 04:05
  • @zaitsman this is my code – Rajadurai Azhagudurai Dec 28 '16 at 12:04
  • Move `Object.SendCustomMail` into a `IObject` interface. Then you can `Mock` the `IObject` and `Assert` that bodyOfTheContent matches conditions. Judging by the method, you are looking at about 10 or so tests. – zaitsman Dec 30 '16 at 08:13
  • Possible duplicate of [Unit testing void methods?](https://stackoverflow.com/questions/246038/unit-testing-void-methods) – Rajadurai Azhagudurai Jun 01 '19 at 09:05

1 Answers1

1

You cannot unit test a void method, but you can make a void method part of a unit test to see if a desired mutation has occured in a class property, supplied input document or record in the database.

I.e.

public class SimpleEmailTest
{
   private int _sentEmails;

   public SimpleEmailTest()
   {
      this._sentEmails = 0;
   }

   [TestMethod]
   public void TestIfEmailsGetSent()
   {
      SendEmail();

      Assert.IsTrue(_sentEmails == 1);
   }

   public void SendEmail
   {
       // do email sending
       _sentEmails++;
   }
}
CthenB
  • 800
  • 6
  • 17
  • 1
    What do you mean 'you can not unit test a void method'? A void method is a method that does soemthing, for each line of code you write you can and should write an assert. – zaitsman Dec 27 '16 at 10:25
  • @zaitsman which is exactly what I'm saying in the second part of my sentence. You write a unit test to check the expected outcome of a variable or property, Since a void method returns neither, you cannot assert whether its value is correct. Thus, you have to unit test what it mutates, rather than unit test the actual method. In line of the above example, I can't do `Assert.IsTrue(SendEmail() == 1)` but I can do `Assert.IsTrue(_sentEmails == 1` – CthenB Dec 27 '16 at 10:31