【问题标题】:Mock method in Parallel.ForEach always returns nullParallel.ForEach 中的模拟方法总是返回 null
【发布时间】:2016-06-09 18:45:00
【问题描述】:

我有以下代码:

public int LoadFilesAndSaveInDatabase(string filesPath)
{
    var calls = new ConcurrentStack<GdsCallDto>();

    var filesInDirectory = this._directoryProxy.GetFiles(filesPath);
    if (filesInDirectory.Any())
    {
        Parallel.ForEach(filesInDirectory, file =>
        {
            var lines = this._fileProxy.ReadAllLines(file, Encoding.Unicode);
            if (lines.Any())
            {
                // Reads the file and setup a new DTO.
                var deserializedCall = this._fileManager.DeserializeFileContent(lines, Path.GetFileName(file));

                // Insert the DTO in the database.
                this._gdsCallsData.InsertOrUpdateGdsCall(deserializedCall);

                // We keep track of the dto to count the number of restored items.
                calls.Push(deserializedCall);
            }
        });
    }
    return calls.Count;
}

我有以下单元测试:

[TestMethod]
public void ShouldLoadFilesAndSaveInDatabase()
{
    // Arrange
    var path = RandomGenerator.GetRandomString(56);
    var encoding = Encoding.Unicode;
    var fileNameEnvironment = RandomGenerator.GetRandomString();
    var fileNameModule = RandomGenerator.GetRandomString();
    var fileNameRecordLocator = RandomGenerator.GetRandomString(6);
    var fileNameTimestamp = RandomGenerator.GetRandomDateTime().ToString("O").Replace(':', 'o');

    // We simulate the presence of 4 files.
    var files = new List<string>
    {
        RandomGenerator.GetRandomString(255),
        RandomGenerator.GetRandomString(255),
        RandomGenerator.GetRandomString(255),
        RandomGenerator.GetRandomString(255)
    }.ToArray();

    var expectedResult = 4;

    this._directoryProxy.Expect(d => d.GetFiles(path))
        .Return(files);

    this._fileProxy.Expect(f => f.ReadAllLines(path, encoding))
        .Return(files).Repeat.Times(files.Length);

    // Act
    var result = this._databaseReloadManager.LoadFilesAndSaveInDatabase(path);

    // Assert
    Assert.AreEqual(result, expectedResult);
    this._directoryProxy.AssertWasCalled(d => d.GetFiles(path));
    this._fileProxy.AssertWasCalled(f => f.ReadAllLines(path, Encoding.Unicode));
}

问题出在下面一行:

var lines = this._fileProxy.ReadAllLines(file, Encoding.Unicode);

即使我设置了期望值和返回值,当我运行单元测试时,它总是返回 null。

我正在使用 Rhino.Mocks,它在其他地方工作得非常好,但在那里却不行。

我在这里查看了一些讨论,但没有一个有帮助。可能是因为使用了 Parallel.ForEach 吗?有没有办法做这样的模拟?

如果您需要任何其他信息,请告诉我。

【问题讨论】:

  • 你的名字很糟糕。什么是“_DirectoryProxy”,什么是“_FileProxy”?您是否希望应用程序能够读取不存在的文件?
  • @cFrozenDeath,这个的命名不是我的责任,它是我们公司框架的一部分。 “代理”类旨在避免直接调用 System.IO.File 和 System.IO.Directory 类,以便我们可以模拟它们,并且它们提供与 IO 类相同的功能。

标签: c# unit-testing rhino-mocks parallel.foreach


【解决方案1】:

我认为并行化没有问题。您的问题似乎与 Rhino Mock 的代理实例设置有关。

确保您传递给 ReadAllLines 的参数的内容与您在运行生产代码时调用它们的内容相同。

this._fileProxy.Expect(f => f.ReadAllLines(path, encoding))
        .Return(files).Repeat.Times(files.Length);

例如,如果您在不同的 path 上进行设置,并且在测试执行时该路径的值不同,您可能会看到 NULL 作为回报。但是如果没有在代码中看到完整的设置/构造函数,又很难分辨。还要检查随机生成器,看看每次都使用了什么。

以下是我整理并为我工作的内容。 工作意味着我不会为:

var lines = this._fileProxy.ReadAllLines(file, Encoding.Unicode);

//some dummy code so I can compile
public interface IProxyDir
{
    IEnumerable<string> GetFiles(string path);
}

public class GdsCallDto
{
}

public class Proxy : IProxyDir
{
    public IEnumerable<string> GetFiles(string path)
    {
        throw new NotImplementedException();
    }
}

public interface IFileDir
{
    IEnumerable<string> ReadAllLines(string path, Encoding encoding);
}

