1

我用这个把头发拉出来。我已经查看并找不到使用 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(),而 MapAccountFromXml() 又调用 IsValidAccount()。

也许解决方案是不用担心分解冗长且涉及的 MapAccountFromXml() 和 IsValidAccount() 代码,只需将它们内联到 GetAccount() 调用中,但我宁愿将它们分解为代码可读性。

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

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

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

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

4

2 回答 2

1

同行让我走上正轨,谢谢。

原来,我一直在寻找的东西叫做 Detours in Moles。而不是使用存根接口

var stubAccountService = new SIAccountService();

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

var accountService = new AccountService();

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

当您对您的程序集进行 Mole 时,Moles 会提供 MAccountService。唯一缺少的部分是,要使其正常工作,您需要将以下属性添加到您的测试方法中:

[HostType("Moles")]

这在本地对我有用,但最后我无法让 TFS 进行自动构建

更新

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

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

现在我可以打电话了

accountService.GetMemberAccounts();

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

于 2012-10-24T16:03:40.120 回答
0

要单独测试您的类中的方法,您可以通过为 IsValidAccount 和 MapAccountFromXml 方法制作一个痣来使用痣。或者使用存根实现存根,让存根使用 base 调用原始方法。或者我认为是一个更好的解决方案,创建一个覆盖您想要存根的方法的测试类(这与存根所做的相同,除非您看到自己的代码中发生的所有事情):

public class TestHelperAccountService : AccountService {

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

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

这样,您可以在您的 GetAccount 方法完全隔离运行的 TestHelperAccountService 类上对 GetAccount 方法进行测试。您可以对 MapAccountFromXml 等方法执行相同的操作来单独测试它们。

于 2012-10-11T08:32:36.223 回答