Showing posts with label Mock. Show all posts
Showing posts with label Mock. Show all posts

Tuesday, September 11, 2012

Dual-invocation using Autofac interception - AOP


- The project I've been working on used to have SQL as its primary datastore. We're implementing another data store using MongoDB as it will replace the SQL one soon. During the migration process, we have 2 versions of the website running parallel. The production version is using SQL which is viewed by customers and the other one using MongoDB which is being tested by our staff. So once the Mongo version development is finished, it will be kept running in parallel with the SQL version until we're completely confident that all the features are working fine. During that time, any changes made by users to the SQL db have to be applied to MongoDB.

- There're quite a few solutions in my mind. However, we've had some sort of DataAccess dlls to access Sql database before and another implementation of same interfaces which talk to MongoDB have been implemented. So the options is narrowed down to something like an adapter pattern which takes 2 instances of the same interface, invoke method on the first one and then invoke the same method on the second instance. Being a good fan of Composition over Inheritance, I was thinking of something like:
public class DualService<T> : T
{
    private readonly T _primary;
    private readonly T _secondary;
    
    public DualService(T primary, T secondary)    
    {
        _primary = primary;
        _secondary = secondary;    
    }
    
    public void MethodA()
    {
        _primary.MethodA();
        _secondary.MethodA();    
    }
    
    // So on
}

- Well, this approach is good only if the generic T interface has just a few methods. Ours has over 30 methods and it keeps counting ;), it's implemented by the "Outsource team" So I dropped this option, and thought about the Autofac Interceptor. The intercepter I'm gonna implement will take the secondary implementation and invoke the same method as the invocation if:

  1. The method is not decorated by IgnoreInterceptAttribute
  2. The method has a result type void. (Well, a command should not have return type here if you're curios about the reason)
  3. The method is decorated by DualInvocationAttribute

- Here is the implementation. This approach requires Castle.Core and AutofacContrib.DynamicProxy2. The 2 attributes are just simple attributes I created to enhance the filter:
[AttributeUsage(AttributeTargets.Method)]
public class IgnoreInterceptAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Method)]
public class DualInvocationAttribute : Attribute
{
}

public class DualInvocation<T> : IInterceptor where T: class 
{
    private readonly T _secondaryImplementation;
    private readonly Action<Exception> _onSecondaryImplementationInvokeError;

    public DualInvocation(T secondaryImplementation, Action<Exception> onSecondaryImplementationInvokeError = null)
    {
        _secondaryImplementation = secondaryImplementation;
        _onSecondaryImplementationInvokeError = onSecondaryImplementationInvokeError ?? (ex => Trace.WriteLine(ex));
    }

    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
        var returnType = invocation.Method.ReturnType;
        var att = invocation.Method.GetCustomAttributes(typeof(IgnoreInterceptAttribute), true);
        if (att.Any())
        {
            return;
        }

        if (returnType != typeof(void))
        {
            att = invocation.Method.GetCustomAttributes(typeof(DualInvocationAttribute), true);
            if (!att.Any())
            {
                return;
            }
        }

        var methodInfo = invocation.Method;
        try
        {
            methodInfo.Invoke(_secondaryImplementation, invocation.Arguments);
        }
        catch (Exception ex)
        {
            _onSecondaryImplementationInvokeError(ex);
        }
    }
}

- You might wonder about how to use this class, take a look at the unit tests. You should probably do something either in code or autofac xml config, similar to what I did in the SetUp:
- Here is a dummy interface to demonstrate the Autofac registrations
public interface ISomeService
{
    void Update();

    void Update(int userId);

    [IgnoreIntercept]
    void Update(int userId, bool multi);

    int GetUser(int userId);

    [DualInvocation]
    bool DeleteUser(int userId);
}

- And here is the tests, the tests are using NSubstitute to mock the interface. (Pretty similar to Moq, etc)
[TestFixture]
public class MethodIntercept
{
    private ISomeService primary,
                         secondary;

    private IContainer container;
    private Action<Exception> exceptionHandler;

    [SetUp]
    public void RegisterAutofacComponents()
    {
        primary = Substitute.For<ISomeService>();
        secondary = Substitute.For<ISomeService>();

        var builder = new ContainerBuilder();

        builder.RegisterInstance(secondary).Named<ISomeService>("secondary").SingleInstance();

        builder.RegisterInstance(primary)
               .Named<ISomeService>("primary").SingleInstance()
               .EnableInterfaceInterceptors()
               .InterceptedBy(typeof (DualInvocation<ISomeService>));

        builder.RegisterType<DualInvocation<ISomeService>>()
               .WithParameters(new Parameter[]
               {
                   new ResolvedParameter((p, c) => p.Name == "secondaryImplementation", (p, c) => c.ResolveNamed("secondary", typeof (ISomeService))),
                   new NamedParameter("exceptionHandler", exceptionHandler)
               })
               .AsSelf()
               .InstancePerLifetimeScope();

        container = builder.Build();
    }

