Moq:不可覆盖的成员上的设置无效:x => x.GetByTitle( “ASDF”)

时间:2009-12-25 21:38:23

标签: c# nhibernate nunit resharper moq

我不确定如何解决此问题,尝试对方法“GetByTitle”进行单元测试

以下是我的定义:

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);
}

单元测试:

[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);
}

运行测试会给出错误:

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

更新

我的[Setup]看起来像是:

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

     _articleManager = new ArticleManager(_mockDaoFactory.Object);    
}

3 个答案:

答案 0 :(得分:145)

为了控制模拟对象的行为(至少在Moq中),您需要模拟一个接口,或者确保您尝试控制的行为标记为虚拟。在你的评论中,我理解它,以便_mockArticleDao的实例化完成如下:

_mockArticleDao = new Mock<ArticleDAO>();

如果您想这样做,则需要标记GetArticle方法virtual

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

否则(这是我推荐的),请改为模拟界面。

_mockArticleDao = new Mock<IArticleDAO>();

答案 1 :(得分:0)

创建一个继承的可模拟类

我在尝试从框架模拟无法控制的类时遇到了同样的问题。在我的特定情况下,我不得不模拟 HttpResponseMessage 来设置状态代码以返回Ok,但是如果该属性不是虚拟的,该怎么办呢?

此代码无效,因为 StatusCode 不是虚拟的:

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

答案

  1. 在测试项目中创建一个新类,继承要模拟的类
  2. 重新定义调用基本构造函数的同一组构造函数
  3. 重新定义要设置为虚拟的非虚拟属性或方法(使用 new 关键字显式隐藏原始成员)
  4. 从重新定义的虚拟属性或方法中,调用非虚拟基本属性或方法。

完成。现在,您可以模拟一个派生对象,该对象可以在使用原始对象的任何地方使用,因为它继承自它。这是我的 MockableHttpResponseMessage 类的代码:

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; }
    }        
}

现在,此代码有效:

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

答案 2 :(得分:0)

这是我模拟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("");

///此时,将调用HttpRequestMessage的Mock,并且Callback将填充我的类变量requestMessage。我现在可以在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"));

//下面的帮助器方法

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;
    }

几乎所有的REST测试都使用它,因为我可以传递状态,内容等。因此,我可以测试不同的返回值。