【问题标题】:How do you unit test a MVC controller with EF SaveChanges如何使用 EF SaveChanges 对 MVC 控制器进行单元测试
【发布时间】:2014-05-27 10:16:08
【问题描述】:

下面是一个带有 Post 方法的控制器。如何在不将更改保存到数据库的情况下针对 CREATE 方法编写单元测试?

我正在尝试针对我的控制器编写测试,以便当其他开发人员更改代码时它不会破坏我的功能(我在 Create 方法上有一点功能,以保持它现在的简单性)。

public class AdministratorController : Controller
{
  private IUnitOfWork _uow;

[HttpPost]
public ActionResult Create(MyModel model)
{
    ViewBag.id = model.Id;

    if (model.FirstName == model.LastName)
    {
        ModelState.AddModelError("", "Cannot have same first name and last name.");
    }


    if (ModelState.IsValid)
    {
        MyClass record = new MyClass();

        record.SAFirstName = model.FirstName;
        record.SALastName = model.LastName;
        record.SATitle = model.Title;
        record.SAEmail = model.EmailAddress;

        record.Since = DateTime.Now;

        _uow.AdministratorRepository.AddRecord(record);
        _uow.SaveChanges();
        return RedirectToAction("Index", "Administrator");
    }

    return View(model);
}

}

2.) 我的 UOW 看起来像这样:

公共类 UnitOfWork : IUnitOfWork { 私有只读 MasterContext _context;

public UnitOfWork(MasterContext context)
{
    _context = context;
}

public UnitOfWork()
{
    _context = new MasterContext();
}

public void SaveChanges()
{
    _context.SaveChanges();
}

private IAdministratorRepository _Repo;

public IAdministratorRepository AdministratorRepository
{
    get
    {
        if (this._Repo == null)
        {
            this._Repo = new IAdministratorRepository(_context);
        }
        return _Repo;
    }

}

3) 我的 AdministratorRepository 构造函数如下所示:

    private readonly MasterContext _context;

    public AdministratorRepository(MasterContext context)
    {
        _context = context;
    }

【问题讨论】:

  • 经典答案是,你不知道。你抽象出像数据库这样的东西。但是,我的新想法是,您可以使用 SQL CE 或 SQL Server LocalDB 创建一个用于测试生命周期的数据库。
  • 您已经完成了艰苦的工作,将您的数据库访问抽象到一个可模拟的接口后面。只需创建一个模拟您想要的操作的存根,或使用一个模拟框架。如果你有任何直接传递 Linq 的方法,你会遇到一些问题,因为 Linq to Entities 的行为与 Linq to Objects 略有不同,这是大多数模拟框架会使用的。

标签: c# asp.net-mvc unit-testing mocking


【解决方案1】:

您需要能够将伪造/模拟的 IUnitOfWork 注入您的控制器。最简单的方法是在控制器上创建一个内部构造函数,该构造函数接受假对象并创建一个属性,该属性要么创建一个新实例,要么返回现有实例。

private IUnitOfWork _uow;
private IUnitOfWork UnitOfWork
{
    get
    {
        _uow = _uow ?? new UnitOfWork();
        return _uow;
    }
}

public AdministratorController() {}

internal AdministratorController( IUnitOfWork uow )
{
    _uow = uow;
}

您还需要修改 MVC 项目的 AssemblyInfo 文件,以使内部构造函数对单元测试项目可见。查找 InternalsVisibleToAttribute 。

现在在单元测试中,您可以创建假/模拟对象并将其注入。您没有指出您正在使用什么模拟框架。我们使用 FakeItEasy,所以它是这样的:

var uow = A.Fake<IUnitOfWork>();

var controller = new AdministratorController( uow );

A.CallTo( () => uow.SaveChanges() ).MustHaveHappened();

【讨论】:

  • 然后你必须调整上面的代码来使用 MOCK 来创建你的 mock/fake/stub IUnitOfWork。就我个人而言,我从未听说过 MOCK,所以我不会在细节上提供太多帮助,但老实说,在这一点上,您应该能够从这里获取它并弄清楚自己创建模拟对象的细节。
【解决方案2】:

您应该模拟您的依赖项。在您的存储库的示例 AddRecord() 中。然后您应该使用您的预期模型(您必须在单元测试方法中设置)测试返回的模型字段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-26
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2012-05-25
    • 1970-01-01
    相关资源
    最近更新 更多