MVC4单元测试NSubstitute找不到返回调用

4
我正在进行单元测试的MVC4 Web应用程序,与数据库部分使用实体框架。我使用NSubstitute来模拟数据库。这段代码基本上是从另一个网站复制粘贴过来的,可运行正常,所以我希望我只是忽略了一些非常简单的东西。
提前致谢!
SQL中的应用程序表:
AppID   | ApplicationName
----------------------------
1       | MyCoolApplication
2       | MyOtherApplication

创建了Application类的实体:

public class Application
{
    public int AppID { get; set; }
    public string ApplicationName { get; set; }
}

单元测试中的模拟部分如下:

var mockDb = Substitute.For<MyCoolApplicationsEntities>();

var applications = new List<Application>
{
    new Application {AppID = 1, ApplicationName = "MyCoolApplication"},
    new Application {AppID = 2, ApplicationName = "MyOtherApplication"},
};

var mockApplicationSet = Substitute.For<IDbSet<Application>, DbSet<Application>>();
mockApplicationSet.Provider.Returns(applications.AsQueryable().Provider);
mockApplicationSet.Expression.Returns(applications.AsQueryable().Expression);
mockApplicationSet.ElementType.Returns(applications.AsQueryable().ElementType);
mockApplicationSet.GetEnumerator().Returns(applications.AsQueryable().GetEnumerator());

mockApplicationSet.When(q => q.Add(Arg.Any<Application>()))
    .Do(q => applications.Add(q.Arg<Application>()));

mockApplicationSet.When(q => q.Remove(Arg.Any<Application>()))
    .Do(q => applications.Remove(q.Arg<Application>()));


mockDb.Applications.Returns(mockApplicationSet); //This is the line creating the error

完整的错误信息是:

Test method MyProjectName.Controllers.MyControllerTest.TestOfSectionImTesting threw exception: NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException: Could not find a call to return from.

Make sure you called Returns() after calling your substitute (for example: mySub.SomeMethod().Returns(value)), and that you are not configuring other substitutes within Returns() (for example, avoid this: mySub.SomeMethod().Returns(ConfigOtherSub())).

If you substituted for a class rather than an interface, check that the call to your substitute was on a virtual/abstract member. Return values cannot be configured for non-virtual/non-abstract members.

Correct use:

mySub.SomeMethod().Returns(returnValue);

Potentially problematic use:

mySub.SomeMethod().Returns(ConfigOtherSub());

Instead try:

var returnValue = ConfigOtherSub(); 

mySub.SomeMethod().Returns(returnValue);

但是在我的环境中这并不起作用,因为应用程序不是一个方法。正如我所说的,在我的另一个站点中这个方法很好用,所以肯定是我漏掉了一些基本的东西。我在网上找到的信息对我的情况没有帮助。我已经更新了最新版本的 NSubstitute,并进行了卸载/重新安装,但仍然没有任何结果。

再次感谢您的帮助!

3个回答

15

NSubstitute无法模拟非虚拟成员。(替代类有相当多的陷阱。)

MyCoolApplicationsEntities.Applications需要是虚拟的才能使.Returns()正常工作。


David,感谢您的关注。今天早上我在同事的帮助下成功解决了问题。如果您有兴趣,请查看我的答案。再次感谢! - Timothy
@Timothy,很高兴你解决了它!所以问题不是通过IQueryable接口吗?这意味着context.Applications.Returns(mockApplications)现在应该可以正常工作了? - David Tchepak
context.Applications.Returns(mockApplications) 仍然会出现相同的错误。不过,现在 context.Applications = mockApplications 已经可以正常工作了。 - Timothy

4
这是最终有效的方法:

var context = Substitute.For<MyCoolApplicationsEntities>();

var applications = new List<Application>
{
    new Application {AppID = 1, ApplicationName = "MyCoolApplication"}
};

var mockApplications = Substitute.For<DbSet<Application>, IQueryable<Application>>();
((IQueryable<Application>)mockApplications).Provider.Returns(applications.AsQueryable().Provider);
((IQueryable<Application>)mockApplications).Expression.Returns(applications.AsQueryable().Expression);
((IQueryable<Application>)mockApplications).ElementType.Returns(applications.AsQueryable().ElementType);
((IQueryable<Application>)mockApplications).GetEnumerator().Returns(applications.AsQueryable().GetEnumerator());

mockApplications.When(q => q.Add(Arg.Any<Application>()))
    .Do(q => applications.Add(q.Arg<Application>()));

mockApplications.When(q => q.Remove(Arg.Any<Application>()))
    .Do(q => applications.Remove(q.Arg<Application>()));

context.Applications = mockApplications;

1
我看不到你的类,但你需要创建带有虚成员的接口,并通过接口调用类,这样你就能够模拟出类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接