Moq: Ungültiges Setup für ein nicht überschreibbares Mitglied: x => x.GetByTitle ("asdf")

111

Ich bin mir nicht sicher, wie ich das beheben kann, indem ich versuche, einen Komponententest für die Methode "GetByTitle" durchzuführen.

Hier sind meine Definitionen:

public class ArticleDAO :  GenericNHibernateDAO(IArticle, int>, IArticleDAO
{
    public IArticle GetByTitle(string title)
    {
        IQuery query = Session.CreateQuery("...")
        return query.UniqueResult<IArticle>();
    }
}

public interface IArticleDAO
{
    IArticle GetByTitle(string title);
}

Gerätetest:

[Test]
public void can_load_by_title()
{
    _mockDaoFactory.Setup(x => x.GetArticleDao())
                                .Returns(_mockArticleDao.Object);
    _mockArticleDao.Setup(x => x.GetByTitle("some title"))
                                .Returns(article1.Object);

    _articleManager.LoadArticle("some title");

    Assert.IsNotNull(_articleManager.Article);
}

Das Ausführen des Tests gibt mir den Fehler:

System.ArgumentException: Invalid setup on a non-overridable member:
x => x.GetByTitle("some title")

Aktualisieren

Mein [Setup]sieht aus wie:

[Setup]
public void SetUp()
{
     _mockDaoFactory = new Mock<IDaoFactory>();
     _mockArticleDao = new Mock<ArticleDao>();

     _articleManager = new ArticleManager(_mockDaoFactory.Object);    
}
mrblah
quelle
2
Instanziieren Sie _mockDaoFactoryund _mockArticleDaoirgendwo? Verspotten Sie die Klasse oder die Schnittstelle
Tomas Aschan
Ja, ich habe die Daofactory und das MockarticleDao im [Setup] über die Schnittstelle verspottet. Das DAO wurde mit der Klasse durchgeführt.
Mrblah
@tomas Ich habe meine Frage mit dem Setup-Code aktualisiert.
Mrblah
2
Wie Sie in meiner Antwort sehen können, müssen Sie entweder die Benutzeroberfläche verspotten (das empfehle ich) oder die GetByTitleMethode markieren virtual.
Tomas Aschan
Es sieht auch so aus, als ob die erste Zeile in Ihrem Test in die Setup-Routine verschoben werden könnte ...?
Tomas Aschan

Antworten:

154

Um das Verhalten eines Scheinobjekts zu steuern (zumindest in Moq), müssen Sie entweder eine Schnittstelle verspotten oder sicherstellen, dass das Verhalten, das Sie steuern möchten, als virtuell markiert ist. In Ihrem Kommentar verstehe ich es so, dass das Instanziieren von ungefähr _mockArticleDaoso erfolgt:

_mockArticleDao = new Mock<ArticleDAO>();

Wenn Sie es so lassen möchten, müssen Sie die GetArticleMethode markieren virtual:

public class ArticleDAO :  GenericNHibernateDAO(IArticle, int>, IArticleDAO
{
    public virtual IArticle GetByTitle(string title)
    {
        // ...
    }
}

Andernfalls (und das empfehle ich) verspotten Sie stattdessen die Benutzeroberfläche.

_mockArticleDao = new Mock<IArticleDAO>();
Tomas Aschan
quelle
aber da das ArticleDAO von Generic .... erbt, verspotte ich, wenn ich die Schnittstelle verspotte, die Methoden im GenericNhibern. wird nicht zur Verfügung gestellt?
Mrblah
Da der Aufruf von GetArticleDAO aus der Factory ArticleDAO und nicht IArticleDAO zurückgibt, wird b / c articleDAO auch an eine abstrakte Klasse gebunden, die keine Ruhezustände enthält.
Mrblah
2
Wenn Sie die Schnittstelle nicht verspotten können, testen Sie möglicherweise das Falsche. Wenn Sie jedoch die Methode als virtuell markieren, wird das Problem behoben.
Tomas Aschan
+1 Tomas, ich muss einen Parameter in den ctor einfügen, daher musste ich in meinem Fall die tatsächliche Klasse verspotten und die Methoden auf virtuell setzen, da Sie keine Parameter in den ctor einer Schnittstelle einfügen können. Ist das der richtige Ansatz?
Houman
4
@Kave: Wenn Sie etwas in den Konstruktor einfügen müssen, testen Sie definitiv das Falsche. Verspotten Sie alles, was Sie dem Konstruktor geben, richten Sie sein Verhalten ein und testen Sie, ob sich diese Klasse so verhält, wie sie sollte. Wenn nötig, schreiben Sie eine neue Schnittstelle, die Sie vom Typ "injiziert" implementieren lassen, um auf alle Methodensignaturen zugreifen zu können.
Tomas Aschan