DevelopmentUsing a Shim to mockup static and extension methods

For our C# unit testing, we generally use a mocking framework called Moq which makes it easy to create mock classes for injecting behaviour and verifying calls. But this only works for public instance methods and won’t work for anything static. Extension methods must be static methods within static classes, so what happens when you want to unit test an extension method? In the past I’ve written many wrapper functions which call the extension methods, and while that approach does work, there is a better way: a shim.

What is a shim?

A shim is a piece of code that modifies the compiled code of your application at run time, so that instead of making a specified method call, it runs the shim code that your test provides. In this way you can replace static methods, or even methods in assemblies you cannot modify, like those built in to .NET. A great example of something you can do, is shimming the DateTime.Now property to always return a specified value, rather than it being different every time you call it. Shims are part of “Fakes Assemblies” which is a feature of Visual Studio 2012 and later.

Okay, so how do we shim an extension method?

In your test project, expand the references in the solution explorer and find the reference which contains the thing you want to override. If you want to override a built in .NET function, you can look up the function on MSDN to figure out which assembly it is contained in. Once you’ve found the assembly, right click on it in Visual Studio and select “Add Fakes Assembly”

Add Fakes Assembly” src=”http://dev.aerion.com.au/wp-content/uploads/2017/12/Clipboarder.2017.12.11-300×144.png” alt=”Right-Click > Add Fakes Assembly” width=”300″ height=”144″> Right-Click > Add Fakes Assembly

In your test code, you will need to wrap your test implementation in a using(ShimsContext.Create()){ } block. To call a shim of a method, you will need to callFakes.ShimClassName.Function()(where the Fakes.Shim is the part that is appended to the original type name). Rather than taking in the usual parameters, this will take in a Func with the same parameters and return type as the original function. This Func allows you to implement whatever logic and return whatever you wish, just like mocking up a function with Moq or similar. As long as the testing call is done within the ShimsContext, then when the function is called, it will call your shim.

Keep in mind that if you’re mocking up an extension function, you must override the static function in the assembly it resides in, which is usually not the assembly original class is in. Also, if you wish to mock up a property, the function name will be along the lines of PropertyNameGet or PropertyNameSet. There’s a nice example of using a shim to mock up DateTime.Now below:

[TestClass]  
public class TestClass1  
{   
        [TestMethod]  
        public void TestCurrentYear()  
        {  
            int fixedYear = 2000;  

            // Shims can be used only in a ShimsContext:  
            using (ShimsContext.Create())  
            {  
              // Arrange:  
                // Shim DateTime.Now to return a fixed date:  
                System.Fakes.ShimDateTime.NowGet =   
                () =>  
                { return new DateTime(fixedYear, 1, 1); };  

                // Instantiate the component under test:  
                var componentUnderTest = new MyComponent();  

              // Act:  
                int year = componentUnderTest.GetTheCurrentYear();  

              // Assert:   
                // This will always be true if the component is working:  
                Assert.AreEqual(fixedYear, year);  
            }  
        }  
}

 

 

https://aeriontech.wpenginepowered.com/wp-content/uploads/2021/03/Aerion-Logo-Vector-1_583-1.png
Connect with us

Subscribe to our newsletter today to receive updates on the latest news, releases and special offers. We respect your privacy. Your information is safe.

©2023 Aerion Technologies. All rights reserved | Terms of Service | Privacy Policy