2009-06-06 12 views
1

Nach dem Lesen eines anderen SO-Threads (Should parameters/returns of collections be IEnumerable<T> or T[]?) bin ich der Meinung, dass ein Unit-Test "Problem" wahrscheinlich damit zu tun hat, was der Thread als "späte Bewertung" bezeichnet.IEnumerable <T> Auswertung und Komponententest

Ich übergebe drei verschiedene, aber verwandte Varianten IEnumerables von einer Presenter-Klasse an meine Benutzeroberfläche. Die beste Nachricht ist, dass es funktioniert, aber ich finde keinen Weg, einen Komponententest zu machen, der definitiv beweist, dass das richtige IEnumerable zur richtigen Zeit passiert wird.

Ich habe eine Fassade mit dem folgenden Verfahren:

public IEnumerable<DynamicDisplayDto> NonProjectDtos 
{ 
    get 
    { 
     var activities = LeaveTimeActivities.Cast<TimeSheetActivityBase>() 
      .Concat(AdministrativeActivities.Cast<TimeSheetActivityBase>()); 
     return _assembler.ToActivityDtoCollection(activities, _timeSheet); 
    } 
} 

In einem Moderator, laden I ein Widget:

private ITimeSheetMatrixWidget _nonProjectActivityMatrix; 
public ITimeSheetMatrixWidget NonProjectActivityMatrix { 
    set { 
     .. 
     // load the activities 
     _nonProjectActivityMatrix.Load(Facade.NonProjectDtos); 
     ... 
    } 
} 

Dann wird der Test an einer verspotteten Matrix (mit Rhino Mocks):

Wenn ich in den Debugger schaue, kann ich sehen, dass das IEnumerable laden arg Domain.TransferObjec auswertet ts.TimeSheetDtoAssembler + d__1, was auch der Teil von Rhinos Nachricht ist, dass der Methodenaufruf fehlgeschlagen ist.

Liegt dies an der späten Auswertung? Gibt es eine einigermaßen elegante Möglichkeit, dies genauer zu testen?

Ich würde auch gerne die Zwischenbewertung besser verstehen, die sehr ähnlich der Methode aussieht, die sie zusammengebaut hat (im obigen Fassadencode).

Antwort

3

Sind die Facade-Objekte im Test und im zu testenden Objekt identisch - d. H. Injizieren Sie auch das Facade-Objekt? Wenn die Objekte unterschiedlich sind, würde dies das Problem verursachen, das Sie sehen. Wenn sie identisch sind, können Sie entweder die Enumeration in der Methode (ToList() verwenden).

+0

Hallo. Gute Nachrichten, schlechte Nachrichten - die Fassade wird auch gespritzt. Ich bin auf der Suche nach einem Weg, um die Vorteile der IEnumerable zu halten, aber ich kann es als ein Parameter testen, wenn ich kann. Ich habe versucht, ToList() in den Komponententestaufruf zu setzen, aber das ändert nur den Fehler in einen verständlicherer Parameter, der nicht übereinstimmt. Wo hast du das mit der ToList() gemeint? Thx – Berryl

+0

Ich vermute, dass jemand in der Kette gibt es eine "neue" Operation, die ein anderes Element erzeugt. Anstatt viel Zeit darauf zu verwenden, sollten Sie erwägen, die Überprüfung auf keine Argumente zu belassen und sicherzustellen, dass die richtigen Elemente in der Enumeration verfügbar sind. Ist das nicht wirklich die richtige Definition hier - dass die richtigen Elemente geladen sind? – tvanfosson

+0

Ich sehe nicht, wie ich überprüfen kann, ob die Elemente in einem Test der Schnittstelle geladen wurden. Ich habe nur Zugriff auf IEnumerable als Argument für den Methodenaufruf, soweit ich das sehen kann. Wie würdest du das machen? – Berryl

2

Ich will nur Ihre spät Auswertung Sorge Adresse:

Beide Cast und Concat (und viele System.Linq.Enumerable Methoden) return-Instanzen, die mit IEnumerable<T> entsprechen. Die tatsächlichen Ergebnisse der Aufzählung dieser Instanzen sind zurückgestellt. Warum werden diese Ergebnisse aufgeschoben und nicht eifrig bestimmt?

List<int> firstThreePrimes = Enumerable.Range(0, 1000000) 
    .Where(i => isPrime(i)) 
    .Take(3) 
    .ToList(); 

