Asp.net mvc 异步控制器方法测试

Asp.net mvc async controller method tests

我一直在尝试学习单元测试,而不是 TDD,我试图在我的控制器中尝试单元测试方法。我有 Unity 3.5、Asp.Net MVC 5.1、Entity Framework 6.1.2、MS 测试、NSubstitute 1.8.1.0 和 Visual Studio 2013 更新 3。我有一个包含 3 个项目的解决方案,一个是正常的MVC 5 项目、一个单元测试项目和一个 class 库都被正确引用并且工作正常。我的代码编译正常,一切都很好。我现在正在尝试测试我的异步索引操作,这就是我的问题。我遵循了关于 EF6 测试的 msdn 文档,所以我有一个从上下文创建的接口,并在我的控制器中使用它,如下所示:

public interface ITestContext : IDisposable
{
    IDbSet<Account> Accounts { get; set; }
    IDbSet<Bank> Banks { get; set; }
    DbEntityEntry Entry(object o);
    int SaveChanges();
    Task<int> SaveChangesAsync();
}

那么我的上下文class是这样的:

public class TestContext : DbContext, ITestContext
{
    public TestContext(): base("DefaultConnection"){}

    public virtual IDbSet<Account> Accounts { get; set; }
    public virtual IDbSet<Bank> Banks { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        base.OnModelCreating(modelBuilder);
    }
    public override int SaveChanges()
    {
        return base.SaveChanges();
    }

    public override Task<int> SaveChangesAsync()
    {
        return base.SaveChangesAsync();
    }

    public DbEntityEntry Entity(object o)
    {
        return base.Entry(o);
    }
}

所以我使用 DI 在我的控制器中使用接口。我所有的控制器方法都是异步方法,因此我的索引操作如下所示:

public class AccountsController : Controller
{
    private ITestContext db;

    public AccountsController(ITestContext _db)
    {
        db = _db;
        //db1 = bnk;
    }

    // GET: Accounts
    public async Task<ActionResult> Index()
    {
        var accounts = db.Accounts.Include(a => a.Bank);
        return View(await accounts.ToListAsync());
    }

我根据 NSubstitute DbSet / IQueryable<T> 的提示创建了 TestDbAsyncEnumerator 和 TestDbAsyncQueryProvider。所以烦人的事情是为了测试这个索引方法该怎么做?这是我走了多远:

[TestMethod]
    public async Task TestingIndexControllerAction()
    {
        var testdb = new List<Account>
        {
            new Account{ Bank=new Bank{Name="Zenith"}, AccountBalance=19000, AccountName="John Doe"}
        }.AsQueryable();
        var test = Substitute.For<IDbSet<Account>, IDbAsyncEnumerable<Account>>().Initailize(testdb);
        var context = Substitute.For<ITestContext>();
        context.Accounts.Returns(test);
        var controller = new AccountsController(context);
        var result = await controller.Index();
    }

有人可以帮我解决这个问题并指出正确的方向吗?我已经阅读了博客和书籍,但仍然不明白我应该在这里做什么?结果是 ActionResult 的任务,所以我反对什么?我了解到您针对模拟进行断言并使用存根来满足依赖性。那我该怎么办?我读过的单元测试是可行的方法,我不想放弃,那么我该怎么做呢?请帮忙?!

我认为你可能把我所看到的事情复杂化了。在我看来,测试的想法是将所有内容分解为最简单的形式(单位)。因此,出于测试目的,我可能会以编程方式创建帐户、银行等实例。这样做的原因是为了测试您的逻辑是否在不更改数据的情况下按照它说的去做,如果您使用数据库,这可能会发生。

通过 EF 测试,我会检查每个结果集中是否有任何项目 returned。

我还会测试您要执行的基本 EF 操作,即是否 return 所有银行?是否 return 所有帐户?您可以创建银行吗?创建帐户等。

我使用 NUnit,但大多数单元测试框架都相似。

本质上,您是在检查它是否通过,并且您需要定义测试通过或失败的原因。

这可能就像这样简单:

Assert.IsTrue(result.Equals("Hello World"));

与此集成的 Selenium 在测试表单时也非常有用。

希望对您有所帮助。