    [Test]
    public void Should_invoke_secondary_method_if_it_is_not_ignored()
    {
        // Arrange
        var primaryService = container.ResolveNamed<ISomeService>("primary");
        var secondaryService = container.ResolveNamed<ISomeService>("secondary");

        // Action
        primaryService.Update();

        // Assert
        secondaryService.Received(1).Update();
    }

    [Test]
    public void Should_invoke_secondary_overload_method_if_it_is_not_ignored()
    {
        // Arrange
        var primaryService = container.ResolveNamed<ISomeService>("primary");
        var secondaryService = container.ResolveNamed<ISomeService>("secondary");

        // Action
        primaryService.Update(1);

        // Assert
        secondaryService.Received(1).Update(1);
    }

    [Test]
    public void Should_invoke_secondary_overload_method_if_it_is_decorated_by_DualInvocationAttribute()
    {
        // Arrange
        var primaryService = container.ResolveNamed<ISomeService>("primary");
        var secondaryService = container.ResolveNamed<ISomeService>("secondary");

        // Action
        primaryService.DeleteUser(1);

        // Assert
        secondaryService.Received(1).DeleteUser(1);
    }

    [Test]
    public void Should_not_invoke_secondary_method_if_it_is_not_void()
    {
        // Arrange
        var primaryService = container.ResolveNamed<ISomeService>("primary");
        var secondaryService = container.ResolveNamed<ISomeService>("secondary");

        // Action
        primaryService.GetUser(1);

        // Assert
        secondaryService.DidNotReceive().GetUser(Arg.Any<int>());
    }

    [Test]
    public void Should_not_invoke_secondary_method_if_it_is_ignored()
    {
        // Arrange
        var primaryService = container.ResolveNamed<ISomeService>("primary");
        var secondaryService = container.ResolveNamed<ISomeService>("secondary");

        // Action
        primaryService.Update(1, true);

        // Assert
        secondaryService.DidNotReceive().Update(Arg.Any<int>(), Arg.Any<bool>());
    }

    [Test]
    public void Should_not_throw_exception_if_cannot_invoke_secondary_method()
    {
        // Arrange
        exceptionHandler = ex => Trace.WriteLine(ex);
        secondary.When(x => x.Update()).Do(callInfo => { throw new Exception(); });

        var primaryService = container.ResolveNamed<ISomeService>("primary");
        var secondaryService = container.ResolveNamed<ISomeService>("secondary");

        // Action
        primaryService.Update();

        // Assert
        secondaryService.Received(1).Update();
    }
}


- I reckon this approach is clean, simple and It works on my machine . Cheers.

Wednesday, June 13, 2012

How I Mock Mongo Collection?


- I have been working with Mongo quite alot recently and writing my service classes using mongo without testing is such a pain in the neck. So today, I'll post about how I mock mongo collection for my unit testings.
- Given that I have a ReminderService like below:
public class ReminderService
{
    private readonly MongoCollection<User> _mongoCollection;
    private readonly INotificationService _notificationService;

    public ReminderService(MongoCollection<User> mongoCollection, INotificationService notificationService)
    {
        _mongoCollection = mongoCollection;
        _notificationService = notificationService;
    }

    public void Send()
    {
        var notActivatedUsers = _mongoCollection.FindAs<User>(Query.EQ("activated", false)).ToList();
        notActivatedUsers.ForEach(user => _notificationService.Send(new ActivationReminder(user)));
    }
}

- It has 1 method Send which needs to be unit tested. This method will find all users who have not their activated account and then send reminder emails to them. This service class is oviously designed for unit testing as it has 2 dependencies: a mongo collection and an interface of INotificationService. To mock the interface, it's straightforward , isn't it? So our job today is mocking this mongo collection.

