116

Not sure how I can fix this, trying to do a unit test on the method "GetByTitle"

Here are my definitions:

public class ArticleDAO :  GenericNHibernateDAO(IArticle, int>, IArticleDAO
{
    public IArticle GetByTitle(string title)
    {
        IQuery query = Session.CreateQuery("...")
        return query.UniqueResult<IArticle>();
    }
}

public interface IArticleDAO
{
    IArticle GetByTitle(string title);
}

unit test:

[Test]
public void can_load_by_title()
{
    _mockDaoFactory.Setup(x => x.GetArticleDao())
                                .Returns(_mockArticleDao.Object);
    _mockArticleDao.Setup(x => x.GetByTitle("some title"))
                                .Returns(article1.Object);

    _articleManager.LoadArticle("some title");

    Assert.IsNotNull(_articleManager.Article);
}

Running the test gives me the error:

System.ArgumentException: Invalid setup on a non-overridable member:
x => x.GetByTitle("some title")

Update

My [Setup] looks like:

[Setup]
public void SetUp()
{
     _mockDaoFactory = new Mock<IDaoFactory>();
     _mockArticleDao = new Mock<ArticleDao>();

     _articleManager = new ArticleManager(_mockDaoFactory.Object);    
}
SteveC
  • 15,808
  • 23
  • 102
  • 173
mrblah
  • 99,669
  • 140
  • 310
  • 420
  • 2
    Do you instantiate `_mockDaoFactory` and `_mockArticleDao` somewhere? Do you mock the class or the interface – Tomas Aschan Dec 25 '09 at 21:40
  • Yes I mocked the daofactory and mockarticleDao in the [Setup] using the Interface. the DAO was done using the class. – mrblah Dec 25 '09 at 21:42
  • @tomas I updated my question with the setup code. – mrblah Dec 25 '09 at 21:46
  • 2
    As you can see in my answer, you need to either mock the interface (that's what I recommend) or mark the `GetByTitle` method `virtual`. – Tomas Aschan Dec 25 '09 at 21:51
  • It also looks as if the first line in your test could be moved to the setup routine...? – Tomas Aschan Dec 25 '09 at 21:52
  • Have you considered ticking in the checkmark next to my answer, if I helped solve your problem? – Tomas Aschan Sep 23 '10 at 19:29
  • Possible duplicate of [Why am I getting an Exception with the message "Invalid setup on a non-virtual (overridable in VB) member..."?](https://stackoverflow.com/questions/21768767/why-am-i-getting-an-exception-with-the-message-invalid-setup-on-a-non-virtual) – ebram khalil Mar 05 '18 at 18:38

3 Answers3

159

In order to control the behavior of a mock object (in Moq, at least), you either need to mock an interface, or make sure that the behavior you're trying to control is marked virtual. In your comment, I understand it so that the instantiating of _mockArticleDao is done something like this:

_mockArticleDao = new Mock<ArticleDAO>();

If you want to keep it as so, you need to mark the GetArticle method virtual:

public class ArticleDAO :  GenericNHibernateDAO(IArticle, int>, IArticleDAO
{
    public virtual IArticle GetByTitle(string title)
    {
        // ...
    }
}

Otherwise (and this is what I recommend), mock the interface instead.

_mockArticleDao = new Mock<IArticleDAO>();
Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • but since the ArticleDAO inherits from Generic.... , if I mock the interface the methods in the GenericNhibern. will not be made available? – mrblah Dec 25 '09 at 21:56
  • because the call to GetArticleDAO from the factory returns ArticleDAO not IArticleDAO, b/c articleDAO also binds to an abstract class that has nhibernate stuff in it. – mrblah Dec 25 '09 at 21:57
  • 3
    If you can't mock the interface, then you might be testing the wrong thing... but still, marking the method virtual will solve the problem. – Tomas Aschan Dec 26 '09 at 20:07
  • +1 Tomas, I need to inject a parameter into the ctor, hence in my case I had to mock the actual class and set the methods to virtual, because you can't inject parameters into an Interface's ctor. Is this the right approach? – Houman Feb 14 '11 at 10:14
  • 4
    @Kave: If you need to inject something in the constructor, you are definitely testing the wrong thing. Mock whatever you give the constructor, setup it's behavior and test that *this* class behaves the way it should. If you need to, write a new interface that you make the "injected" type implement in order to access all the method signatures. – Tomas Aschan Feb 14 '11 at 17:12
1

Create an inherited mockable class

I had the same issue trying to mock a class I have no control over, from a framework. In my specific case I had to mock an HttpResponseMessage setting up the status code to return Ok, but how to do it if that property is not virtual?

This code does not work because StatusCode is not virtual:

var httpResponseMessage = new Mock<HttpResponseMessage>();
httpResponseMessage.SetupGet(x => x.StatusCode).Returns(HttpStatusCode.OK);

Answer:

  1. Create a new class in your test project, inheriting from the class you want to mock
  2. Redefine the same set of constructors calling the base constructors
  3. Redefine the non virtual properties or methods you want to setup as virtual (use the new keyword to explicitly hide the original members)
  4. From the redefined virtual properties or methods, call the non virtual base property or method.

Done. Now you can mock a derived object that can be used anywhere the original one is used, because it inherits from it. Here is the code for my MockableHttpResponseMessage class:

public class MockableHttpResponseMessage: HttpResponseMessage
{
    public MockableHttpResponseMessage() : base() {}
    public MockableHttpResponseMessage(HttpStatusCode code) : base (code) { }
    public new virtual HttpStatusCode StatusCode { 
        get { return base.StatusCode; } 
        set { base.StatusCode = value; }
    }        
}

Now, this code works:

var httpResponseMessage = new Mock<MockableHttpResponseMessage>();
httpResponseMessage.SetupGet(x => x.StatusCode).Returns(HttpStatusCode.OK);
  • How does this work? if `HttpResponseMessage.StatusCode` is not virtual if it is called by some implementing class it will get the original StatusCode, not the one you defined – Xriuk Feb 01 '23 at 13:23
  • The "new virtual" keywords on the HttpStatusCode property of the MockableHttpResponseMessage class redefine the property as virtual, and when invoked it calls the original one on the base class. – Marcelo Barsotti Apr 25 '23 at 13:30
0

Here's how I Mock HttpMessageHandler:

private HttpRequestMessage requestMessage = new HttpRequestMessage();
Mock<HttpMessageHandler> handlerMock = 
GetHttpMessageHandlerMock(HttpStatusCode.OK);  

MyRestService myRestService = new MyRestService();
myRestService.client = new HttpClient(handlerMock.Object);

var response = myRestService.Get("");

//At this point, the Mock of HttpRequestMessage is called and the Callback fills my class variable requestMessage. I can now look inside the requestMessage.

var headers = requestMessage?.Headers.ToString();
var queryBegin = requestMessage.RequestUri.OriginalString.IndexOf('?');
var queryString = requestMessage.RequestUri.OriginalString.Substring(queryBegin + 1);
        Assert.That(headers.Contains("x-api-key: fakeApiKey"));

//Helper methods below

private Mock<HttpMessageHandler> GetHttpMessageHandlerMock(HttpStatusCode statusCode)
{
        var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
        handlerMock
           .Protected()
           .Setup<Task<HttpResponseMessage>>(
              "SendAsync",
              ItExpr.IsAny<HttpRequestMessage>()
             , ItExpr.IsAny<CancellationToken>()
           )
           .Returns(Task.FromResult(GetFakeResponse(statusCode)))
           .Callback<HttpRequestMessage, CancellationToken>((p, q) => requestMessage = p)
          .Verifiable();
        return handlerMock;
    }


    private HttpResponseMessage GetFakeResponse(HttpStatusCode statusCode)
    {
        var s = "{\"data\":{\"status\":\"SUCCESS\",\"errorCode\":\"\",\"errorMessage\":\"9\"}}";
        HttpResponseMessage response = new HttpResponseMessage()
        {

            StatusCode = statusCode,
            Content = new StringContent(s),
            ReasonPhrase = "OK",
            RequestMessage = new HttpRequestMessage()
        };
        return response;
    }

I use this for almost all my REST tests, because I can pass in status, content, etc. So, I can test different return values.

wesreitz
  • 79
  • 6