2016-09-18 1 views
1

Ich lerne Unit-Tests und Moq für ASP.NET MVC 5 zu verwenden. Ich versuche, meinen ersten Komponententest für die Indexaktion eines meiner Controller zu schreiben.Wie kann ich Moq verwenden, um meine Indexaktion zu testen, die eine Liste aus der Datenbank zurückgibt?

Hier ist der Code für die Indexaktion.

[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())); 
    } 
} 

Alles, was ich tun möchte, ist überprüfen nur, dass die zurückgegebene Ansicht

So etwas wie diese

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

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

     // Assert 
     Assert.IsNotNull(result); 
    } 

Natürlich nicht null ist, dies nicht funktioniert, weil die Verbindung zur Datenbank und die Verwendung der ApplicationUserId so würden Sie mir helfen, moq und Unit-Test diese Aktion oder empfehlen Sie mir ein Tutorial, wo ich mich mit Spott in ASP.NET MVC vertraut machen kann.

+0

Abstrakt die Abhängigkeit der Datenbank/dbcontext und dann können Sie das in den Controller – Nkosi

+0

@Nkosi Mock Würdest du mir bitte weitere Informationen zur Verfügung stellen. Vielen Dank. – Baso

+0

Ihr Controller hat eine harte Abhängigkeit von dem 'ApplicationDbContext', was es schwierig macht, in Ihrem Komponententest herumzuspielen. Sie müssen Ihren dbcontext so umformen, dass er von einer Abstraktion erbt, die die Funktionalität von dbcontext verfügbar macht, und dann hängt der Controller von dieser Abstraktion ab und nicht von der Konkretion. von dort können Sie ein DI-Framework verwenden, um die Abhängigkeit in Ihren Controller zu injizieren, und jetzt haben Sie auch die Flexibilität, die Abstraktion durch eine Scheinimplementierung in Ihren Tests zu ersetzen, wenn es sein muss – Nkosi

Antwort

1

Eine Möglichkeit, dies zu tun ist, um abstrakte die Abhängigkeit in einer virtuellen Methode, zum Beispiel kapseln: eine virtuelle Methode erstellen, die die Benutzerkosten zurückgibt, sollten jetzt Ihren Controller wie folgt aussehen:

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()); 
     } 
    } 

Dann Erstellen Sie eine Stub-Klasse, die von Ihrem Controller abgeleitet ist, und überschreiben Sie die GetUserExpenses() -Methode. Es soll wie folgt aussehen:

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

Jetzt in Ihrem Gerät zu testen, erstellen Sie die Instanz von ExpenseControllerStub nicht aus ExpenseController, und es sollte funktionieren:

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

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

     // Assert 
     Assert.IsNotNull(result); 
    } 

Dies ist, wie es manuell zu tun. Wenn Sie einen Mockframework dafür verwenden müssen, müssen Sie GetUserExpenses() public nicht geschützt machen, dann ein Setup machen eine leere Kosten Liste zurückzukehren, so etwas wie:

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

Aber ich ziehe nicht um diese Methode zu veröffentlichen! Möglicherweise gibt es eine Möglichkeit für Moq, geschützte Methoden zu konfigurieren/einzurichten, aber ich bin mir dessen nicht bewusst.

Edit: Eine bessere Lösung ist es, das Ausgaben-Repository vollständig zu abstrahieren, in diesem Fall wird es einfach sein, es zu verspotten.

Eine andere Lösung ist es, den DbContext in den Controller-Konstruktor zu injizieren und ein Spott-Framework zu verwenden, um es und die Ausgaben DbSet zu verspotten. Sie können eine Probe finden, dies zu tun here

Edit # 2: Sie auch TestStack.FluentMVCTesting oder MvcContrib.TestHelper verwenden, können Sie MVC Prüfung zu erleichtern.

+0

Vielen Dank für die Antwort, obwohl es das aktuelle Problem lösen wird, aber es ist nicht wirklich eine praktische Lösung. Ich lese gerade darüber, wie man das Repository abstrahiert, wie Sie am Ende erwähnt haben, wenn Sie damit helfen könnten, wäre ich so dankbar :) – Baso

+1

Nein, das ist eine sehr praktische Lösung, besonders wenn Sie mit altem Code arbeiten. Dies kann das einfachste der wenigen Dinge sein, die Sie dann tun können. Die Repository-Implementierung hat viele Variationen - abhängig von Ihren Bedürfnissen - und EF implementiert bereits ein generisches Repository für Sie (DbSet). Wie auch immer, ich habe meine Antwort mit einem externen Link aktualisiert, wie man DbContext und DbSet vortäuscht –

Verwandte Themen