- Before mocking a class like mongo collection, I often check whether it has virtual methods because the thing we need to mock here is the method FindAs. By pressing F12 on the MongoCollection class to go to assembly metadata, we can easily see that this method is virtual and return a MongoCursor.
// Summary:
//     Returns a cursor that can be used to find all documents in this collection
//     that match the query as TDocuments.
//
// Parameters:
//   query:
//     The query (usually a QueryDocument or constructed using the Query builder).
//
// Type parameters:
//   TDocument:
//     The type to deserialize the documents as.
//
// Returns:
//     A MongoDB.Driver.MongoCursor<TDocument>.
public virtual MongoCursor<TDocument> FindAs<TDocument>(IMongoQuery query);

- Hmm, it's getting more complicated here because this method does not return an enumerable or collection but a MongoCursor<TDocument> and that type inherits from IEnumerable<TDocument>. So it's telling me that when we call mongoCursor.ToList(), behind the scene, it will call GetEnumerator(). And certainly, checking class MongoCursor, I see that it's methods are all virtual so it's really open for unit testing.

- To wrap up, the idea will be creating a mock cursor to return expected items and let it be the result of method FindAs on our mock collection. There are other things to note here: mocking a class is not similar to mocking an interface since it has constructor. That means we have to provide the constructor parameters to create a mock object of MongoCollection class. The parameters here are MongoDatabase and MongoCollectionSettings<TDefaultDocument>:
// Summary:
//     Creates a new instance of MongoCollection. Normally you would call one of
//     the indexers or GetCollection methods of MongoDatabase instead.
//
// Parameters:
//   database:
//     The database that contains this collection.
//
//   settings:
//     The settings to use to access this collection.
public MongoCollection(MongoDatabase database, MongoCollectionSettings<TDefaultDocument> settings);

- To have a MongoDatabase we need MongoServer and MongoDatabaseSettings and so on. So honestly it's not really simple. I don't want my unit test to talk to real mongo server while running so I must ensure everything runs in memory. I had to create a TestHelper class for these thing, ofcourse for reuse reason:
public static class MongoTestHelpers
{
    private static readonly MongoServerSettings ServerSettings;
    private static readonly MongoServer Server;
    private static readonly MongoDatabaseSettings DatabaseSettings;
    private static readonly MongoDatabase Database;

    static MongoTestHelpers()
    {
        ServerSettings = new MongoServerSettings
        {
            Servers = new List<MongoServerAddress>
            {
                new MongoServerAddress("unittest")
            }
        };
        Server = new MongoServer(ServerSettings);
        DatabaseSettings = new MongoDatabaseSettings("databaseName", new MongoCredentials("", ""), GuidRepresentation.Standard, SafeMode.True, true);
        Database = new MongoDatabase(Server, DatabaseSettings);
    }

    /// <summary>
    /// Creates a mock of mongo collection
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    /// <remarks></remarks>
    public static MongoCollection<T> CreateMockCollection<T>()
    {
        var collectionSetting = new MongoCollectionSettings<T>(Database, typeof(T).Name);
        var m = Substitute.For<MongoCollection<T>>(Database, collectionSetting);
        m.Database.Returns(Database);
        m.Settings.Returns(collectionSetting);
        return m;
    }

    public static MongoCollection<T> ReturnsCollection<T>(this MongoCollection<T> collection, IEnumerable<T> enumerable)
    {
        var cursor = Substitute.For<MongoCursor<T>>(collection, Substitute.For<IMongoQuery>());
        cursor.GetEnumerator().Returns(enumerable.GetEnumerator());
        cursor.When(x => x.GetEnumerator())
              .Do(callInfo => enumerable.GetEnumerator().Reset());// Reset Enumerator, incase the method is called multiple times

        cursor.SetSortOrder(Arg.Any<IMongoSortBy>()).Returns(cursor);
        cursor.SetLimit(Arg.Any<int>()).Returns(cursor);
        cursor.SetFields(Arg.Any<IMongoFields>()).Returns(cursor);
        cursor.SetFields(Arg.Any<string[]>()).Returns(cursor);
        cursor.SetFields(Arg.Any<string>()).Returns(cursor);
        cursor.SetSkip(Arg.Any<int>()).Returns(cursor);

        collection.Find(Arg.Any<IMongoQuery>()).Returns(cursor);
        collection.FindAs<T>(Arg.Any<IMongoQuery>()).Returns(cursor);
        collection.FindAll().Returns(cursor);
        // You properly need to setup more methods of cursor here
        
        return collection;
    }
}

