Jan 15, 2010

Test Example in System.Web.Mvc

Because the mvc project and test project are seperate, we need the test project to see the internal member in System.Web.Mvc project, so there is a attribute in the mvc project.

[assembly: InternalsVisibleTo("System.Web.Mvc.Test")]

The effort to make the project testable of the mvc project is very obvious. For example, to make DefaultControllerFactory testable, the class has a property setter, so that test project can inject mocking object.

DefaultControllerFactory factory = new DefaultControllerFactory();
MockBuildManager buildManagerMock = new MockBuildManager(new Assembly[] { });
ControllerTypeCache controllerTypeCache = new ControllerTypeCache();

factory.BuildManager = buildManagerMock;
factory.ControllerTypeCache = controllerTypeCache;

// Act
Type controllerType = factory.GetControllerType("sometype");

// Assert
Assert.IsNull(controllerType, "Shouldn't have found a controller type.");
Assert.AreEqual<int>(0, controllerTypeCache.Count, "Cache should be empty.");

//member of DefaultControllerFactory
internal IBuildManager BuildManager {
    get {
        if (_buildManager == null) {
            _buildManager = new BuildManagerWrapper();
        }
        return _buildManager;
    }
    set {
        _buildManager = value;
    }
}

internal ControllerBuilder ControllerBuilder {
    get {
        return _controllerBuilder ?? ControllerBuilder.Current;
    }
    set {
        _controllerBuilder = value;
    }
}

Another example is as follow. Even we need to increase performance of the by using staticControllerTypeCase, we still allow test project to inject an _instanceControllerTypeCase for testing.

//member of DefaultControllerFactory
internal ControllerTypeCache ControllerTypeCache {
    get {
        return _instanceControllerTypeCache ?? _staticControllerTypeCache;
    }
    set {
        _instanceControllerTypeCache = value;
    }
}

//member of ControllerContext
//RouteData can be mocked.
public virtual RouteData RouteData {
    get {
        if (_routeData == null) {
            _routeData = (_requestContext != null) ? _requestContext.RouteData : new RouteData();
        }
        return _routeData;
    }
    set {
        _routeData = value;
    }
}