Search code examples
.netunit-testingmoqassembliesmono.cecil

How can i mock a return type of a factory method of a 3rd party library?


I am working on a tool that loads assemblies using Mono.Cecil. I have the following Factory that returns an object named binary and depends on interface responsible for loading assemblies with AssemblyDefinintion.ReadAssembly method.

I am completely new to unit testing and I need to unit test the GetBinary method.

internal sealed class DotNetBinaryFactory : IBinaryFactory
{
    private readonly IBinaryLoader binaryLoader;
    public DotNetBinaryFactory(IBinaryLoader binaryLoader)
    {
        this.binaryLoader = binaryLoader;
    }

    [NotNull] public Binary GetBinary([NotNull] FilePath path)
    {
        var assembly = binaryLoader.LoadBinary(path);
        return BinaryUtils.GetBinary(assembly);
    }
}

Here is the IBinaryLoader dependency implementation

internal interface IBinaryLoader
{
    AssemblyDefinition LoadBinary(FilePath path);
}

internal sealed class DotnetBinaryLoader : IBinaryLoader
{
    public AssemblyDefinition LoadBinary([NotNull] FilePath path)
    {
        try
        {
            return AssemblyDefinition.ReadAssembly(path.Path);
        }                
        catch (Exception)
        {
            Console.WriteLine("Exception in loading Assembly From : " + path.Path);
            throw;
        }
    }
}

So to unit test GetBinary method I need to mock IBinaryLoader and setup LoadBinary method to return a dummy AssemblyDefinition object, but I can't create one.

Here is what I tried

[Test]
public void GetDotnetBinary_AssemblyDefinitionInput_ReturnCorrectDotnetFrameworkBianry()
{
    var path = FilePath.GetRandom();
    var mockedAssemblyDef = new Mock<AssemblyDefinition>();
    var binaryLoader = new Mock<IBinaryLoader>();
    binaryLoader.Setup(m => m.LoadBinary(path)).Returns(mockedAssemblyDef.Object);
}

Solution

  • You need to create mocks of services, not of data. In your case AssemblyDefinition is data, and is sealed as well. Mocks aren't meant for concrete data classes. Your mock of the "service" binaryLoader looks ok.

    What you need to do instead is create your dummy AssemblyDefinition another way, and return that. I'm not familiar with Mono.Cecil, but it looks like there is a static Create() method you could use. Once you've got that instance you need, the rest of your test should fall into place.

    You'll end up with something like

    [Test]
    public void GetDotnetBinary_AssemblyDefinitionInput_ReturnCorrectDotnetFrameworkBianry()
    {
        // arrange
        var path = FilePath.GetRandom();
        var assemblyDef = AssemblyDefinition.Create(...); // or however your create a real one
        var binaryLoader = new Mock<IBinaryLoader>();
        binaryLoader.Setup(m => m.LoadBinary(path)).Returns(assemblyDef);
        var factory = new DotNetBinaryFactory(binaryLoader);
    
        // act
        var result = factory.GetBinary(path);
    
        // assert
        // your assertions here...
    }
    

    I see also that your types are internal. Assuming your tests are in a different assembly, you will have to make the internals visible to that assembly. This is done by putting an [InternalsVisibleTo] attribute in the source assembly.