- There are some interesting points here:
  1. I'm using NSubstitute, hope you understand the syntax. I think the syntax is similar to other frameworks like Moq so it's not a problem if you use different library.
  2. The code will never talk to real database as I used "unittest" as server address.
  3. Method ReturnsCollection is an extension method of MongoCollection<T>. We'll use it to specify the items we simulate the result from mongo query. In practice, we might use fluent syntax on mongo cursor object such as SetSortOrder, SetLimit, etc so we definitely don't want these things alter our result. That's why you see there're alot of lines to mock the result for those methods to return the same cursor.

- So with this helper, the unit test will look like this:
[TestFixture]
public class ReminderServiceTests
{
    [Test]
    public void Send_should_send_notification_to_unactivated_users()
    {
        // Arrange
        var mockCollection = MongoTestHelpers.CreateMockCollection<User>();
        mockCollection.ReturnsCollection(new List<User>
        {
            new User {IsActivated = false}, 
            new User {IsActivated = false}
        });
        var notificationService = Substitute.For<INotificationService>();
        var service = new ReminderService(mockCollection, notificationService);

        // Action
        service.Send();

        // Assert
        notificationService.Received(2).Send(Arg.Any<ActivationReminder>());
    }
}

- Well, The purpose of this test is guaranteeing that the notification service will send reminders to all unactivated users from database. Because I'm using mongo so the final thing we want to mock is the result set returned by Mongo, we're not testing mongo query here. Hope this help.


Cheers.

Thursday, July 28, 2011

Unit Test DisplayFormat attribute HtmlHelper

    Previously, I wrote about how to mock a ViewEngine for one of my unit tests. Today, I came across a question on StackOverflow asking about how to unit test the out put of DataAnnotation attribute DisplayFormat. I'm very interested in Unit Test so I decide to find the answer, another reason is to increase my reputation on StackOverflow :D. Honestly, I don't exactly know the reason why to test the output because for me, we don't need to test code that was not written by us. Anyways, this is a interesting question for me.     Let's say we have a ViewModel using DisplayFormat like below:
public class UserViewModel
{        
    [DisplayFormat(DataFormatString = "{0:dd/MM/yy}")]
    public DateTime Birthday { get; set; }
}
    And here is the basic unit test to verify the output
[Test]
public void Test_output_display_format_for_Birthday_property()
{
    // Arrange
    var _model = new UserViewModel {Birthday = DateTime.Parse("28/07/11") };
    var helper = MvcTestControllerBuilder.GetHtmlHelper<UserViewModel>();
    helper.ViewData.Model = _model;
    
    // Action
    var result = helper.DisplayFor(x => x.Birthday);
    
    // Assert
    Assert.That(result.ToHtmlString(), Is.EqualTo("28/07/11"));
}
    Apparently, this test failed like the description in the StackOverflow question. I decided to debug the MVC source code and found 2 reasons: + First, the MVC framework will find the ActionCacheItem using method GetActionCache:
internal static Dictionary<string, ActionCacheItem> GetActionCache(HtmlHelper html)
{
    HttpContextBase httpContext = html.ViewContext.HttpContext;
    if (!httpContext.Items.Contains(cacheItemId))
    {
        Dictionary<string, ActionCacheItem> dictionary = new Dictionary<string, ActionCacheItem>();
        httpContext.Items[cacheItemId] = dictionary;
        return dictionary;
    }
    return (Dictionary<string, ActionCacheItem>) httpContext.Items[cacheItemId];
}
It'll try to find the cache item in httpContext.Items but the Items is null. So the first thing we need to mock the value for httpContext.Items:
var helper = MvcTestControllerBuilder.GetHtmlHelper<UserViewModel>();
helper.ViewContext.HttpContext.Setup(x => x.Items).Returns(new Dictionary<string, object>());
+ Secondly, the MVC framework will try to find the display template for "DateTime" and "String". Obviously we don't have those stuff in the Unit test environment. The code is located in TemplateHelpers.cs:
internal static string ExecuteTemplate(HtmlHelper html, ViewDataDictionary viewData, string templateName, DataBoundControlMode mode, GetViewNamesDelegate getViewNames, GetDefaultActionsDelegate getDefaultActions)
{
    Dictionary<string, ActionCacheItem> actionCache = GetActionCache(html);
    Dictionary<string, Func<HtmlHelper, string>> dictionary2 = getDefaultActions(mode);
    string str = modeViewPaths[mode];
    foreach (string str2 in getViewNames(viewData.ModelMetadata, new string[] { templateName, viewData.ModelMetadata.TemplateHint, viewData.ModelMetadata.DataTypeName }))
    {
        ActionCacheItem item;
        Func<HtmlHelper, string> func;
        string key = str + "/" + str2;
        if (actionCache.TryGetValue(key, out item))
        {
            if (item != null)
            {
                return item.Execute(html, viewData);
            }
            continue;
        }
        ViewEngineResult result = ViewEngines.Engines.FindPartialView(html.ViewContext, key);
        if (result.View != null)
        {
            ActionCacheViewItem item2 = new ActionCacheViewItem {
                ViewName = key
            };
            actionCache[key] = item2;
            using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
            {
                result.View.Render(new ViewContext(html.ViewContext, result.View, viewData, html.ViewContext.TempData, writer), writer);
                return writer.ToString();
            }
        }
        if (dictionary2.TryGetValue(str2, out func))
        {
            ActionCacheCodeItem item3 = new ActionCacheCodeItem {
                Action = func
            };
            actionCache[key] = item3;
            return func(MakeHtmlHelper(html, viewData));
        }
        actionCache[key] = null;
    }
    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.TemplateHelpers_NoTemplate, new object[] { viewData.ModelMetadata.RealModelType.FullName }));
}
    So the main thing we need to do is somehow mock the result at line "19" to make the view engine return a ViewEngineResult that has View property equals to null. I make a helper method similar to this post to do this:
