shlatchz
shlatchz

Reputation: 1652

Unit testing a method that uses an external dll

I have a project named A that has a class named ClassA. ClassA has a method named ReadBlock() which creates a CloudBlockBlob object and calls one of its methods.

CloudBlockBlob is a class which is located in Microsoft.WindowsAzure.Storage.Blob namespace which is in Microsoft.WindowsAzure.Storage.dll.

My project A has a unit testing project named A.Tests. Now, I want to test method ReadBlock(). To test it, I need to mock the CloudBlockBlob object and intercept the calls to its methods, return custom values and verify that the methods were called.

UPDATE: The question is whether I can do this without modifying project A's code.

Thanks!

Upvotes: 3

Views: 2402

Answers (3)

Old Fox
Old Fox

Reputation: 8725

Without modifing class A code you won't be able to UT the ReadBlock method using Moq. You'll be able to UT this method using code weaving tools (MsFakes, Typemock Isolator, etc...)

For example(MsFakes):

[TestMethod]
public void TestMethod1()
{
    using (ShimsContext.Create())
    {
        ShimCloudBlockBlob.AllInstances.<the method you want to override>  = (<the method arguments>) => {};
    }
}

Inside the using scope you'll be able to override any method CloudBlockBlob has, through the property AllInstances.

In the next section I'm going to discuss all the other options you have...

Option 1:

    public class A
    {
        private IBlockBlob _blockBlob;

        public A(IBlockBlob blockBlob)
        {
            _blockBlob = blockBlob;
        }

        public void ReadBlock()
        {
            _blockBlob.DoSomething();
        }
    }

Since you create a new instance each time call ReadBlock(your method's current behavior) you better inject a factory instead of wrapper and DoSomething should be create; Option 2:

    public class A
    {
        private readonly IFactoryBlockBlob _blobFctory;

        public A(IFactoryBlockBlob blobFctory)
        {
            _blobFctory = blobFctory;
        }

        public void ReadBlock()
        {
           var blob =  _blobFctory.Create();
        }
    }

However, based on your question and your comments it seems that your class 'has a dependency' instead of 'needs a dependency'.

enter image description here

(Mark Siemens wrote a great book about DI, this chart was taken from his book)

With this new piece of information your method should be something like; Option 3:

    public class A
    {
        public void ReadBlock(ICloudBlob blob)
        {
        }
    }

But you don't want to change the signature of the method:

    public class A
    {

        public void ReadBlock()
        {
            ReadBlock(new CloudBlockBlob(<the params bla bla...>));
        }

        internal void ReadBlock(ICloudBlob blob)
        {
        }
    }

Add InternalsVisibleToAttribute, then verify the behavior of the internal method.

By reading between the lines, it feels to me that your class is a kind of "legacy code" meaning that it can do the job, won't change, and verifying its behavior might be a waste of time. In the past I've posted a chart (in this answer) which may help you to decide the way to handle this case.

Upvotes: 5

Eva
Eva

Reputation: 449

Disclaimer, I work in Typemock.

You can do it without modifying project A's code using Isolator. There is a simple example how it can be done:

public class Foo
{
    public void ReadBlock()
    {
        var block = new CloudBlockBlob(new Uri("http://myUrl/%2E%2E/%2E%2E"));
        var name = block.Name;
    }
}

[TestMethod, Isolated]
public void TestReadBlock()
{
    //Arrange
    var fakeBlock = Isolate.Fake.AllInstances<CloudBlockBlob>();
    Isolate.WhenCalled(() => fakeBlock.Name).WillReturn("Name");

   //Act
   var foo = new Foo();
   foo.ReadBlock();

   //Assert
   Isolate.Verify.WasCalledWithAnyArguments(() => fakeBlock.Name);
}

Hope it helps!

Upvotes: 1

Glen Thomas
Glen Thomas

Reputation: 10744

Its probably best to create a very simple mockable wrapper for CloudBlockBlob to improve your code's testability and inject it using dependency inversion.

Right now you probably have something like:

public class A
{
    public void ReadBlock()
    {
        var blockBlob = new CloudBlockBlob();
        blockBlob.DoSomething();
    }
}

Instead, inject your wrapper into A so that the dependency on CloudBlockBlob is not known to A:

public class A
{
    IBlockBlob _blockBlob

    public A(IBlockBlob blockBlob)
    {
        _blockBlob = blockBlob;
    }

    public void ReadBlock()
    {
        _blockBlob.DoSomething();
    }
}

Upvotes: 3

Related Questions