【问题标题】:Faking a INI Configuration File Settings with FakeItEasy in C#在 C# 中使用 FakeItEasy 伪造 INI 配置文件设置
【发布时间】:2013-08-18 05:03:04
【问题描述】:

我有一个继承自抽象 Configuration 类的类,然后每个类实现 INI 文件、XML、conf 或专有格式的读取器。我在使用 FakeItEasy 创建要测试的对象时遇到问题。

我尝试测试的对象通过依赖注入使用配置对象,因此它可以通过调用 ReadString()、ReadInteger() 等函数来简单地读取配置设置,然后是位置的文本(部分, 例如,带有 INI 的密钥)可以以任何格式的配置文件(INI、XML、conf 等)从适当的部分检索。

正在使用的示例代码:

public class TestFile
{
    private readonly ConfigurationSettings ConfigurationController_ ;

    ...

    public TestFile(ConfigurationSettings ConfigObject)
    {
        this.ConfigurationController_ = ConfigObject;
    }

    public TestFile(XMLFile ConfigObject)
    {
        this.ConfigurationController_ = ConfigObject;
    }

    public TestFile(INIFile ConfigObject)
    {
        this.ConfigurationController_ = ConfigObject;
    }

    ...

    private List<string> GetLoginSequence()
    {
        List<string> ReturnText = new List<string>();
        string SignOnFirst = ConfigurationController_.ReadString("SignOn", "SignOnKeyFirst", "Y");
        string SendEnterClear = ConfigurationController_.ReadString("SignOn", "SendEnterClear", "N");

        if (SendEnterClear.Equals("Y", StringComparison.CurrentCultureIgnoreCase))
        {
            ReturnText.Add("enter");
            ReturnText.Add("clear");
        }

        if (SignOnFirst.Equals("N", StringComparison.CurrentCultureIgnoreCase))
        {
            ReturnText.AddRange(MacroUserPassword("[$PASS]"));
            ReturnText.Add("sign_on");
        }
        else
        {
            ReturnText.Add("sign_on");
            ReturnText.AddRange(MacroUserId("[$USER]"));
            ReturnText.AddRange(MacroUserPassword("[$PASS]"));
        }
        return ReturnText;
    }

一个简单的测试示例:

    [TestMethod]
    public void TestSignOnSequence()

        IniReader FakeINI = A.Fake<IniReader>();

        //Sample Reads:
        //Config.ReadString("SignOn", "SignOnKeyFirst", "Y");
        //Config.ReadString("SignOn", "SendEnterClear", "N");  // Section, Key, Default

        A.CallTo(() => FakeINI.ReadString(A<string>.That.Matches(s => s == "SignOn"), A<string>.That.Matches(s => s == "SendEnterClear"))).Returns("N");
        A.CallTo(() => FakeINI.ReadString(A<string>.That.Matches(s => s == "SignOn"), A<string>.That.Matches(s => s == "SignOnKeyFirst"))).Returns("N");

        A.CallTo(FakeINI).Where( x => x.Method.Name == "ReadInteger").WithReturnType<int>().Returns(1000);

        TestFile TestFileObject = new TestFile(FakeINI);

        List<string> ReturnedKeys = TestFileObject.GetLoginSequence();
        Assert.AreEqual(2, ReturnedKeys.Count, "Ensure all keystrokes are returned");

这编译得很好,但是当我执行代码时,我得到以下异常:

Test threw Exception:
FakeItEasy.Configuration.FakeConfigurationException:
    The current proxy generator can not intercept the specified method for the following reason:
    - Non virtual methods can not be intercepted.

如果我更改创建假的方式,然后无一例外地工作,我无法为对同一函数的各种调用获取不同的值。

A.CallTo(FakeINI).Where( x => x.Method.Name == "ReadString").WithReturnType<string>().Returns("N");

上述方法不允许我控制函数对 INI 的不同调用的返回。

如何将参数的where和test这两种方法结合起来?

根据要求提供其他定义:

public abstract class ConfigurationSettings
{
    ...

    abstract public int ReadInteger(string Section, string Key, int Default);
    abstract public string ReadString(string Section, string Key, string Default);

    public int ReadInteger(string Section, string Key)
    {   return ReadInteger(Section, Key, 0);    }

    public int ReadInteger(string Key, int Default)
    {   return ReadInteger("", Key, Default);   }