ToList aufzählt das IEnumerable<int> Ergebnis aus Take(3). Es werden keine Zahlen von Range erzeugt, gefiltert durch Where oder begrenzt durch Take, bis ToList dieses Ergebnis aufzählt.

Wenn die Ergebnisse von Range eifrig ermittelt wurden, würden wir 1000000 Ints generieren.

Wenn die Ergebnisse von Where eifrig bestimmt wurden, dann müsste die Laufzeit all diese 1000000-Ints an unsere isPrime-Methode senden.

Stattdessen sieht die Aufrufsequenz so aus.

Range 
return from Range 
Where 
return from Where 
Take 
return from Take 
ToList 
    GetEnumerator(Take) 
    GetEnumerator(Where) 
     GetEnumerator(Range) 
     return 0 
     isPrime(0) is false 
     return 1 
     isPrime(1) is false 
     return 2 
     isPrime(2) is true 
    2 is the first result 
    List.Add(2) 
     return 3 
     isPrime(3) is true 
    3 is the second result 
    List.Add(3) 
     return 4 
     isPrime(4) is false 
     return 5 
     isPrime(5) is true 
    5 is the third result 
    List.Add(5) 
    break; 

Dies ist mit einem Debugger einfach zu bestätigen. Geh einfach durch und beobachte die Anrufe zu isPrime.

Aus der Untersuchung Ihres Codes sieht es so aus, als ob _assembler.ToActivityDtoCollection wahrscheinlich das Ergebnis aufzählt und Sie wahrscheinlich keine verzögerte Ausführung erleben.

+0

Hallo David. Die verzögerte Ausführung klingt kühler als die späte Ausführung - ist das die richtige Beschreibung? Was ist mit der _assembler.ToActivityCollection() sagt Ihnen, dass das wahrscheinlich nicht der Fall ist? Ich bin auf der Suche nach einem Weg, um die Vorteile der IEnumerable zu halten, aber in der Lage, es als Parameter zu testen, wenn ich kann. Prost - – Berryl

+0

ToActivityCollection zählt entweder seine Eingabe auf und erstellt eine neue Sammlung oder es wandelt die Eingabe in die neue Sammlung um. Da Sie den tatsächlichen Implementierungstyp der Cast-Methode nicht steuern, ist es unwahrscheinlich, dass das Casting für Ihre Sammlung funktioniert. Ich folge daher, dass die Aufzählung erfolgen muss. (Es sei denn, die ActivityDtoCollection erhält nur die Enumerable und löst sie in einer verzögerten Weise auf, aber ich vermute, dass Sie das wissen würden, wenn es der Fall wäre). –

+0

ToActivityCollection erhält eine IEnumerable und zählt sie einzeln auf, wobei yield return ToDto (activity) verwendet wird, um ein IEnumerable zu erzeugen. Ich rate von dem, was ich von dir gelernt habe und was ich im Debugger sehe, dass dies eine verzögerte Ausführung ist. – Berryl

2

Rhino spottet funktioniert perfekt, obwohl es nicht immer in der Lage, zu wissen, warum Sie die falsche Syntax oder Einschränkungen verwendet haben :-)

Die Art und Weise ein IEnumerable Argument zu prüfen, für die Gleichstellung nur die folgenden Inline verwenden wird Einschränkung:

Arg<T>.List.Equal(yourList) 

Hier ist ein vollständiges Beispiel:

[Test] 
public void NonProjectMatrix_Injection_IsLoaded() 
{ 
    _nonProjectMatrix = MockRepository.GenerateMock<ITimeSheetMatrixWidget>(); 

    var dtos = _facade.NonProjectDtos; 
    nonProjectMatrix.Expect(x => x.Load(Arg<IEnumerable<DynamicDisplayDto>>.List.Equal(dtos))).Return(dtos.Count()); 

     new MatrixEntryService(_facade, _projectMatrix, _nonProjectMatrix, _totalMatrix); 

     _nonProjectMatrix.VerifyAllExpectations(); 
    } 

Also das Problem hatte nichts wirklich mit verzögerter Ausführung zu tun. Rhino spuckte nur alles aus, was er über einen Anruf wusste, der nicht so gemacht wurde, wie ich es ihm erwartet hatte, und so betrachtete der IEnumerable die Zeit des Erwartungsversagens.

Prost ..

Verwandte Themen