Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock method in Parallel.ForEach always returns null

I have the following code:

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

And I have the following unit test:

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

The problem is on the following line:

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

Even though I set an expectation and a return value, when I run the unit test, it always returns null.

I am using Rhino.Mocks, it works perfectly fine elsewhere but not there.

I had a look at some discussions here but none of them helped. Can it be due to the use of Parallel.ForEach? Is there a way of doing such a mock?

If you need any other info, please let me know.

like image 285
DotNetMatt Avatar asked Jun 25 '26 12:06

DotNetMatt


2 Answers

I don't think there is a problem with the Parallelization. It seems your problem is related to the proxy instance setup with Rhino Mock.

Ensure what you pass into the parameters of the ReadAllLines are the same as you call them when it run through the production code.

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

For instance if you setup on different path and the value of that path diefferent when test executes you may see NULL in return. But again hard to tell without seeing the full setup/constructor in the code. Also check the random generators see what has been used in each time.

The below is I sort of put together and working for me. Working means I don't get NULL for:

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

Sample Unit Test

[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();
    }
}
like image 161
Spock Avatar answered Jun 27 '26 02:06

Spock


I could get rid of this issue, probably caused by the use of random values. I am now calling the method IgnoreArguments() on my expectation:

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

It does the trick (i.e. the unit test is running successfully), but I do not know if it is very elegant.

like image 22
DotNetMatt Avatar answered Jun 27 '26 00:06

DotNetMatt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!