    public int ReadInteger(string Key)
    {   return ReadInteger(Key, 0);             }

    public string ReadString(string Section, string Key)
    {   return ReadString(Section, Key, null);  }
}

public class IniReader : ConfigurationSettings
{
    ...

    public IniReader()
    {
    }

    public IniReader(string PathAndFile) 
    {
        this.PathAndFileName = PathAndFile;
    }

    public override int ReadInteger(string Section, string Key, int Default)
    {
        return GetPrivateProfileInt(Section, Key, Default, PathAndFileName);
    }

    public override string ReadString(string Section, string Key, string Default)
    {
        StringBuilder WorkingString = new StringBuilder(MAX_ENTRY);
        int Return = GetPrivateProfileString(Section, Key, Default, WorkingString, MAX_ENTRY, PathAndFileName);
        return WorkingString.ToString();
    }
}

【问题讨论】:

  • 嗨,@Steven Scott。如果我们看到IniReader 的定义(至少是签名),我认为我们可以提供更好的帮助。听起来您正试图伪造一些非虚拟的方法 (which isn't supported)。如果没有伪造类的签名(以及表明哪一行引发异常),我们就无能为力了。
  • 顺便说一句,也许我不应该混淆事物,但您的原始测试可能会更简单一些。你的第一个 A.CallTo 相当于A.CallTo(() =&gt; FakeINI.ReadString("SignOn", "SendEnterClear")).Returns("N");
  • 哦!我刚刚注意到别的东西。在GetLoginSequence 中,您有两个带有3 参数的ReadString 方法——看起来像一个部分、一个键和一个默认值。但是您的A.CallTos 仅指定 2 - 部分和键。我认为这不会导致您的“无法拦截非虚拟方法”。错误,但它会在某些时候导致错误,除了在您没有指定参数的最后一个示例中。但正如您所指出的,这不允许您为各种输入指定不同的返回类型。
  • @BlairConrad 感谢您的回复。实际上,INIReader 中的 ReadString 有各种选项,包括您指示的 3(Section, Key, Default)和 2 个参数(Section, Key),然后调用 3 参数 ReadString。
  • @BlairConrad 我认为你的定义不是虚拟的是正确的,但是我在父类中将它们定义为抽象,所以它们必须在继承类中定义并实现,然后这样做不允许我将虚拟方法添加到抽象定义中。我已将示例代码添加到主要问题中。

标签: c# testing ini fakeiteasy


【解决方案1】:

你得到了

当前代理生成器无法拦截指定方法,原因如下: - 非虚方法不能被拦截。

错误,因为您试图伪造 ReadString 的 2 参数版本。 Only virtual members, abstract members, or interface members can be faked。由于您的双参数 ReadString 不是这些,因此无法伪造。我认为您应该有一个虚拟的 2 参数 ReadString 伪造 3 参数 ReadString。

您的示例使我倾向于伪造 3 参数 ReadString,尤其是因为 GetLoginSequence 使用了那个。然后我认为你可以只使用表达式(而不是方法名称字符串)进行约束,一切都会成功。

我用你的代码(主要是在你更新之前)做了一个小测试,并成功伪造了 3 参数 ReadString:

[Test]
public void BlairTest()
{
    IniReader FakeINI = A.Fake<IniReader>();

    A.CallTo(() => FakeINI.ReadString("SignOn", "SendEnterClear", A<string>._)).Returns("N");
    A.CallTo(() => FakeINI.ReadString("SignOn", "SignOnKeyFirst", A<string>._)).Returns("N");

    // Personally, I'd use the above syntax for this one too, but I didn't
    // want to muck too much.
    A.CallTo(FakeINI).Where(x => x.Method.Name == "ReadInteger").WithReturnType<int>().Returns(1000);


    TestFile TestFileObject = new TestFile(FakeINI);

    List<string> ReturnedKeys = TestFileObject.GetLoginSequence();
    Assert.AreEqual(2, ReturnedKeys.Count, "Ensure all keystrokes are returned");
}

【讨论】:

  • 谢谢。您的指导也让我更加努力地查看我的代码,并且看到抽象和虚拟之间的区别,我也可以使用虚拟。但是,按照您的指示进行更改以使额外的参数像魅力一样起作用。非常感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
  • 2012-09-10
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 2012-03-11
相关资源
最近更新 更多