views:

88

answers:

1

I would like to be able to mock the ClientScriptManager class on a webforms Page object, however it seems like I can't, I get the error that I can't mock a sealed class.

MockRepository mocks = new MockRepository()

Page page = mocks.PartialMock<Page>();

var clientScript = mocks.PartialMock<ClientScriptManager>(); //error here

SetupResult.For(page.ClientScript).Return(clientScript);

Any advice on how to mock the clientscriptmanager would be appreciated.

+3  A: 

As you've discovered, you can't use most mocking libraries to mock sealed types. One reason for this is that many mock libraries operate by creating a derived type but if the class is sealed then they can't derive from it.

What we've done internally at Microsoft is that we use a hand-written IClientScriptManager interface and then use a ClientScriptManagerWrapper that implements that interface and delegates all calls to a real ClientScriptManager.

Then whatever type needs to use the ClientScriptManager it will instead have a reference to an IClientScriptManager. At runtime we create a ClientScriptManagerWrapper (and pass in the real ClientScriptManager). At test time we use a mock object library to create a mock IClientScriptManager and use that instead.

And here's a code sample:

public class SomeClassThatNeedsClientScriptManager {
    private IClientScriptManager _iClientScriptManager;

    public IClientScriptManager IClientScriptManager {
        get {
            if (_iClientScriptManager == null) {
                _iClientScriptManager = new ClientScriptManagerWrapper(Page.ClientScriptManager);
            }
            return _iClientScriptManager;
        }
        set {
            _iClientScriptManager = value;
        }
    }

    public void SomeMethodThatUsesClientScriptManager() {
        IClientScriptManager.RegisterClientScriptBlock(typeof(Whatever), "key", "alert('hello')");
    }
}

public interface IClientScriptManager {
    void RegisterClientScriptBlock(Type type, string key, string script);
}

public class ClientScriptManagerWrapper : IClientScriptManager {
    private readonly ClientScriptManager _clientScriptManager;

    public ClientScriptManagerWrapper(ClientScriptManager clientScriptManager) {
        if (clientScriptManager == null) {
            throw new ArgumentNullException("clientScriptManager");
        }
        _clientScriptManager = clientScriptManager;
    }

    public void RegisterClientScriptBlock(Type type, string key, string script) {
        _clientScriptManager.RegisterClientScriptBlock(type, key, script);
    }
}

You can then modify the IClientScriptManager interface and the ClientScriptManagerWrapper to have whatever methods you need.

Eilon