在Microsoft Moles中创建部分存根

时间:2012-10-10 20:22:30

标签: unit-testing moles stub

我正用这个把头发拉出来。我看过并找不到一个简单明了的例子,用于创建和使用Microsoft Moles的部分存根。也许我错过了某些方面,或者我的代码架构很差,但我似乎无法让它工作。

这是我的课程(简化):

public class AccountService : IAccountService {
        private readonly webServiceProxy IExternalWebServiceProxy;

    public AccountService(IExternalWebServiceProxy webServiceProxy) {
        this.webServiceProxy = webServiceProxy;
    }

    public List<AccountModel> GetAccounts(string customerId) {
         var returnList = new List<AccountModel>();
         var xmlResponse = webServiceProxy.GetAllCustomerAccounts(customerId);

         var accountNodes = xmlResponse.SelectNodes("//AccountNodes");
            if (accountNodes != null)
            {
                foreach (XmlNode node in accountNodes)
                {
                    var account = this.MapAccountFromXml(node);

                    if (!string.IsNullOrEmpty(account.AccountNumber))
                    {
                        returnList.Add(account);
                    }
                }
            }

            return returnList;
    }

    public AccountModel MapAccountFromXml(XmlNode node) {
        if (!IsValidAccount(node) {
            return null;
        }

        // This performs a lot of XML manipulation getting nodes based on attributes 
        // and mapping them to the various properties of the AccountModel. It's messy 
        // and I didn't want it inline with the other code.

        return populatedAccountModel;
    {

    public bool IsValidAccount(XmlNode node) 
    {
        var taxSelectValue = node.SelectSingleNode("//FORMAT/Field[@taxSelect='1']").First().Value;
        var accountStatus = // similar to first line in that it gets a single node using a specific XPath
        var maturityDate = // similar to first line in that it gets a single node using a specific XPath
        var maturityValue = // similar to first line in that it gets a single node using a specific XPath

        return taxSelectValue != string.Empty && taxSelectValue != "0" && (accountStatusValue != "CL" || (maturityDate.Year >= DateTime.Now.AddYears(-1).Year));
    }
}

我想要做的是测试我的GetAccounts()方法。我可以删除IExternalWebServiceProxy调用并返回伪XML,但我的服务中发生了内部调用,因为我的GetAccounts()方法调用了MapAccountFromXml(),而后者又调用了IsValidAccount()。

也许解决方案是不要担心会破坏长且涉及的MapAccountFromXml()和IsValidAccount()代码,只是将它们内联到GetAccount()调用中,但我宁愿将它们分开以便于代码可读性。 / p>

我创建了Moles程序集,并知道我可以像这样创建我的类的存根版本

var stubWebService = SIExternalWebServiceProxy {
       GetAllCustomerAccounts = delegate {
            return SomeHelper.GetFakeXmlDocument();
       }
}

var stubAccountService = new SAccountService() { callsBase = true; }

我的问题是我不知道如何覆盖对MapAccountFromXml和IsValidAccount的内部调用,我不希望我的单元测试测试这些方法,我想隔离GetAccounts进行测试。我在某处阅读了某些方法需要在部分存根中被覆盖的虚拟方法,但是找不到任何能够在调用我想要测试的基础时创建一个覆盖几个方法的存根的存根。

2 个答案:

答案 0 :(得分:1)

Peer让我走上正轨,谢谢你。

事实证明,我所寻找的东西在鼹鼠中被称为Detours。而不是使用

来存根接口
var stubAccountService = new SIAccountService();

我需要做的是创建一个AccountService的实例,然后绕过我想要模拟的方法的所有调用,比如这个

var accountService = new AccountService();

MAccountService.AllInstances.MapAccountFromXmlXmlNode = delegate { 
    return new AccountModel(); 
};

MAccountService由Moles在你装配时提供。唯一缺少的是,要使其工作,您需要将以下属性添加到测试方法中:

[HostType("Moles")]

这在我当地很有用,但最终我无法让TFS进行自动构建

更新

在看Rhino Mocks时,我偶然发现了另一种做法。如果被模拟的类中的方法是 virtual ,那么您可以在模拟中覆盖它们,如下所示:

var accountService = new SAccountService();
accountService.MapAccountFromXmlXmlNode = delegate
    {
        return new AccountModel();
    }

现在我可以致电

accountService.GetMemberAccounts();

当accountService调用MapAccountFromXml时,它将被存根捕获并在我认为必要时进行处理。没有搞乱HostType,它就像一个魅力。

答案 1 :(得分:0)

要在issolation中测试你的类中的方法,你可以通过为IsValidAccount和MapAccountFromXml方法制作一个mole来使用moles。或者使用存根进行存根实现,其中存根使用base调用orriginal方法。或者我认为是一个更好的解决方案,创建一个测试类来覆盖你想要存根的方法(除了你看到你自己的代码中发生的所有事情之外,这与存根会做的相同):

public class TestHelperAccountService : AccountService {

    public override AccountModel MapAccountFromXml(XmlNode node) {
        return new AccountModel(){
                    //Accountmodelstub
              };
    {

    public override bool IsValidAccount(XmlNode node) 
    {
        return true;
    }
}

通过这种方式,您可以对TestHelperAccountService类中的GetAccount方法进行测试,其中GetAccount方法以完全日照运行。您可以对MapAccountFromXml等方法执行相同的操作,以单独测试它们。