views:

52

answers:

4

I am trying to unit test an action filter I wrote. I want to mock the HttpClientCertificate but when I use MOQ I get exception. HttpClientCertificate doesnt have a public default constructor.

code:

//Stub HttpClientCertificate </br>
var certMock = new Mock<HttpClientCertificate>();
HttpClientCertificate clientCertificate = certMock.Object;
requestMock.Setup(b => b.ClientCertificate).Returns(clientCertificate);
certMock.Setup(b => b.Certificate).Returns(new Byte[] { });
A: 

The issue is that you need to specify constructor parameters when creating the mock of the HttpClientCertificate.

var certMock = new Mock<HttpClientCertificate>(ctorArgument);

The bad news is that the ctor for HttpClientCertificate is internal and takes in an HttpContext, so it probably won't work.

Haacked
+2  A: 

This is the most awkward case of creating unit testable systems in .NET. I invariable end up adding a layer of abstraction over the component that I can't mock. Normally this is required for classes with inaccessible constructors (like this case), non-virtual methods or extension methods.

Here is the pattern I use (which I think is Adapter pattern) and is similar to what MVC team has done with all the RequestBase/ResponseBase classes to make them unit testable.

//Here is the original HttpClientCertificate class
//Not actual class, rather generated from metadata in Visual Studio

public class HttpClientCertificate : NameValueCollection {
    public byte[] BinaryIssuer { get; }
    public int CertEncoding { get; }
    //other methods
    //...
}

public class HttpClientCertificateBase {
    private HttpClientCertificate m_cert;

    public HttpClientCertificateBase(HttpClientCertificate cert) {
       m_cert = cert;
    }
    public virtual byte[] BinaryIssuer { get{return m_cert.BinaryIssuer;} }
    public virtual int CertEncoding { get{return m_cert.CertEncoding;} }
    //other methods
    //...
}

public class TestClass {
  [TestMethod]
  public void Test() {
      //we can pass null as constructor argument, since the mocked class will never use it and mock methods will be called instead
      var certMock = new Mock<HttpClientCertificate>(null);
      certMock.Setup(cert=>cert.BinaryIssuer).Returns(new byte[1]);
  }
}

In your code that uses HttpClientCertificate you instead use HttpClientCertificateBase, which you can instantiate like this - new HttpClientCertificateBase(httpClientCertificateInstance). This way you are creating a test surface for you to plug in mock objects.

Igor Zevaka
+1  A: 

Unless you want to write more code to make the class "Testable" I suggest you use Typemock Isolator, Unless specified otherwise it looks for the first c'tor available - public, internal or private and fake (mocks) it's parameters so you won't have to.

Creating the fake object is as simple as:

var fakeHttpClientCertificate = Isolate.Fake.Instance<HttpClientCertificate>();
Dror Helper
A: 

Another alternative is to use the free Microsoft Moles framework. It will allow you to replace any .NET method with your own delegate. Check out the link as it gives an example that is pretty easy to understand. I think you'll find it much nicer than adding layers of indirection to get HttpClientCertificate into a testable state.

Matt Spinelli