C# 使用Rhinomock模拟用于单元测试的xml文件

C# 使用Rhinomock模拟用于单元测试的xml文件,c#,xml,unit-testing,rhino-mocks,C#,Xml,Unit Testing,Rhino Mocks,我想模拟用于单元测试的xml,如下所示。我正在使用Rhinomocks框架进行模拟。如何通过不使用实际的xml文件来对我的方法进行单元测试。我必须更改代码结构吗 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class MyService : IMyService { private readonly string mSchemaPath = Path.Combine(

我想模拟用于单元测试的xml,如下所示。我正在使用Rhinomocks框架进行模拟。如何通过不使用实际的xml文件来对我的方法进行单元测试。我必须更改代码结构吗

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MyService : IMyService
{       
    private readonly string mSchemaPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "App_Data",
        "schema_0.1.xsd");        
    private readonly string mXmlPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "App_Data",
        "MyDataRecords.xml");

    private XDocument mXDocument;

    public MyService()
    {
        try
        {
            //load xml document
            mXDocument = XDocument.Load(mXmlPath);               

            if (mXDocument == null)
            {
                throw new Exception("Null returned while reading xml file");
            }
        }
        catch (Exception e)
        {
           //my exception management code
        }
    }

    public List<MyDataRecords> GetAllRecords()
    {
        ////fetch records from xDocument

       mXDocument.Save();
    }
    public void AddRecord(MyRecord record)
    {
        ////add record

       mXDocument.Save();
    }
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
公共类MyService:IMyService
{       
私有只读字符串mSchemaPath=Path.Combine(HostingEnvironment.ApplicationPhysicalPath,“应用程序数据”,
“schema_0.1.xsd”);
私有只读字符串mXmlPath=Path.Combine(HostingEnvironment.ApplicationPhysicalPath,“应用程序数据”,
“MyDataRecords.xml”);
私人XDocument文件;
公共MyService()
{
尝试
{
//加载xml文档
mXDocument=XDocument.Load(mXmlPath);
if(mXDocument==null)
{
抛出新异常(“读取xml文件时返回Null”);
}
}
捕获(例外e)
{
//我的异常管理代码
}
}
公共列表GetAllRecords()
{
////从xDocument获取记录
mXDocument.Save();
}
公共无效添加记录(MyRecord)
{
////添加记录
mXDocument.Save();
}
更新:

我已经修改了MyService类,使其具有重载构造函数,它接受
Func
来加载XDocument,还接受
Func
来解析与
HostingEnvironment.ApplicationPhysicalPath
相对应的值。调用默认构造函数时,将执行对XDocument.load的相同调用,类似于se用于在构建xml和xsd文件的路径时使用
HostingEnvironment.ApplicationPhysicalPath

但是,在单元测试中,您可以像这样调用其他构造函数:

        const string mockDirectory = "TEST";
        var expectedXmlPath = Path.Combine(mockDirectory, "App_Data", "MyDataRecords.xml");
        string xmlPathPassed = "";
        var service = new MyService(path =>
            {
                xmlPathPassed = path;
                return XDocument.Parse("<note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>");
            },
            () => mockDirectory);
        Assert.Equal(expectedXmlPath, xmlPathPassed);
const string mockDirectory=“TEST”;
var expectedXmlPath=Path.Combine(mockDirectory,“App_Data”,“MyDataRecords.xml”);
字符串xmlPathPassed=“”;
var service=newmyservice(路径=>
{
xmlPathPassed=path;
return XDocument.Parse(“ToveJaniReminderDon’t忘了我这个周末!”);
},
()=>mockDirectory);
等于(expectedXmlPath,xmlPathPassed);
您还可以在服务上公开XDocument,可能是通过只读属性,并检查XDocument是否表示模拟的xml

我的服务:

public class MyService : IMyService
{
private const string AppDataDirectoryName = "App_Data";
private const string SchemaFileName = "schema_0.1.xsd";
private const string XmlFileName = "MyDataRecords.xml";
private readonly Func<string, XDocument> mdocumentLoader;
private readonly Func<string> mAppDataDirectoryBuilder;
private readonly string mSchemaPath = "";
private readonly string mXmlPath = "";
private XDocument mXDocument;

public MyService() : this(XDocument.Load, () => HostingEnvironment.ApplicationPhysicalPath)
{
}

public MyService(Func<string, XDocument> documentLoader, Func<string> appDataDirectoryBuilder)
{
    mdocumentLoader = documentLoader;
    mAppDataDirectoryBuilder = appDataDirectoryBuilder;
    try
    {
        var baseDirectory = mAppDataDirectoryBuilder();
        mSchemaPath = Path.Combine(baseDirectory, AppDataDirectoryName, SchemaFileName);
        mXmlPath = Path.Combine(baseDirectory, AppDataDirectoryName, XmlFileName);
        mXDocument = mdocumentLoader(mXmlPath);

        if (mXDocument == null)
        {
            throw new Exception("Null returned while reading xml file");
        }
    }
    catch (Exception e)
    {
        //my exception management code
    }
}

public List<MyRecord> GetAllRecords()
{
    ////fetch records from xDocument
    return null;
    //mXDocument.Save();
}

public void AddRecord(MyRecord record)
{
    ////add record
    // mXDocument.Save(record);
}
公共类MyService:IMyService
{
私有常量字符串AppDataDirectoryName=“App\u Data”;
私有常量字符串SchemaFileName=“schema_0.1.xsd”;
private const string XmlFileName=“MyDataRecords.xml”;
私有只读Func mdocumentLoader;
私有只读Func mAppDataDirectoryBuilder;
私有只读字符串mSchemaPath=“”;
私有只读字符串mXmlPath=“”;
私人XDocument文件;
public MyService():这(XDocument.Load,()=>HostingEnvironment.ApplicationPhysicalPath)
{
}
公共MyService(Func documentLoader、Func appDataDirectoryBuilder)
{
mdocumentLoader=documentLoader;
mAppDataDirectoryBuilder=appDataDirectoryBuilder;
尝试
{
var baseDirectory=mAppDataDirectoryBuilder();
mSchemaPath=Path.Combine(baseDirectory、AppDataDirectoryName、SchemaFileName);
mXmlPath=Path.Combine(baseDirectory、AppDataDirectoryName、XmlFileName);
mXDocument=mdocumentLoader(mXmlPath);
if(mXDocument==null)
{
抛出新异常(“读取xml文件时返回Null”);
}
}
捕获(例外e)
{
//我的异常管理代码
}
}
公共列表GetAllRecords()
{
////从xDocument获取记录
返回null;
//mXDocument.Save();
}
公共无效添加记录(MyRecord)
{
////添加记录
//mXDocument.Save(记录);
}
}

[程序集:InternalsVisibleTo(“MyService.UnitTests”)]
公共类MyService:IMyService
{
私有只读字符串路径;
私有只读字符串mXmlPath;
公共MyService()
:这个(
Combine(HostingEnvironment.ApplicationPhysicalPath,“App_Data”,“MyDataRecords.xml”),
Combine(HostingEnvironment.ApplicationPhysicalPath,“App_数据”,“schema_0.1.xsd”))
{
}
内部MyService(字符串xmlPath、字符串schemaPath)
{        
尝试
{
mXmlPath=xmlPath;
mSchemaPath=schemaPath;
//加载xml文档
mXDocument=Xdocument.Laod(mXmlPath);
if(mXDocument==null)
{
抛出新异常(“读取xml文件时返回Null”);
}
}
捕获(例外e)
{
//我的异常管理代码
}
}
公共列表GetAllRecords()
{
////从xDocument获取记录
mXDocument.Save();
}
公共无效添加记录(MyRecord)
{
////添加记录
mXDocument.Save();
}

}

这是一个绝妙的建议,在不破坏大部分代码结构的情况下,我可以做到这一点。唯一需要考虑的是,客户端也可以使用其他构造函数。我如何阻止这种情况?这种单元测试方法在将测试改装为遗留代码时非常有用。不过,我还没有找到一种不公开额外构造函数的方法。可测试性p在我的书中,ayoff超过了这个设计问题;始终加载原始xml。我不确定是否正确地跟踪了您。在上面的示例中,mXmlPath始终针对生产和单元测试场景进行初始化,这很好。在单元测试场景中,调用
var service=new MyService(不相关路径=>XDocument.Parse(“用于单元测试的xml”));
,忽略“不相关路径”(mXmlPath)并只解析xml字符串,没有实际加载xml文件。您的单元测试是否加载xml字符串?为什么不简单地将“仅测试”构造函数设置为内部,然后扩展内部,使其可访问
[assembly: InternalsVisibleTo("MyService.UnitTests")]
public class MyService : IMyService
{

private readonly string mSchemaPath;

private readonly string mXmlPath; 


 public MyService()
        : this(
            Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "App_Data", "MyDataRecords.xml"),
            Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "App_Data", "schema_0.1.xsd"))
    {

    }

internal MyService(string xmlPath,string schemaPath)
{        
    try
    {
         mXmlPath=xmlPath;
         mSchemaPath=schemaPath;

        //load xml document
        mXDocument = Xdocument.Laod(mXmlPath);

        if (mXDocument == null)
        {
            throw new Exception("Null returned while reading xml file");
        }
    }
    catch (Exception e)
    {
        //my exception management code
    }
}

public List<MyRecord> GetAllRecords()
{
    ////fetch records from xDocument

    mXDocument.Save();
}

public void AddRecord(MyRecord record)
{
    ////add record

    mXDocument.Save();
}