2013-01-10 6 views
8

Sto usando Moq per verificare se un metodo viene chiamato nel mio unittest. In questo caso specifico voglio testare se il metodo in prova registra un errore tramite log4net. Il problema è che ciò può essere fatto chiamando log.Error o log.ErrorFormat. O va bene.Come posso verificare se uno dei due metodi è stato chiamato?

Come posso verificare questo? So solo come verificare che siano stati entrambi chiamati.

var logMock = new Mock<ILog>(); 

var myClass = new MyClass(logMock.Object); 

myClass.MyMethod(); 

logMock.Verify(log => log.Error(It.IsAny<object>())); 
logMock.Verify(log => log.ErrorFormat(It.IsAny<string>(), It.IsAny<object>())); 

Ora che ci penso, entrambi hanno un mucchio di sovraccarichi, non mi importa se uno qualsiasi dei sovraccarichi sono chiamati sia (sto iniziando a dubitare che questo è un buon test).

Grazie in anticipo.

EDIT: ho pensato a qualcosa di brutto:

try 
{ 
    logMock.Verify(log => log.Error(It.IsAny<object>())); 
} 
catch (Moq.MockException ex) 
{ 
    logMock.Verify(log => log.ErrorFormat(It.IsAny<string>(), It.IsAny<object>())); 
} 

forse posso avvolgere questo in una sorta di metodo di estensione ... per esempio VerifyAny.

risposta

9

Si potrebbe registrare un callback per ogni metodo di errore valido che imposta un flag:

// Arrange 
bool errorFlag = false; 
logMock 
    .Setup(l => l.Error(It.IsAny<object>())) 
    .Callback((object o) => errorFlag = true); 

/* repeat setup for each logMock method */ 

// Act 
myClass.MyMethod(); 

// Assert 
Assert.IsTrue(errorFlag); 

Naturalmente, questo sarà ancora noioso se si dispone di molti sovraccarichi per coprire.

EDIT: E per il divertimento, ecco un metodo di estensione per Mock<T>.VerifyAny:

public static class MockExtensions 
{ 
    public static void VerifyAny<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) 
     where T: class 
    { 
     List<MockException> exceptions = new List<MockException>(); 
     bool success = false; 
     foreach (var expression in expressions) 
     { 
      try 
      { 
       mock.Verify(expression); 
       success = true; 
       break; 
      } 
      catch (MockException ex) 
      { 
       exceptions.Add(ex); 
      } 
     } 

     if (!success) 
     { 
      throw new AggregateException("None of the specified methods were invoked.", exceptions); 
     } 
    } 
} 

Usage:

[TestMethod] 
public void FooTest() 
{ 
    Mock<IFoo> fooMock = new Mock<IFoo>(); 
    fooMock.Object.Bar1(); 

    fooMock.VerifyAny(
     f => f.Bar1(), 
     f => f.Bar2()); 
} 
+0

La mia versione di VerifyAny era circa la stessa, tranne che non ho pensa al vincolo del tipo e raccogli le eccezioni. L'utilizzo è in realtà un po 'più pulito rispetto all'approccio CallBack. Oppure la cattura di eccezioni come quella è una cattiva idea? Hmmm .... –

+0

@MatthijsWessels La cattura di eccezioni attese come quella sembra un po 'sporca, ma per un progetto di unit test non mi preoccuperei troppo delle prestazioni. Forse sarebbe meglio avere un metodo 'Mock .SetupAny' invece che inietti i callback e restituisca un bool. –

1

se sono specificamente testando che un errore specifico è stato registrato, perché non hanno 2 test, uno che garantisce che venga chiamato log.Error e uno che assicura che venga chiamato log.ErrorFormat, presumo che sia possibile controllare quale viene chiamato in base all'input.

se si vuole ancora verificare uno o l'altro, si può semplicemente utilizzare questo approccio, fa esattamente quello che ti serve:

Verify that either one method or the other was invoked in a unit test

+1

Il requisito per 'MyClass' è che registra un messaggio di registro del livello di errore. Errore, ErrorFormat e il loro sovraccarico lo fanno tutti. Non importa per me quale viene usato. L'approccio CallBack come specificato nella risposta che hai collegato sembra che possa fare ciò che voglio. Anche se ora sto dubitando tra VerifyAll di Scott Wegner o CallBacks. –

Problemi correlati