Mronzer
Mronzer

Reputation: 439

How to use moq to unit test an application that consumes asmx service in .NET

I have a web application that sends an sms by consuming an ASMX service(which is now production and working fine), however I now need to unit test this application consumes the asmx service, below is the specific action method I'm interested in unit testing

public ActionResult CreateSms(SendSMSViewModel tblSend)
{
    if (ModelState.IsValid)
    {
        if (_dbManager.SendSMS(tblSend.CellNumber, tblSend.Message, User.Identity.Name))
        {
            TempData["Success"] = "Message was successfully sent";
        }
        else
        {
            TempData["Error"] = "An error occured while sending the message";
        }
    }

    return RedirectToAction("CreateSms");
}

So this action method receives a view model as parameter, this vmodel has 2 properties(cell number, message) and there is also user who is sending the sms. Now in my unit test or integration(apologies I also happen to confuse the two) I have the following code, this first part I am just trying to mock the method(s)

public DatabaseManagerTest()
{
    Mock<IDatabaseManager> moqDB = new Mock<IDatabaseManager>();

    //Setup send sms test
    moqDB.Setup(md => md.SendSMS("0734233173", "Test message", "Ronny.Mahlangu")).Returns(true);

    this._mockDatabaseManager = moqDB.Object;
}

and below is the actual test method

public void TestSendSms()
{
    bool results = this._mockDatabaseManager.SendSMS("0734233173", "Test message", "Ronny.Mahlangu");
    Assert.IsTrue(results);
}

My testing seem to pass, however I also want to unit test the action method itself, in this case I'm only testing whether sms sends successfully, how do I go about testing the action method itself, surely I need to mock the asmx service but I am blank

Also note that the SendSMS method comes from a class named DatabaseManager, which is where the asmx methods are being invoked, here is the peace of code from that class

public bool SendSMS(string cellNumber, string message, string logonName) {    
    if(string.IsNullOrWhiteSpace(cellNumber))
    {
        throw new ArgumentNullException("cell phone number is null");
    }
    if(string.IsNullOrWhiteSpace(message))
    {
        throw new ArgumentNullException("Sms message is null");
    }

    using (SMSService.SendSmsSoapClient sms = new SMSService.SendSmsSoapClient())
    {
        if (sms.SendDirect(cellNumber, message, ConfigurationManager.AppSettings["costCentre"], userLogonName, ConfigurationManager.AppSettings["source"]) == SMSService.Status.Successful)
        {
            SaveSmsDetails(cellNumber, message, userLogonName);
            return true;
        }
    }

    return false; 
}

Upvotes: 3

Views: 687

Answers (1)

Nkosi
Nkosi

Reputation: 247088

DatabaseManager is a dependency of the controller that wraps the 3rd part call to the asmx service. You first test is actually testing the mock and is of not much use. To test the controller you need to provided the necessary dependencies for the action to flow to completion.

Here is a simple example.

Assuming the following interface based on original example in question..

public interface IDatabaseManager {
    bool SendSMS(string cellNumber, string message, string logonName);
}

The following represents the controller under test in this example

public class SmsController : Controller {
    private IDatabaseManager _dbManager;

    public SmsController(IDatabaseManager databaseManager) {
        this._dbManager = databaseManager;
    }


    public ActionResult CreateSms(SendSMSViewModel tblSend) {
        if (ModelState.IsValid) {
            if (_dbManager.SendSMS(tblSend.CellNumber, tblSend.Message, User.Identity.Name)) {
                TempData["Success"] = "Message was successfully sent";
            } else {
                TempData["Error"] = "An error occured while sending the message";
            }
        }

        return RedirectToAction("CreateSms");
    }
}

Testing the action would require mocking the dependencies used in the action. The following show how one can do that.

[TestMethod]
public void TestCreateSms() {
    //Arrange
    var expectedCellNumber = "0734233173";
    var expectedMessage = "Test message";
    var expectedName = "Ronny.Mahlangu";
    var expectedResult = true;

    // - dependency mocked with desired behavior
    var moqDB = new Mock<IDatabaseManager>();
    moqDB
        .Setup(md => md.SendSMS(expectedCellNumber, expectedMessage, expectedName))
        .Returns(expectedResult)
        .Verifiable();

    // - Fake user for request in order to access User.Identity.Name
    var principal = new ClaimsPrincipal(new GenericIdentity(expectedName));
    var contextMock = new Mock<HttpContextBase>();
    contextMock.Setup(_ => _.User).Returns(principal);

    // - controller under test
    var controller = new SmsController(moqDB.Object);
    controller.ControllerContext = new ControllerContext(contextMock.Object, new RouteData(), controller);

    // - view model sent to action
    var tblSend = new SendSMSViewModel {
        CellNumber = expectedCellNumber,
        Message = expectedMessage
    };

    //Act
    var result = controller.CreateSms(tblSend);

    //Assert
    moqDB.Verify(); //verify that the mock executed as expected
    //you can also assert the results of calling the action to confirm expectations
    Assert.IsTrue(controller.TempData.ContainsKey("Success"));
    Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
    var redirectResult = result as RedirectToRouteResult;
    Assert.AreEqual("CreateSms", redirectResult.RouteValues["action"]);
}

The above can be copied and modified to test other scenarios, like when SendSMS fails, by simply making the mocked dependency return false when called.

This should be enough to get you started.

Upvotes: 2

Related Questions