public static ViewEngineResult SetupNullViewFor(string viewName)
{
    Mock<IViewEngine> mockedViewEngine = GetCurrentMockViewEngine() ?? new Mock<IViewEngine>();
    var viewEngineResult = new ViewEngineResult(new List<string>());

    mockedViewEngine.Setup(x => x.FindPartialView(It.IsAny<ControllerContext>(), viewName, It.IsAny<bool>()))
                    .Returns(viewEngineResult);
    mockedViewEngine.Setup(x => x.FindView(It.IsAny<ControllerContext>(), viewName, It.IsAny<string>(), It.IsAny<bool>()))
                    .Returns(viewEngineResult);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(mockedViewEngine.Object);
    return viewEngineResult;
}

private static Mock<IViewEngine> GetCurrentMockViewEngine()
{
    foreach(var v in ViewEngines.Engines)
    {
        try
        {
            return Mock.Get<IViewEngine>(v);
        }
        catch (Exception)
        {
        }
    }
    return null;
}
    With all of these stuff, the final unit test would look like:
[Test]
public void Test_output_display_format_for_Birthday_property()
{
    // Arrange    
    MvcTestFixtureHelper.SetupNullViewFor("DisplayTemplates/DateTime");
    MvcTestFixtureHelper.SetupNullViewFor("DisplayTemplates/String");
    
    var _model = new UserViewModel {Birthday = DateTime.Parse("28/07/11")};
    var helper = MvcTestControllerBuilder.GetHtmlHelper<UserViewModel>();
    helper.ViewContext.HttpContext.Setup(x => x.Items).Returns(new Dictionary<string, object>());
    helper.ViewData.Model = _model;
    
    // Action
    var result = helper.DisplayFor(x => x.Birthday);
    
    // Assert
    Assert.That(result.ToHtmlString(), Is.EqualTo("28/07/11"));
}
You can refer to my similar posts about MvcTestControllerBuilder (this class has some methods to mock HtmlHelper) and How to mock the ViewEngine. In those posts, I used NSubstitue but now I changed to use Moq, they're quite similar :D. Cheers.

Tuesday, July 19, 2011

How to mock UrlHelper?

Sometime, you need to write unit test for an action method that needs a UrlHelper. But I'm sure you will never can mock that stuff since it's a concrete class and it's methods are not virtual. Let's say we have following action method and want to test it:
[Authorize]
public ActionResult LogOnAs(Guid userId, string returnUrl)
{
    // Logic to login user by Id ...
    
    if (Url.IsLocalUrl(returnUrl))
    {
        return Redirect(returnUrl);
    }

    return RedirectToAction("Index", "User");
}
I did some research, experimented with some results and find that extracting the UrlHelper methods to an interface then make a wrapper class to implement that interface is the best way. We need to define a new property to the BaseController like below.
 
public new IUrlHelper Url {get; set;}
 
Then in the unit test project, after initializing the controller, you can mock the interface easily. You probably need only some methods from the UrlHelper so I would recommend you extract only those required methods to IUrlHelper. For example:
public interface IUrlHelper
{
    string Action(string actionName, string controllerName);

    string Action(string actionName, string controllerName, object routeValues);

    string Action(string actionName, string controllerName, RouteValueDictionary routeValues);
    
