Monday, March 28, 2016

How could we mock a non-virtual method from an external dependency.

Please choose 'View Source' in your browser to view the HTML, or File | Save to save this file to your hard drive for editing.

Introduction

Through this article, we are knowing about how we able to mock a non-virtual method with no interface implementation of an external object. To understand this article you need hands-on unit-testing experience in c# with Machine.Specifications.

Mocking is not easy always

Most often when we are writing unit test, we are stuck with some not-virtual method which could not be easily mocked. Through this article, I am trying to explain how could we achieve this by below code snippet.

Consider a situation when we want to upload a file to AWS (Amazon Web Services) S3 location,  we are using AWS SDK which exposes a TransferUtility class, a simple API for uploading content to S3.
This blocks of code should be used to upload files to S3 location and this.clientFactory.Get(request.AwsIdentifier) method return and AmazonS3 object. Here if you closely look into the uploader object, this is instantiated by TransferUtility class with a constructor which receive a signature of AmazonS3.
    public class Uploader : IUploader
    {
        public Uploader(IClientFactory clientFactory)
        {
            this.clientFactory = clientFactory;
        }
        
        private readonly IClientFactory clientFactory;
        
        public async Task<string> Upload(UploadRequest request)
        {
            using (var client = await this.clientFactory.Get(request.AwsIdentifier))
            {
                using (var uploader = new TransferUtility(client))
                {
                    if(uploader == null)
                    {
                        throw new ArgumentException("Uploader is required", "fileLocation");
                    }
                    var awsRequest = new TransferUtilityUploadRequest
                    {
                        BucketName = request.BucketName,
                        Key = path,
                        FilePath = request.FileLocation,
                     };

                    await uploader.UploadAsync(awsRequest);
                }

                return request.BucketName + request.FileLocation;
            }
        }
    }

Points of Interest

If we need to write a unit test for method uploader.UploadAsync(awsRequest) to check this method received the valid value which we provided by calling Upload(UploadRequest request).
    [Subject(typeof(Uploader))]
    public abstract class When_upload_a_file
    {
        Establish context = () =>
        {
            s3ClientFactory = Substitute.For<IClientFactory>();
            amazonS3 = Substitute.For<IAmazonS3>();

            uploader = new Uploader(s3ClientFactory);
            
            
            uploadRequest = new UploadRequest
            {
                BucketName = "example-bucket-name",
                AwsIdentifier = "12345",
                FileLocation = "abc.txt"
            };
        };
        
        Because of = async () =>
        {
            response = await uploader.Upload(uploadRequest);
        };

        
        It transferUtility_should_reveive_bucket_name_in_uploader_request =
            () => uploader.Received().Upload(Arg.Is<TransferUtilityUploadRequest>(r => r.BucketName == uploadRequest.BucketName), Arg.Any<CancellationToken>());

        
        
        protected static Uploader uploader;
        protected static IAmazonS3 amazonS3;
        protected static IClientFactory s3ClientFactory;
        
        static UploadRequest uploadRequest;
        static string response;
    }
Complexity arise here!, We could not achieve uploader proxy because uploader objects instantiated by the concrete implementation of TransferUtility class.  TransferUtility doesn't implement an interface and the method I need to mock is not virtual.  that's why we could not mock this object for calling uploader.UploadAsync(awsRequest) method.

How could we achieve this?

If we face like this situation where we are not able to change the external API class, we just need to introduce a wrapper class to avoid concrete class. Below code snippet is an interface and its implementation which have a UploadAsync method with the same signature that contains in UploadAsync method of TransferUtility class. And implemented wrapper class have a constructor which get original TransferUtility object.
 public interface IAwsTransferUtilityWrapper : IDisposable
    {
        Task UploadAsync(TransferUtilityUploadRequest request, CancellationToken cancellationToken = default(CancellationToken));
    }
    public class AwsTransferUtilityWrapper : IAwsTransferUtilityWrapper
    {
        private readonly TransferUtility originalTransferUtility;

        public AwsTransferUtilityWrapper(TransferUtility originalTransferUtility)
        {
            this.originalTransferUtility= originalTransferUtility;
        }

        public async Task UploadAsync(TransferUtilityUploadRequest request, CancellationToken cancellationToken = new CancellationToken())
        {
            await this.originalTransferUtility.UploadAsync(request, cancellationToken);
        }

        public void Dispose()
        {
            this.originalTransferUtility.Dispose();
        }
    }
}

We need to change the instantiation of uploader object in Uploader class as below code snippet
    public class Uploader : IUploader
    {
        public Uploader(IClientFactory clientFactory)
        {
            this.clientFactory = clientFactory;
        }
        
        private readonly IClientFactory clientFactory;
        public static  Func<IAmazonS3, IAwsTransferUtilityWrapper> AwsTransferUtilityFactory { get; set; } = client => new AwsTransferUtilityWrapper(new TransferUtility(client));
        
        public async Task<string> Upload(UploadRequest request)
        {
            using (var client = await this.clientFactory.Get(request.AwsIdentifier))
            {
                using (var uploader = AwsTransferUtilityFactory(client))
                {
                    if(uploader == null)
                    {
                        throw new ArgumentException("Uploader is required", "fileLocation");
                    }
                    var awsRequest = new TransferUtilityUploadRequest
                    {
                        BucketName = request.BucketName,
                        Key = path,
                        FilePath = request.FileLocation
                    };

                    await uploader.UploadAsync(awsRequest);
                }

                return request.BucketName + request.FileLocation;
            }
        }
    }
Now we can test our uploader.UploadAsync(awsRequest) method as like below unit test.
    [Subject(typeof(Uploader))]
    public abstract class When_upload_a_file
    {
        Establish context = () =>
        {
            originalTransferUtilityFactory = Uploader.AwsTransferUtilityFactory;
            transferUtility = Substitute.For<ITransferUtilityWrapper>();
            
            s3ClientFactory = Substitute.For<IClientFactory>();
            amazonS3 = Substitute.For<IAmazonS3>();

            Uploader.AwsTransferUtilityFactory = _ => transferUtility;
            uploader = new Uploader(s3ClientFactory);
            
            
            uploadRequest = new UploadRequest
            {
                BucketName = "example-bucket-name",
                AwsIdentifier = "12345",
                FileLocation = "abc.txt"
            };
        };
        
        Because of = async () =>
        {
            response = await uploader.Upload(uploadRequest);
        };

        
        It transferUtility_should_reveive_bucket_name_in_uploader_request =
            () => uploader.Received().UploadAsync(Arg.Is<TransferUtilityUploadRequest>(r => r.BucketName == uploadRequest.BucketName), Arg.Any<CancellationToken>());

        
        
        protected static Uploader uploader;
        protected static IAmazonS3 amazonS3;
        protected static IClientFactory s3ClientFactory;
        protected static IAwsTransferUtilityWrapper transferUtility;
        static Func<IAmazonS3, IAwsTransferUtilityWrapper> originalTransferUtilityFactory;
        
        static UploadRequest uploadRequest;
        static string response;
    }
Now we are able to  mock a method of external API class by imploementing a wrapper class 'AwsTransferUtilityWrapper'.

A Deep Dive into Computed Columns in Entity Framework Core

Entity Framework Core (EF Core) is a popular Object-Relational Mapping (ORM) framework that simplifies database access for .NET applications...