如何在MVC5 / Entity Framework 6中为CRUD编写测试单元?

时间:2017-01-20 15:58:46

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

我是ASP.NET MVC和单元测试的新手,我不知道如何为这些方法编写测试方法:

configure(feathers.socketio(socket))

我已经编写了这段代码用于测试,但这还不够。

// GET: Worts/Details
public ActionResult Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    var wort = (from s in db.Worts
                where s.ID == id
                select s).FirstOrDefault();

    if (wort == null)
    {
        return HttpNotFound();
    }

    return View(wort);
}

// GET: Worts/Create
public ActionResult Create()
{
    return View();
}

// POST: Worts/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Title,CreateDate,ClickCount,DislikeCount,LikeCount, Creator_ID")] Wort wort)
{
    if (ModelState.IsValid)
    {
        wort.CreateDate = DateTime.Now;
        wort.ClickCount = 0;
        wort.DislikeCount = 0;
        wort.LikeCount = 0;

        var userId = User.Identity.GetUserId();
        wort.Creator = db.Users.Where(x => x.ID == userId).FirstOrDefault();

        db.Worts.Add(wort);
        db.SaveChanges();

        try
        {
            this.mailSender.SendEmail(wort);
            return RedirectToAction("Index");
        }
        catch (Exception)
        {
            throw;
        }
    }
    return View(wort);
}

// GET: Worts/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Wort wort = db.Worts.Find(id);

    if (wort == null)
    {
        return HttpNotFound();
    }
    return View(wort);
}

// POST: Worts/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,Title,CreateDate")] Wort wort)
{
    if (ModelState.IsValid)
    {
        db.Entry(wort).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(wort);
}

// GET: Worts/Delete/5
public ActionResult Delete(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Wort wort = db.Worts.Find(id);

    if (wort == null)
    {
        return HttpNotFound();
    }
    return View(wort);
}

// POST: Worts/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
    Wort wort = db.Worts.Find(id);
    db.Worts.Remove(wort);
    db.SaveChanges();
    return RedirectToAction("Index");
}

有人可以帮我为这些方法编写正确完整的测试单元吗?

3 个答案:

答案 0 :(得分:1)

我的第一个建议是将您的业务逻辑与您的管道分开。

考虑以下代码:

// GET: Worts/Details
public ActionResult Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    var wort = (from s in db.Worts
                where s.ID == id
                select s).FirstOrDefault();

    if (wort == null)
    {
        return HttpNotFound();
    }

    return View(wort);
}

首先,让我们将数据库调用移动到自己的库中。

// GET: Worts/Details
public ActionResult Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    var wort = WortService.GetWort(id);

    if (wort == null)
    {
        return HttpNotFound();
    }

    return View(wort);
}

由于WortService没有任何ASP.NET MVC内容,因此测试起来会容易得多。

现在让我们看一下样板文件。 ASP.NET MVC的一个有趣的事情是你可以使用"异常过滤器"。你需要两个。

  1. 将ArgumentException转换为HttpStatusCode.BadRequest
  2. 将MissingDataException转换为HttpStatusCode.NotFound
  3. 您的服务层将根据需要抛出这两个例外。

    现在你的MVC层就是:

    // GET: Worts/Details
    public ActionResult Details(int? id)
    {
        var wort = WortService.GetWort(id);
        return View(wort);
    }
    

    这很简单,它不需要自己的测试。您的大部分测试都是针对WortService的,其余的测试都包含在您执行的任何UI测试中。

    public class WortService { 
        public LWContext dbLW = new LWContext(); 
        public Wort GetWort(int? id) 
        {
            if (id == null)
                 throw new ArgumentNullException("id");
    
            var wort = (from s in dbLW.Worts where s.ID == id select s).FirstOrDefault(); 
            return wort; 
        } 
    }
    
    
    public class ArgumentExceptionFilterAttribute : ExceptionFilterAttribute 
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception is ArgumentException)
            {
                context.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
            }
        }
    }
    

答案 1 :(得分:0)

这是Microsoft的直接方法

https://www.asp.net/web-api/overview/testing-and-debugging/mocking-entity-framework-when-unit-testing-aspnet-web-api-2

它与WebApi有关,对MVC的工作方式是否相同。我还使用了像Moq或FakeItEasy这样的模拟框架,而不是创建一个TestStoreAppContext类。

答案 2 :(得分:0)

您需要先分离本地版本,然后再执行更新过程

        var localEntity = dbContext.Set<theModel>()
            .Local
            .FirstOrDefault(f => f.Id == theModel.Id);
        if (localEntity != null)
        {
            dbContext.Entry(localEntity).State = EntityState.Detached;
        }
        dbContext.Entry(appModel).State = EntityState.Modified;