    bool IsLocalUrl(string url);
}
Finally, we can create an adaptor class like below to delegate all method calls to the real UrlHelper object:
public class UrlHelperAdaptor : UrlHelper, IUrlHelper
{
    internal UrlHelperAdaptor(RequestContext requestContext)
        : base(requestContext)
    {
    }

    internal UrlHelperAdaptor(RequestContext requestContext, RouteCollection routeCollection)
        : base(requestContext, routeCollection)
    {
    }

    public UrlHelperAdaptor(UrlHelper helper) 
        : base(helper.RequestContext, helper.RouteCollection)
    {
    }
}
Apparently, we need to initialize the new Url property in the BaseController to make the real code work normally:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
    base.Initialize(requestContext);
    Url = new UrlHelperAdaptor(base.Url);
}
Now, the controller method is fully testable.
[Test]
public void LogonAs_should_return_RedirectToRouteResult()
{
    // Arrange
    var controller = new AccountController();
    /* Create a mock of IUrlHelper */
    controller.Url = Moq.Mock.Of<IUrlHelper>(x => x.IsLocalUrl(It.IsAny<string>()) == false);     

    // Action
    var result = controller.LogOnAs(Guid.NewGuid(), "any-return-url") as RedirectToRouteResult;

    // Assert
    result.Should().Not.Be.Null();
}
Using this approach can help you test any class that depends on IUrlHelper such as custom UrlHelper classes. Cheers

Thursday, April 14, 2011

How to mock the MVC View Engine

Let's say we've created an HtmlHelper extension method to render a partial view without a need to provide a partial view name based on the type or template hint of a view model. The helper would look like:
public static void DisplayModel(this HtmlHelper htmlHelper, object model)
{
    var type = model.GetType();
    var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type);
    if (string.IsNullOrEmpty(modelMetadata.TemplateHint))
    {
        htmlHelper.RenderPartial("DisplayTemplates/" + modelMetadata.ModelType.Name, model);
    }
    else
    {
        htmlHelper.RenderPartial("DisplayTemplates/" + modelMetadata.TemplateHint, model);
    }
}
This method is quite simple. It employs the built-in extension method of HtmlHelper: RenderPartial(). If you read the MVC source code, you will find that this method requires the ViewEngines to find the provided viewName and return a viewEngineResult. Then the viewEngineResult will be used to render the View content to Html. So it's quite complicated to test something that requires a ViewEngine behind the scene. I fell into this sittuation when i tried to test my extension methods (I attempt to raise the coverage for our team unit tests, started by writing unit tests for all the shits I made before.). After digging into the MVC source code, I think I need to replace the default view engines in ViewEngine.Engines collection by a mocked ViewEngine. Here is the implementation:
public static ViewEngineResult SetupViewContent(string viewName, string viewHtmlContent)
{
    var mockedViewEngine = Substitute.For<IViewEngine>();
    var resultView = Substitute.For<IView>();
    resultView.When(x => x.Render(Arg.Any<ViewContext>(), Arg.Any<TextWriter>())).Do(c =>
    {
        var textWriter = c.Arg<TextWriter>();
        textWriter.Write(viewHtmlContent);
    });
    var viewEngineResult = new ViewEngineResult(resultView, mockedViewEngine);

    mockedViewEngine.FindPartialView(Arg.Any<ControllerContext>(), viewName,  Arg.Any<bool>()).Returns(viewEngineResult);
    mockedViewEngine.FindView(Arg.Any<ControllerContext>(), viewName, Arg.Any<string>(), Arg.Any<bool>()).Returns(viewEngineResult);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(mockedViewEngine);
    return viewEngineResult;
}
As i said in my previous posts, I'm using NSubstitute as my primary mock framework due to it's simplicity and easy to use. To setup all the things for my test, I'll create a mock for the IView which will be used to initialize the ViewEngineResult. And the ViewEngineResult will be the output for our mocked IViewEngine when it's method FindView or FindPartialView is executed. The most interesting point to do these stuff is that the default viewEngine collection in MVC3 has 2 item which are Aspx view engine and razor view engine. Therefore, to make our mocked view engine to be able to work, we need to clear these 2 default view engines and insert our mocked view engine. That's it. Here is one of the tests:
[TestMethod]
public void Can_use_a_model_type_as_partial_view_name_and_display_model_with_provided_view_model()
{
    // Arrange
    var builder = MvcTestControllerBuilder.GetDefaultBuilder();
    var htmlHelper = builder.GetHtmlHelper();
    htmlHelper.ViewContext.Writer = Substitute.For<StringWriter>();
    MvcTestFixtureHelper.SetupViewContent("DisplayTemplates/ProfileViewModel", "ProfileViewModel Content");

    // Action
    htmlHelper.DisplayModel(new ProfileViewModel());

    // Assert
    htmlHelper.ViewContext.Writer.Received().Write("ProfileViewModel Content");
}
Please refer to this post about MvcTestControllerBuilder. Cheers.

