使用C#中的FakeItEasy伪造INI配置文件设置

时间:2013-08-15 21:37:27

标签: c# testing ini fakeiteasy

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

我试图测试的对象通过依赖注入使用配置对象,因此它可以通过调用ReadString(),ReadInteger()等函数,然后调用该位置的文本来简单地读取配置设置(Section ,例如,带有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的不同调用的返回值。

如何组合两种方法,参数的位置和测试?

要求的其他定义:

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

1 个答案:

答案 0 :(得分:2)

你得到了

  

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

错误,因为您试图伪造ReadString的2参数版本。 Only virtual members, abstract members, or interface members can be faked。由于你的两个paremter 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");
}