public class FileProxy : IFileDir
{
    public IEnumerable<string> ReadAllLines(string path, Encoding encoding)
    {
        throw new NotImplementedException();
    }
}

public interface IFileMgr
{
    string DeserializeFileContent(IEnumerable<string> lines, string content);
}

public class FileMgr : IFileMgr
{
    public string DeserializeFileContent(IEnumerable<string> lines, string content)
    {
        throw new NotImplementedException();
    }
}

//system under test
public class Sut
{
    private IProxyDir _directoryProxy;
    private IFileDir _fileProxy;
    private IFileMgr _fileManager;

    public Sut(IProxyDir proxyDir,  IFileDir fileProxy, IFileMgr mgr)
    {
        _fileManager = mgr;
        _directoryProxy = proxyDir;
        _fileProxy = fileProxy;
    }

    public int LoadFilesAndSaveInDatabase(string filesPath)
    {
        var calls = new ConcurrentStack<GdsCallDto>();

        var filesInDirectory = this._directoryProxy.GetFiles(filesPath);
        if (filesInDirectory.Any())
        {
            Parallel.ForEach(filesInDirectory, file =>
            {
                var lines = this._fileProxy.ReadAllLines("ssss", Encoding.Unicode);

                if (lines.Any())
                {
                    // Reads the file and setup a new DTO.
                    var deserializedCall = this._fileManager.DeserializeFileContent(lines, Path.GetFileName("file"));

                    // Insert the DTO in the database.
                    //this._gdsCallsData.InsertOrUpdateGdsCall(deserializedCall);

                    // We keep track of the dto to count the number of restored items.
                    //calls.Push(deserializedCall);
                }
            });
        }

        return 1;
    }
}

示例单元测试

[TestClass]
public class UnitTest1
{
    private IProxyDir _directoryProxy;
    private IFileDir _fileProxy;
    private IFileMgr _fileMgr;

    private Sut _sut;

    public UnitTest1()
    {
        _directoryProxy = MockRepository.GenerateMock<IProxyDir>();
        _fileProxy = MockRepository.GenerateMock<IFileDir>();
        _fileMgr = MockRepository.GenerateMock<IFileMgr>();
    }

    [TestMethod]
    public void ShouldLoadFilesAndSaveInDatabase()
    {
        // Arrange
        var path = RandomGenerator.GetRandomString(56);
        var encoding = Encoding.Unicode;
        var fileNameEnvironment = RandomGenerator.GetRandomString(5);
        var fileNameModule = RandomGenerator.GetRandomString(5);
        var fileNameRecordLocator = RandomGenerator.GetRandomString(6);
        var fileNameTimestamp = RandomGenerator.GetRandomDateTime().ToString("O").Replace(':', 'o');

        // We simulate the presence of 4 files.
        var files = new List<string>
        {
            RandomGenerator.GetRandomString(255),
            RandomGenerator.GetRandomString(255),
            RandomGenerator.GetRandomString(255),
            RandomGenerator.GetRandomString(255)
        }.ToArray();

        var expectedResult = 4;

        this._directoryProxy.Expect(d => d.GetFiles(path))
            .Return(files);

        this._fileProxy.Expect(f => f.ReadAllLines(path, encoding))
    .Return(files).Repeat.Times(files.Length);

        _sut = new Sut(_directoryProxy, _fileProxy, _fileMgr);

        // Act
        var result = this._sut.LoadFilesAndSaveInDatabase(path);

        // Assert
        Assert.AreEqual(result, expectedResult);
        this._directoryProxy.AssertWasCalled(d => d.GetFiles(path));
        this._fileProxy.AssertWasCalled(f => f.ReadAllLines(path, Encoding.Unicode));
    }

}

internal class RandomGenerator
{
    public static string GetRandomString(int number)
    {
        return "ssss";
    }

    public static DateTime GetRandomDateTime()
    {
        return new DateTime();
    }
}

【讨论】:

  • 我认为您的代码有效,因为您删除了随机生成器的“随机性”,但您的代码与我的相同。所以不幸的是它没有帮助。我可以通过对期望使用 IgnoreArguments() 方法来解决这个问题。
  • 有道理谢谢 :)
【解决方案2】:

我可以摆脱这个问题,可能是由于使用了随机值。我现在根据我的期望调用方法 IgnoreArguments():

this._fileProxy.Expect(f => f.ReadAllLines(path, encoding))
    .Return(files).Repeat.Times(files.Length).IgnoreArguments();

它成功了(即单元测试运行成功),但我不知道它是否非常优雅。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 2020-08-16
    • 2019-08-30
    • 1970-01-01
    • 2020-01-21
    • 1970-01-01
    相关资源
    最近更新 更多