簡體   English   中英

如何在MVC5 /實體框架6中編寫CRUD的測試單元?

[英]How can I write Test Unit for CRUD in MVC5 / Entity Framework 6?

我是ASP.NET MVC和單元測試的新手,我不知道如何為這些方法編寫測試方法:

// 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");
}

我已經編寫了此代碼進行測試,但這還不夠。

[TestMethod]
public void Details()
{
        // Arrange
        WortsController controller = new WortsController();

        int id = 22;

        // Act
        ViewResult result = controller.Details(id) as ViewResult;

        // Assert
        Assert.IsNotNull("Details", result.ViewName);
}

[TestMethod]
public void Create()
{
        // Arrange
        WortsController controller = new WortsController();

        // Act
        ViewResult result = controller.Create(new LikeWort.Models.Wort()) as ViewResult;

        // Assert
        Assert.IsNotNull(result);
}

[TestMethod]
public void Edit(int? id)
{
        // Arrange
        WortsController controller = new WortsController();

        // Act
        ViewResult result = controller.Edit(id) as ViewResult;

        // Assert
        Assert.IsNotNull(result);
}

[TestMethod]
public void Delete(int? id)
{
        // Arrange
        WortsController controller = new WortsController();

        // Act
        ViewResult result = controller.Delete(id) as ViewResult;

        // Assert
        Assert.IsNotNull(result);
}

誰能幫助我為這些方法編寫正確而完整的測試單元?

我的第一個建議是將您的業務邏輯與管道分開。

考慮以下代碼:

// 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

您的服務層將根據情況拋出這兩個異常。

現在,您的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);
        }
    }
}

這是微軟的直接方法

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類。

您需要先分離本地版本,然后再執行更新過程

        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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM