为什么NSubstitute在指定倍数时仅返回最后一个值

时间:2016-09-02 11:32:10

标签: c# mocking nsubstitute

我正在尝试在我的DAO类中模拟IDbConnection,但我收到以下错误:

字符串长度均为5.字符串在索引0处不同。   预计:“11111”   但是:“22222”

这是我的代码:

正在测试的课程

internal class ImportAcquisitionDataDAO : IImportAcquisitionDataDAO
{
    private static Logger log = LogManager.GetCurrentClassLogger();

    private readonly IDbConnection connection;

    internal ImportAcquisitionDataDAO(IDbConnection connection)
    {
        this.connection = connection;
    }

    internal List<DefinitionEntry> GetDefinitions()
    {
        log.Debug("Getting definitions from database.");

        var definitions = new List<DefinitionEntry>();

        using (connection)
        {
            connection.Open();
            log.Trace("Database connection opened");

            IDbCommand command = connection.CreateCommand();
            command.CommandText = @"SELECT *
                                    FROM MAPPING";

            IDataReader reader = command.ExecuteReader();
            log.Trace("Command executed:\n{0}", command.CommandText);

            definitions = GetMeterEntries(reader);
        }

        log.Debug("Obtained {0} definitions.", definitions.Count);
        return definitions;         
    }

    private List<DefinitionEntry> GetMeterEntries(IDataReader reader)
    {
        log.Trace("Parsing definitions from response");

        var result = new List<DefinitionEntry>();

        while (reader.Read())
        {
            var definition = new DefinitionEntry(
                reader.GetString(0),
                reader.GetString(1),
                reader.GetString(2),
                reader.IsDBNull(3) ? null : reader.GetString(3),
                reader.IsDBNull(4) ? null : reader.GetString(4),
                reader.IsDBNull(5) ? null : reader.GetString(5),
                reader.IsDBNull(6) ? null : reader.GetString(6)
                );

            log.Trace(definition.ToString());
            result.Add(definition);
        }

        return result;
    }
}

public class DefinitionEntry
{
    public string MeterSN { get; private set; }
    public string MATNR { get; private set; }
    public string IpAddress { get; private set; }
    public string SIM { get; private set; }
    public string ModulID { get; private set; }
    public string SIMUser { get; private set; }
    public string SIMPassword { get; private set; }

    public DefinitionEntry(string meterSN, string matnr, string ipAddress, string sim, string modulId, string simUser, string simPassword)
    {
        MeterSN = meterSN;
        MATNR = matnr;
        IpAddress = ipAddress;
        SIM = sim;
        ModulID = modulId;
        SIMUser = simUser;
        SIMPassword = simPassword;
    }
}

测试类

[TestFixture]
public class ImportAcquisitionDataDAOTests
{
    private IDbConnection mockConnection;
    private IDbCommand mockCommand;
    private IDataReader mockReader;
    private ImportAcquisitionDataDAO dao;

    [SetUp]
    public void SetUp()
    {
        mockConnection = Substitute.For<IDbConnection>();
        mockCommand = Substitute.For<IDbCommand>();
        mockReader = Substitute.For<IDataReader>();
        dao = new ImportAcquisitionDataDAO(mockConnection);

        mockConnection.CreateCommand().Returns(mockCommand);
        mockCommand.ExecuteReader().Returns(mockReader);
    }

    [Test]
    public void TestGetDefinitions()
    {
        // mock
        var databaseDefinitionFirst = new DefinitionEntry("11111", "AS3000-5/100-400-P", "10.42.42.26", "SIM-001", "12345lkj", "alibaba", "abrakadabra");
        var databaseDefinitionSecond = new DefinitionEntry("22222", "AS3000-5/100-400-Q", "10.42.42.158", null, null, null, null);

        mockReader.Read().Returns(true, true, false);
        mockReader.GetString(Arg.Is<int>(0)).Returns(databaseDefinitionFirst.MeterSN, databaseDefinitionSecond.MeterSN);
        mockReader.GetString(Arg.Is<int>(1)).Returns(databaseDefinitionFirst.MATNR, databaseDefinitionSecond.MATNR);
        mockReader.GetString(Arg.Is<int>(2)).Returns(databaseDefinitionFirst.IpAddress, databaseDefinitionSecond.IpAddress);
        mockReader.IsDBNull(Arg.Is<int>(3)).Returns(false, true);
        mockReader.GetString(Arg.Is<int>(3)).Returns(databaseDefinitionFirst.SIM);
        mockReader.IsDBNull(Arg.Is<int>(4)).Returns(false, true);
        mockReader.GetString(Arg.Is<int>(4)).Returns(databaseDefinitionFirst.ModulID);
        mockReader.IsDBNull(Arg.Is<int>(5)).Returns(false, true);
        mockReader.GetString(Arg.Is<int>(5)).Returns(databaseDefinitionFirst.SIMUser);
        mockReader.IsDBNull(Arg.Is<int>(6)).Returns(false, true);
        mockReader.GetString(Arg.Is<int>(6)).Returns(databaseDefinitionFirst.SIMPassword);

        // use
        List<DefinitionEntry> tested = dao.GetDefinitions();

        // verify
        Assert.AreEqual(2, tested.Count);
        AssertDefinitionEntry(databaseDefinitionFirst, tested.First());
        AssertDefinitionEntry(databaseDefinitionSecond, tested.Last());
    }

    private void AssertDefinitionEntry(DefinitionEntry expected, DefinitionEntry tested)
    {
        Assert.AreEqual(expected.MeterSN, tested.MeterSN);
        Assert.AreEqual(expected.MATNR, tested.MATNR);
        Assert.AreEqual(expected.IpAddress, tested.IpAddress);
        Assert.AreEqual(expected.SIM, tested.SIM);
        Assert.AreEqual(expected.ModulID, tested.ModulID);
        Assert.AreEqual(expected.SIMUser, tested.SIMUser);
        Assert.AreEqual(expected.SIMPassword, tested.SIMPassword);
    }
}

这是执行第一个Assert之前的调试屏幕: enter image description here

除了MeterSN字段之外,我都按照我的预期设置,两个条目都相同。但我无法找出原因

1 个答案:

答案 0 :(得分:0)

这是一个非常微妙的问题。

简短回答:

mockReader.GetString(Arg.Is<int>(0)).Returns(databaseDefinitionFirst.MeterSN, databaseDefinitionSecond.MeterSN);移至最后一次调用存根(位于mockReader.GetString(Arg.Is<int>(6))下方)。

更长的答案:

当我们说mockReader.GetString(Arg.Is<int>(5))时,Arg.Is部分返回零,这意味着mockReader.GetString(0)在测试设置中被多次调用。所以“11111”确实首先按照配置返回,但是当调用GetDefinitions时,该值已经被消耗掉了。

顺便提一下,NSubstitute将mockReader.GetString(5).Returns(...)视为与mockReader.GetString(Arg.Is<int>(5))相同,这会简化您的代码。

另外,我建议尝试找到use a real data reader in your tests的方法。像下面这样的东西可以让你避免嘲笑那种类型的细节:

        DataTable dt = new DataTable();
        dt.Columns.AddRange(
            new [] {
                new DataColumn("a", typeof(string)),
                new DataColumn("b", typeof(string)),
                new DataColumn("c", typeof(string)),
                new DataColumn("d", typeof(string)),
                new DataColumn("e", typeof(string)),
                new DataColumn("f", typeof(string)),
                new DataColumn("g", typeof(string))
            });
        dt.Rows.Add("11111", "AS3000-5/100-400-P", "10.42.42.26", "SIM-001", "12345lkj", "alibaba", "abrakadabra");
        dt.Rows.Add("22222", "AS3000-5/100-400-Q", "10.42.42.158", null, null, null, null);
        mockReader = new DataTableReader(dt);

然后,您可以从测试中删除所有模拟设置。通过一些调整(例如,一些辅助方法来执行诸如databaseDefinitionFirst之类的预期对象自动填充读者的方法)这可以形成一种测试与读者交互的所有代码的好方法,而不是必须伪造一切都在外面。