2016-09-18 3 views
1

ASP.NET MVC 5에 대해 단위 테스트 및 Moq를 사용하는 방법을 배우고 있습니다. 컨트롤러 중 하나의 인덱스 동작에 대한 첫 번째 단위 테스트를 작성하려고합니다.Moq을 사용하여 데이터베이스에서 목록을 반환하는 내 색인 작업을 테스트하려면 어떻게해야합니까?

다음은 색인 조치를위한 코드입니다.

[Authorize] 
public class ExpenseController : Controller 
{ 
    private ApplicationDbContext db = new ApplicationDbContext(); 

    // GET: /Expense/ 
    public ActionResult Index() 
    { 
     return View(db.Expenses.ToList().Where(m => m.ApplicationUserId == User.Identity.GetUserId())); 
    } 
} 

난 그냥 돌아보기 물론이

[TestMethod] 
    public void ExpenseIndex() 
    { 
     // Arrange 
     ExpenseController controller = new ExpenseController(); 

     // Act 
     ViewResult result = controller.Index() as ViewResult; 

     // Assert 
     Assert.IsNotNull(result); 
    } 

같은

뭔가 null가 아닌 것으로 확인되어하고 싶은 모든이 때문에 데이터베이스에 연결하는 작동하지 않습니다 그리고 ApplicationUserId을 사용하면 moq 및 유닛 테스트에 도움이 될 것입니다. 또는 ASP.NET MVC에서 조롱하는 것에 익숙해 질 수있는 자습서를 권해드립니다. 추상적 인 예를 들어, 가상 방법 의존성을 캡슐화 에이 작업을 수행하는

+0

Abstract 데이터베이스/dbcontext에 대한 의존성을 추상화 한 다음 컨트롤러로 조롱 할 수 있습니다. – Nkosi

+0

@Nkosi 자세한 정보를 제공해 주시겠습니까? 고맙습니다. – Baso

+0

컨트롤러가'ApplicationDbContext'에 강하게 의존하여 유닛 테스트를 조롱하기가 어렵습니다. dbcontext의 기능을 노출하는 추상화를 상속하려면 dbcontext를 리팩토링해야하며 컨트롤러는 concretion 대신 추상화에 의존해야합니다. 거기에서 DI 프레임 워크를 사용하여 컨트롤러에 의존성을 주입 할 수 있습니다. 이제는 필요에 따라 테스트에서 모의 ​​구현으로 추상화를 대체 할 수있는 유연성을 갖게됩니다. – Nkosi

답변

1

한 가지 방법은 다음과 같습니다 그런

public class ExpenseController : Controller 
    { 
     private ApplicationDbContext db = new ApplicationDbContext(); 

     // GET: /Expense/ 
     public ActionResult Index() 
     { 
      return View(GetUserExpenses()); 
     } 

     protected virtual List<Expense> GetUserExpenses() 
     { 
      return db.Expenses.ToList().Where(m => m.ApplicationUserId == User.Identity.GetUserId()); 
     } 
    } 

: 사용자 비용을 반환하는 가상 메서드를 만들고, 지금 컨트롤러는 같아야합니다 컨트롤러에서 파생 된 스텁 클래스를 만들고 GetUserExpenses() 메서드를 재정의합니다. 당신의 단위 테스트에서 지금

public class ExpenseControllerStub : ExpenseController 
    { 
     protected override List<Expense> GetUserExpenses() 
     { 
      return new List<Expense>(); 
     } 
    } 

ExpenseController에서하지 ExpenseControllerStub에서 인스턴스를 생성하고 그것을 작동합니다 : 그것은 같이한다

[TestMethod] 
    public void ExpenseIndex() 
    { 
     // Arrange 
     ExpenseControllerStub controller = new ExpenseControllerStub(); 

     // Act 
     ViewResult result = controller.Index() as ViewResult; 

     // Assert 
     Assert.IsNotNull(result); 
    } 

이 수동으로 수행하는 방법이다. 당신은 이것에 대한 조롱 프레임 워크를 사용해야하는 경우, 당신은() 대중이 보호되지 GetUserExpenses을해야합니다, 빈 상태 (empty)의 비용 목록을 반환하는 설정을, 무언가 같이 :

var mock = new Moq.Mock<ExpenseController>(); 
mock.Setup(m => m.GetUserExpenses().Returns(new List<Expense>()); 

그러나 나는 선호하지 않는다 이 메서드를 public으로 만들기 위해서! Moq가 보호 된 방법을 설정/설정하는 방법이있을 수 있지만이를 알지 못합니다.

편집 : 더 나은 해결책은 비용 저장소를 완전히 추상화하는 것입니다.이 경우 조롱은 곧바로 진행됩니다.

또 다른 해결 방법은 컨트롤러 생성자에 DbContext를 삽입하고 조롱 프레임 워크를 사용하여 비용 및 DbSet을 조롱하는 것입니다. 이 작업을 수행 할 샘플을 찾을 수 있습니다. here

편집 # 2 : MVC 테스트를 더 쉽게하기 위해 TestStack.FluentMVCTesting 또는 MvcContrib.TestHelper을 사용할 수도 있습니다.

+0

답변을 주셔서 감사 합니다만 현재 문제는 해결되지만 실용적인 해결책은 아닙니다. 나는 당신이 그걸 도울 수 있다면 마지막에 언급했던대로 저장소를 추상화하는 방법에 대해 현재 읽고있다. 나는 매우 감사 할 것이다. :) – Baso

+1

아니요, 이것은 특히 레거시 코드로 작업 할 때 매우 실용적인 솔루션입니다. 이것은 당신이 할 수있는 몇 안되는 것들 중 가장 간단한 것일 수 있습니다. 저장소 구현에는 필요에 따라 다양한 변형이 있으며 EF는 이미 일반 저장소 (DbSet)를 구현합니다. 어쨌든, DbContext 및 DbSet을 조롱하는 방법에 대한 외부 링크를 사용하여 답변을 업데이트했습니다. –

관련 문제