如何使用 moq 对在 .NET 中使用 asmx 服务的应用程序进行单元测试

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

我有一个通过使用ASMX服务发送短信的网络应用程序(现在已经生产并且工作正常),但是我现在需要单元测试这个应用程序使用asmx服务,下面是我的具体操作方法我对单元测试感兴趣

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");
}

所以这个动作方法接收一个视图模型作为参数,这个 vmodel 有 2 个属性(单元格号,消息),还有发送短信的用户。现在在我的单元测试或集成中(抱歉我也碰巧混淆了两者)我有以下代码,第一部分我只是想模拟方法

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;
}

以下为实际测试方法

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

我的测试似乎通过了,但是我还想对action方法本身进行单元测试,在这种情况下我只是测试短信是否发送成功,我该如何测试action方法本身,当然我需要模拟 asmx 服务,但我是空白的

另请注意,SendSMS 方法来自名为 DatabaseManager 的 class,这是调用 asmx 方法的地方,这里是来自 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; 
}

DatabaseManager 是控制器的依赖项,它包装了对 asmx 服务的第三部分调用。您的第一个测试实际上是在测试模拟,并没有多大用处。要测试控制器,您需要为操作提供必要的依赖项才能完成。

这是一个简单的例子。

假设以下界面基于所讨论的原始示例..

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

下面代表本例中被测控制器

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");
    }
}

测试操作需要模拟操作中使用的依赖项。下面展示了如何做到这一点。

[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"]);
}

可以复制和修改上面的内容以测试其他场景,比如当 SendSMS 失败时,只需在调用时制作模拟依赖项 return false

这应该足以让您入门。