Sunday, March 20, 2011

Write unit test for a method depends on RoleProvider

Let's say I have a class like this:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return string.IsNullOrEmpty(Roles) 
            ? base.AuthorizeCore(httpContext) 
            : IsUserInRole(Roles);
    }

    public static bool IsUserInRole(string roles)
    {
        return roles.Split(',', ';').Any(x => System.Web.Security.Roles.IsUserInRole(x.Trim()));
    }
}
This class is a very simple class that depends on a RoleProvider to check whether current user is in one of demanded roles. The question is what I'm suppose to test this class? Hmmm, the System.Web.Security.Roles class has a Providers collection of RoleProvider but it's readonly as well as the property Provider which should be default provider. It looks like there is only one way to put my mock/fake role provider is placing it into configuration file of the Test project. Currently, I'm interested in using NSubstitue as the mocking framework, but I think the following TestRoleProvider can be easily changed to use other mock library like RhinoMock or Moq. Here is my very simple TestRoleProvider that can be configed in the App.config and later on arrange our "Expectation" on it:
public class TestRoleProvider : RoleProvider
{
    private readonly RoleProvider _roleProvider;
    public TestRoleProvider(RoleProvider provider)
    {
        _roleProvider = provider;
    }

    public TestRoleProvider()
    {
        _roleProvider = Substitute.For<RoleProvider>();
    }

    public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    {
        RoleProvider.AddUsersToRoles(usernames, roleNames);
    }

    public override string ApplicationName
    {
        get { return RoleProvider.ApplicationName; }
        set { RoleProvider.ApplicationName = value; }
    }

    public RoleProvider RoleProvider
    {
        get { return _roleProvider; }
    }

    public override void CreateRole(string roleName)
    {
        RoleProvider.CreateRole(roleName);
    }

    public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
    {
        return RoleProvider.DeleteRole(roleName, throwOnPopulatedRole);
    }

    public override string[] FindUsersInRole(string roleName, string usernameToMatch)
    {
        return RoleProvider.FindUsersInRole(roleName, usernameToMatch);
    }

    public override string[] GetAllRoles()
    {
        return RoleProvider.GetAllRoles();
    }

    public override string[] GetRolesForUser(string username)
    {
        return RoleProvider.GetRolesForUser(username);
    }

    public override string[] GetUsersInRole(string roleName)
    {
        return RoleProvider.GetUsersInRole(roleName);
    }

    public override bool IsUserInRole(string username, string roleName)
    {
        return _roleProvider.IsUserInRole(username, roleName);
    }

    public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
    {
        RoleProvider.RemoveUsersFromRoles(usernames, roleNames);
    }

    public override bool RoleExists(string roleName)
    {
        return RoleProvider.RoleExists(roleName);
    }
}
And here is the configuration in App.config to make the test project use this TestRoleProvider as default RoleProvider:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <roleManager enabled="true" defaultProvider="TestRoleProvider">
      <providers>
        <add name="TestRoleProvider" type="MyCompany.MyProject.Tests.TestRoleProvider, MyCompany.MyProject.Tests"/>
        <!-- 
            Note: Change the namespace, assembly name to the assembly you put TestRoleProvider.
        -->
      </providers>
    </roleManager>
  </system.web>
