Crad's .NET Blog

L'UGIblog di Marco De Sanctis
posts - 190, comments - 457, trackbacks - 70

Ancora sui Mock objects

Espando un pochino il concetto espresso nel precedente post a beneficio di chi sia ignaro della questione

Nel mio caso specifico, ho la necessità di connettermi ad un servizio remoto che mi restituisce un documento XML, che viene poi a sua volta interpretato per creare una serie di entities del mio object model. La classe di cui può avvalersi l'utilizzatore per ottenere le previsioni del tempo accetta nel costruttore un oggetto di tipo IWeatherConnector, che sarà poi fisicamente responsabile di inviare una richiesta Http, ottenere una risposta e restituire l'XML di processare:

public class WeatherProvider: IWeatherProvider
{
    
public WeatherProvider(IWeatherConnector connector)
    {
        ....
    }

    
public CurrentConditions GetCurrentConditions
        (
string locationId, UnitType unitType)
    {
        
// metodo da testare, in cui utilizzo l'istanza di
        // IWeatherConnector passata nel costruttore
    
}

    
public Forecast[] GetForecasts
        (
string locationId, UnitType unitType, int days)
    {
        
// altro metodo da testare... stesse caratteristiche 
        // del precedente
    
}
}

Ora... nel mio Unit Test voglio verificare che il metodo GetCurrentConditions riesca ad effettuare il parsing dell'XML in maniera corretta, restituendo l'istanza di CurrentConditions che mi aspetto. Come posso fare? sicuramente non posso collegarmi ogni volta a Weather.com per eseguire i test, perché il collegamento potrebbe essere down, il mio test fallirebbe, otterrei un rosso, ma il mio metodo magari funziona benissimo, avrei quindi un test dal risultato *NON DETERMINISTICO* perché influenzato da fattori esterni!

Cosa fare allora? ho bisogno di un Mock Object, di un IWeatherConnector fittizio che mi permetta di isolare la mia classe e di testarla autonomamente. Ho usato NMock (perché avevo già un piccolo esempio di Andrea, come detto), in realtà ne esistono altri, tra cui Rhino-Mocks del bravo Ayende Rahien (sì, lo stesso di NHibernate Query Analyzer). NMock dicevo... bene... lo uso nella Fixture per costruire il mio connector fasullo:

private IWeatherConnector getWorkingConnector(string expectedResponse)
{
    DynamicMock mockGenerator = 
new DynamicMock(typeof(IWeatherConnector));

    XmlDocument resp = 
new XmlDocument();
    resp.LoadXml(expectedResponse);
    mockGenerator.SetupResult("InvokeService", resp, 
new Type[] { typeof(string) });
    
return (IWeatherConnector) mockGenerator.MockInstance;
}

Con 5 (CINQUE) righe di codice, dico a NMock di generare un tipo che implementi IWeatherConnector, così che possa usarlo nel costruttore della classe da testare, e che, eseguendo il metodo InvokeService restituisca la risposta expectedResponse specificata come parametro. E se volessi controllare come si comporta la mia classe under test se il connector solleva un'eccezione?

private IWeatherConnector getBrokenConnector()
{
    DynamicMock mockGenerator = 
new DynamicMock(typeof(IWeatherConnector));

    mockGenerator.ExpectAndThrow("InvokeService", 
        
new TimeoutException(), new Type[] { typeof(string) });
    
return (IWeatherConnector)mockGenerator.MockInstance;
}

Ora dico al mockGenerator che l'istanza generata deve sollevare una TimeoutException ad ogni esecuzione del metodo InvokeService!

Mica male, eh?

powered by IMHO 1.3

Print | posted on lunedì 5 giugno 2006 17:58 | Filed Under [ .Net 2.0 Misc ]

Powered by:
Powered By Subtext Powered By ASP.NET