Moq: 对不可重写成员的无效设置: x = > x. GetByTitle (“ asdf”)

不确定如何修复这个问题,尝试对方法“ 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")

更新

My [Setup] looks like:

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


_articleManager = new ArticleManager(_mockDaoFactory.Object);
}
87585 次浏览

为了控制模拟对象的行为(至少在 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>();

创建继承的仿真类

我在尝试从框架模拟一个我无法控制的类时遇到了同样的问题。在我的具体案例中,我不得不模拟 HttpResponseMessage设置状态代码来返回 OK,但是如果该属性不是虚拟的,该如何做呢?

这段代码不起作用,因为 状态代码不是虚拟的:

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. 重新定义调用基本构造函数的同一组构造函数
  3. 重新定义要设置为虚拟的非虚拟属性或方法(使用 新的关键字显式隐藏原始成员)
  4. From the redefined virtual properties or methods, call the non virtual base property or method.

成交。现在,您可以模拟一个派生对象,它可以在任何使用原始对象的地方使用,因为它是从派生对象继承而来的。下面是我的 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);

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("");

//此时,将调用 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 测试中都使用它,因为我可以传递状态、内容等。因此,我可以测试不同的返回值。