Friday, August 25, 2017

Mocking extension methods

I know it's been a while since I've blogged about anything, reason for this is that I've taken on the challenge of adjusting to the .NET Core world. And I must say it's a nice world to be in, and sometimes I find it quite hard to go back to the ol' MVC.

Getting back to the reason for this blogpost, which is around how to mock extension methods in your unit tests, let's consider the following example (please don't judge on the "code quality", this is for test purpose only).

Controller:

    public class HomeController : Controller
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private ISession _session => _httpContextAccessor.HttpContext.Session;
        public HomeController(IHttpContextAccessor httpContext)
        {
            _httpContextAccessor = httpContext;
            _session.SetString("test", "test");
        }
        public IActionResult Index()
        {
            ViewData["Session"] = _session.GetString("test");
            return View();
        }
    }
Unit test:

    public class UnitTest1
    {
        Mock<IHttpContextAccessor> _mockHttp = null;
        Mock<ISession> _mockSession = null;
        HomeController _sutController = null;
        public UnitTest1()
        {
            _mockHttp = new Mock<IHttpContextAccessor>();
            _mockSession = new Mock<ISession>();
            _mockHttp.Setup(x => x.HttpContext.Session).Returns(_mockSession.Object);
            _sutController = new HomeController(_mockHttp.Object);
        }
        [Fact]
        public void Test1()
        {
            _mockSession.Setup(x => x.GetString(It.IsAny<string>())).Returns(string.Empty).Verifiable();
            var result = _sutController.Index();
            Assert.IsType<ViewResult>(result);
            _mockSession.VerifyAll();
        }

Pretty standard and simple no? You have a controller that receives the IHttpContextAccessor instance (thank you .NET Core), which is a wrapper on top of ... ahem ... HttpContext. The Index action will just put in the ViewData whatever we have in the Session under the "test" key (which we add in the constructor but nevermind that ... i'm trying to make a point).

In the unit tests we mock the IHttpContextAccessor type and when the Session property is called we tell it to return another mocked ISession instance. And in our amazing test we just call the Index action and test that the GetString method from Session is called and that the controller generates a ViewResult.

Again, simple, standard and the outcome quite obvious:


Not really what you'd expect, and this brings us to the topic from the title of the blogpost. 

I've done a bit of digging around and realised that even if at compile time the method GetString is there, since it's an extension method, at runtime the mocking framework will have issues trying to mock it since it doesn't belong to the ISession type.

So what would the solution be?

Well, after a few good hours spent on a search engine (Google and Stackoverflow) was still stuck. However thanks to a suggestion from a colleague of mine, I've started digging up in the .net core code base for the extension method, to see the implementation.

So in this case, GetString (extension method) calls Get (extension method) that calls the TryGetValue which belongs to the ISession interface. So why not mock that and hope for the best?

Updated unit test:

        [Fact]
        public void Test1()
        {
            byte[] sessionValue = Encoding.UTF8.GetBytes("test");
            _mockSession.Setup(x => x.TryGetValue(It.IsAny<string>(), out sessionValue)).Returns(true).Verifiable();
            var result = _sutController.Index();
            Assert.IsType<ViewResult>(result);
            _mockSession.VerifyAll();
        }

And the outcome is the magic green tick.

So the takeaway from this is that if you want to mock extension methods, don't try it, it won't work anyway. Instead mock the method belonging to the type that the extension method calls.


Monday, October 10, 2016

Simulating load balancing on local environment

This has been on my mind for quite a while. I mean, often I found myself unable to recreate issues seen on the live site and more often than not, the root cause is that the live site sits on a scalable web farm, whilst on my local machine the site runs on 1 instance in IIS.

For those that don't know what web farms include here's my 2 pence on that.


So we've got Sir User that types-in your site's URL (and going past the bits and bobs that happen behind the scenes for the request to hit your infrastructure). If you've got a horizontally scaled environment (aka web farm), sitting in front of your servers (apps...whatever) you'll most likely have a load balancer (or similar infrastructure) that will decide which of the instances available in the pool will deal with the request.

This sounds simple enough, but consider that loading a page usually means more than 1 request, since you have to download images, css, js and other dependencies. Furthermore, what if the load balancer will send requests from the same user to different instances?

In order for developers to simulate what happens in the live environment, they'll need to simulate load balancing. Sure, not the whole logic and benefits that a load balancer has, but at least simple routing to different instances.

After quite a bit of investigation I came across an interesting post on Code Project that discussed the 2 options I want to highlight here.

Use IIS Max Worker Process

This option is built-in IIS, and you can use it straight away. All you have to do is go to the application pool settings, and increase the Maximum Worker Process number (i.e. 2).

What IIS will do, is spin up to w3wp processes running under the same user (based on application pool settings) both replying to requests.

The tricky part in here is proof that IIS indeed does reply using both worker processes.

To do this, it's quite easy. In the Application_PreSendRequestHeaders event (in your Global.asax) just use this piece of code:

protected void Application_PreSendRequestHeaders()
{
#if DEBUG
            Response.AddHeader("PID", System.Diagnostics.Process.GetCurrentProcess().Id.ToString());
#endif
}

What this does is to append an HTTP header in your response with the current process ID, but only in DEBUG mode.

Using Fiddler

For those who don't know this, Fiddler is a nifty little tool that can do a lot of stuff. Although the official page says it's a "web debugging proxy", that last word (proxy) means that you can do quite a few things with it, like:
- capture all the traffic between your browser and a server
- replay requests and yes, you can tamper with them - anyone thinking AppSec testing?
- set as your website's default proxy and all of the sudden you can capture traffic from the server side to any API (for example)
- filter - this is a biggie; basically you can instruct Fiddler to only show the traffic with a specific list of host names

All these features are out of the box, but in order for Fiddler to fit into the scope of this post (Load Balancing simulation) these are the steps you need to take:
- create a new site in IIS pointing to the same codebase but with a different app pool
- give your new website a new host name (i.e. your-host-name2)
- create a simple class library project
- add reference to Fiddler.exe (yes, the actual exe file - you can find this in Program Files or where else Fiddler is installed)
- in your project add the following piece of code

[assembly: RequiredVersion("2.2.8.6")] 
namespace FiddlerLoadBalancer
{
    public class LoadBalancer : IAutoTamper
    {
        private string[] _hosts;
        private Random _rnd;
 
        public void OnLoad()
        {
            _rnd = new Random((int)DateTime.UtcNow.Ticks);
            _hosts = new[] { "your-host-name:443", "your-host-name2:443" };
        }
 
        public void AutoTamperRequestBefore(Session oSession)
        {
            if (_hosts != null && (oSession.host == "your-host-name" || oSession.host == "your-host-name:80"))
            {
                oSession.host = _hosts[_rnd.Next(0, _hosts.Length)];
            }
        }
    }
}

- build and copy the DLL into c:\Users\<your_username>\Documents\Fiddler2\scripts

What will happen is that as long as Fiddler is running and your type in your browser any URL that contains "your-host-name" as domain name, Fiddler will randomly send the request to one of the host names in the list.

A bonus in the code above (if you haven't spotted it already) is the enforcement of port 443 (aka HTTPS), an easy way to see how your app will work only under HTTPS. For this you need your IIS websites to have HTTPS bindings (dooh).

Conclusion

I've tried both versions and the one I prefer most is the Fiddler approach. Yes, it's not easy because you have to write some code and do a duplicate of your website, but once set up, you can see in fiddler for 1 page request all the subsequent requests (images, CSS, JS) going randomly to either website for handling.

The IIS approach works fine, but as far as I could see, once a request is handled by a specific w3wp instance, any subsequent requests will be sent to the same process.