3

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.

  • How can I mock an object that is fully created inside a method?
  • Can I somehow change project A's dll reference and reference it to a mock dll that creates a mock object instead the real one?
  • Can I override project A's call for classes inside the Microsoft.WindowsAzure.Storage.Blob with an implementation of my own in A.Tests class?

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

Thanks!

Old Fox
  • 8,629
  • 4
  • 34
  • 52
shlatchz
  • 1,612
  • 1
  • 18
  • 40
  • Probably best to create a very simple mockable wrapper for CloudBlockBlob to improve your code's testability and inject it using dependency inversion. – Glen Thomas Mar 28 '16 at 14:23
  • 2
    The question is whether I can do this without modifying project **A**'s code? I know that one option is to inject ICloudBlob from outside. – shlatchz Mar 28 '16 at 14:29
  • Ah ok, you didn't specify that in the question.. – Glen Thomas Mar 28 '16 at 14:30

3 Answers3

5

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.

Mike Lowery
  • 2,630
  • 4
  • 34
  • 44
Old Fox
  • 8,629
  • 4
  • 34
  • 52
3

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();
    }
}
Glen Thomas
  • 10,190
  • 5
  • 33
  • 65
  • 2
    The question is whether I can do this without modifying project **A**'s code? – shlatchz Mar 28 '16 at 14:31
  • I don't think there is any way that you could achieve that. If you are unable to modify A's code then why would you need to create tests for it? – Glen Thomas Mar 28 '16 at 14:39
  • 2
    I just don't want project **A**'s code to be twice as complicated just to be able to test something. There's no other use for that injection other than for the testing and it complicated the code more than it should be. – shlatchz Mar 28 '16 at 14:57
  • Dependency inversion also greatly increases code readability and maintainability. If your testing is important then the DI is worth paying the cost – Glen Thomas Mar 28 '16 at 15:04
  • @GlenThomas Very helpful, Thanks. – Igliv Apr 06 '16 at 10:43
1

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!

Eva
  • 449
  • 2
  • 8