2013-02-25 15 views
10

Ho la seguente classe e test. Voglio testare il passaggio di un valore null come parametro al costruttore e mi aspetto un ArgumentNullException. Ma dal momento che utilizzo il metodo CreateAnonymous dell'Autofixture, ho invece un TargetInvocationException.Test di autofirmazione per parametro costruttore non valido

Qual è il modo corretto di scrivere questo tipo di test?

public sealed class CreateObject : Command { 
    // Properties 
    public ObjectId[] Ids { get; private set; } 
    public ObjectTypeId ObjectType { get; private set; } 
    public UserId CreatedBy { get; private set; } 

    // Constructor 
    public CreateObject(ObjectId[] ids, ObjectTypeId objectType, UserId createdBy) { 
     Guard.NotNull(ids, "ids"); 
     Guard.NotNull(objectType, "objectType"); 
     Guard.NotNull(createdBy, "createdBy"); 

     Ids = ids; 
     ObjectType = objectType; 
     CreatedBy = createdBy; 
    } 
} 

[TestMethod] 
[ExpectedException(typeof(ArgumentNullException))] 
public void constructor_with_null_ids_throw() { 
    fixture.Register<ObjectId[]>(() => null); 
    fixture.CreateAnonymous<CreateObject>(); 
} 
+3

Farei convalide di questo tipo di clausola Guard a un livello superiore. Vedere http://stackoverflow.com/a/11455580/11635. Inoltre, ['ExpectedExceptionAttribute' è una cattiva idea - se non puoi usare xUnit almeno usa qualche forma di' Assert.Throws'] (http://stackoverflow.com/a/113616/11635) (Sì, non ne so nè di questi confrontano la tua domanda riguardo a "TargetInvocationException" che è il motivo per cui non si tratta di una risposta, ma il primo passo è quello di spostare per chiarire il codice in modo da poter lavorare con esso - anche se questo è un bug, ti consigliamo di produrre una risposta più chiara e una soluzione temporanea per il provvisorio –

risposta

11

IMO, Ruben Bartelink's comment è la risposta migliore.

Con AutoFixture.Idioms, è possibile fare questo, invece:

var fixture = new Fixture(); 
var assertion = new GuardClauseAssertion(fixture); 
assertion.Verify(typeof(CreateObject).GetConstructors()); 

Il metodo Verify vi fornirà un messaggio molto dettagliato un'eccezione se qualsiasi argomento del costruttore in qualsiasi costruttore manca una clausola Guard.


FWIW, AutoFixture utilizza ampiamente riflessione, quindi non mi considero un bug che si getta una TargetInvocationException. Mentre potrebbe scartare tutte le istanze TargetInvocationException e ridisporre le loro proprietà InnerException, ciò significherebbe anche l'eliminazione di informazioni (potenzialmente) preziose (come la traccia dello stack AutoFixture). Ho preso in considerazione questo, ma non voglio prendere AutoFixture in quella direzione, proprio per questo motivo. Un cliente può sempre filtrare le informazioni, ma se le informazioni vengono rimosse prematuramente, nessun cliente può recuperarle.

Se preferite l'altro approccio, non è troppo difficile da scrivere un metodo di supporto che scarta l'eccezione - forse qualcosa di simile:

public Exception Unwrap(this Exception e) 
{ 
    var tie = e as TargetInvocationException; 
    if (tie != null) 
     return tie.InnerException; 
    return e; 
} 
+0

E il controllo del valore ParamName? Se lancio la nuova ArgumentNullException senza specificare ParamName o con valore errato, il test continuerà a passare. –

+0

Se lo fai, uno strumento di analisi del codice statico può capirlo IIRC, Visual Studio Code Analysis lo fa. –

+0

Ma dal punto di vista TDD non è la soluzione perfetta :) Certo, io uso gli strumenti di analisi statica (R # avverte su valori errati), ma voglio controllare ParamName in test unitari. È possibile in AutoFixture? –

0

mi sono imbattuto in questo mentre ero alla ricerca di qualcosa di simile. Vorrei aggiungere che, combinato con automoqcustomization e xunit, anche il codice sottostante funziona e risulta molto più pulito.

[Theory, AutoMoqData] 
    public void Constructor_GuardClausesArePresent(GuardClauseAssertion assertion) 
    { 
     assertion.Verify(typeof(foo).GetConstructors()); 
    } 

Hai solo bisogno di creare l'attributo AutoMoqData come segue.

public class AutoMoqDataAttribute : AutoDataAttribute 
    { 
     public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization())) 
     { 

     } 
    } 
Problemi correlati