</configuration>
Now, I will write a simple unit test for method CustomAuthorizeAttribute.IsUserInRole(roles):
[TestMethod]
public void Can_Check_User_Roles_Using_Role_Provider()
{
    // Arrange
    var testRoleProvider = Roles.Provider as TestRoleProvider;
    if (testRoleProvider == null)
    {
        throw new Exception("TestRoleProvider must be configed in app.config as default provider for your unit tests");
    }
    testRoleProvider.RoleProvider.IsUserInRole(Arg.Any<string>(), Arg.Any<string>()).Returns(true);
            
    // Action
    var result = CustomAuthorizeAttribute.UserInRole("Admin, Mod");

    // Assert
    Assert.AreEqual(true, result);
}
And here the result after running above unit test: TestFailed Look like something's wrong. I swear I had setup the provider correctly in App.config. The Test ran failed and that means "testRoleProvider" is not null because I did not receive any exception. Maybe there are some secrets inside the implementation of System.Web.Security.Roles. Fine, I will find out what they are. It's time to use the power of Reflector but let's download it first. Hmm, newest version is 7 and it has not been free anymore. Ok, I will use previous free version, let's show some googling skill. Hehe, I found some old web addresses that allow me to download Reflector 5.x. Arggg, It forced me to upgrade to 7.0, I chose not to upgrade and It deleted the reflector executable file. All I need is to see the code inside Roles, so free 14 days trial is enough for me :(. And here is the secret:
public static bool IsUserInRole(string username, string roleName)
{
    bool flag3;
    if (HostingEnvironment.IsHosted && EtwTrace.IsTraceEnabled(4, 8))
    {
        EtwTrace.Trace(EtwTraceType.ETW_TYPE_ROLE_BEGIN, HttpContext.Current.WorkerRequest);
    }
    EnsureEnabled();
    bool flag = false;
    bool flag2 = false;
    try
    {
        SecUtility.CheckParameter(ref roleName, true, true, true, 0, "roleName");
        SecUtility.CheckParameter(ref username, true, false, true, 0, "username");
        if (username.Length < 1)
        {
            return false;
        }
        IPrincipal currentUser = GetCurrentUser();
        if (((currentUser != null) && (currentUser is RolePrincipal)) && ((((RolePrincipal) currentUser).ProviderName == Provider.Name) && StringUtil.EqualsIgnoreCase(username, currentUser.Identity.Name)))
        {
            flag = currentUser.IsInRole(roleName);
        }
        else
        {
            flag = Provider.IsUserInRole(username, roleName);
        }
        flag3 = flag;
    }
    finally
    {
        if (HostingEnvironment.IsHosted && EtwTrace.IsTraceEnabled(4, 8))
        {
            if (EtwTrace.IsTraceEnabled(5, 8))
            {
                string str = SR.Resources.GetString(flag ? "Etw_Success" : "Etw_Failure", CultureInfo.InstalledUICulture);
                EtwTrace.Trace(EtwTraceType.ETW_TYPE_ROLE_IS_USER_IN_ROLE, HttpContext.Current.WorkerRequest, flag2 ? "RolePrincipal" : Provider.GetType().FullName, username, roleName, str);
            }
            EtwTrace.Trace(EtwTraceType.ETW_TYPE_ROLE_END, HttpContext.Current.WorkerRequest, flag2 ? "RolePrincipal" : Provider.GetType().FullName, username);
        }
    }
    return flag3;
}
And this is the way it gets current user:
private static IPrincipal GetCurrentUser()
{
    if (HostingEnvironment.IsHosted)
    {
        HttpContext current = HttpContext.Current;
        if (current != null)
        {
            return current.User;
        }
    }
    return Thread.CurrentPrincipal;
}
Okey, i have enough information to let the test use my roleprovider. It's quite complicated to modify HostingEnvironment.IsHosted as well as HttpContext.Current. Indeed, I can create a mock httpContext and set the mock object to HttpContext.Current but I think changing CurrentPrincipal of current Thread is more simple. So, I decided to change my unit test arrangement:
[TestMethod]
public void Can_Check_User_Roles_Using_Role_Provider()
{
    // Arrange
    var identity = Substitute.For<IIdentity>();
    identity.IsAuthenticated.Returns(true);
    identity.Name.Returns("AnyUsername");

    var principal = Substitute.For<IPrincipal>();
    principal.Identity.Returns(identity);
    Thread.CurrentPrincipal = principal;

    var testRoleProvider = Roles.Provider as TestRoleProvider;
    if (testRoleProvider == null)
    {
        throw new Exception("TestRoleProvider must be configed in app.config as default provider for your unit tests");
    }
    testRoleProvider.RoleProvider.IsUserInRole(Arg.Any<string>(), Arg.Any<string>()).Returns(true);

    
    // Action
    var result = CustomAuthorizeAttribute.IsUserInRole("Admin, Mod");


    // Assert
    Assert.AreEqual(true, result);
}
Hoho, finally It passed. Reflector is so useful, isn't it? I think we can do the same way if we want to test any implementations that need MembershipProvider or ProfileProvider. I would move the Principal Arrangement into a public static method of TestRoleProvider to make it reusable. And because I changed the Thread.CurrentPrincipal during the test, so it could be nice if we make a test initialize and test cleanup to backup/restore current principle.But I let you to do those stuff if you're interested in :D Cheers.