2009-08-05 5 views
14

Desidero caricare un file XML esterno in un test di unità per testare un codice di elaborazione su tale XML. Come ottengo il percorso del file?Come eseguire il MapPath in un test di unità in C#

Di solito in una web app che vorrei fare:

XDocument.Load(Server.MapPath("/myFile.xml")); 

Ma ovviamente nel mio test di unità non ho alcun riferimento alla Server o HttpContext così come posso tracciare un percorso in modo che non devo specificare il percorso completo?

UPDATE:

Voglio solo mettere in chiaro che il codice In realtà sto testando è per una classe di parser XML, qualcosa di simile:

public static class CustomerXmlParser { 
    public static Customer ParseXml(XDocument xdoc) { 
    //... 
    } 
} 

Quindi, per testare questo ho bisogno analizzare un XDocument valido. Il metodo testato non accede al file system stesso. Potrei creare l'XDocument da una stringa direttamente nel codice di test, ma ho pensato che sarebbe stato più semplice caricarlo da un file.

risposta

24

Un'altra idea sarebbe quella di utilizzare la dipendenza injecti sopra.

public interface IPathMapper { 
string MapPath(string relativePath); 
} 

E poi semplicemente utilizzare 2 implementazioni

public class ServerPathMapper : IPathMapper { 
    public string MapPath(string relativePath){ 
      return HttpContext.Current.Server.MapPath(relativePath); 
    } 
} 

E poi è necessario anche l'implementazione finto

public class DummyPathMapper : IPathMapper { 
    public string MapPath(string relativePath){ 
     return "C:/Basedir/" + relativePath; 
    } 
} 

E poi tutte le funzioni che devono mappare il percorso di avrebbe semplicemente bisogno di avere accesso a un'istanza di IPathMapper: nella tua app Web deve essere ServerPathMapper e nella tua unità viene verificato DummyPathMapper - DI base (Dependency Injection).

+0

Qual è il vantaggio di Interfaccia qui ?. Invece, possiamo semplicemente creare 2 file di classe e chiamarli di conseguenza 1 da un'app reale e 1 dall'endpoint del test? Qualcuno può spiegare il vantaggio di questa interfaccia qui? –

+0

Utilizzando le classi è necessario decidere di ereditare una classe dall'altra per rendere felice il sistema di tipi. Avere l'implementazione del test eredita da quella reale o viceversa non è davvero una soluzione ideale. Facendolo in questo modo non è necessario che le implementazioni reali e di test vengano a conoscenza l'una dell'altra, ma hanno solo un'interfaccia comune che devono soddisfare. – kastermester

5

Personalmente, sarei molto cauto nell'avere qualsiasi codice che si basi su un archivio di risorse back-end , che sia un file system o un database - si sta introducendo una dipendenza nel test dell'unità che è probabile che porti a falsi negativi, ovvero test non riusciti a causa del codice di test specifico ma perché il file non è presente o il server non è disponibile ecc.
Vedere questo link per IMO una buona definizione di cosa è un test di unità e, cosa più importante, non è

Il test dell'unità deve testare una funzionalità atomica e ben definita che non verifica se un file può essere caricato. Una soluzione è "prendere in giro" il caricamento del file - ci sono diversi approcci a questo però, personalmente mi prenderei in giro solo l'interfaccia con il file system che stai usando e non provo a fare un vero e proprio sdoganamento completo del filesystem - here's un post SO buono e here's una buona discussione SO sul file system beffardo

Speranza che aiuta

+0

Giusto per essere chiari, non sto testando se l'XML può essere caricato o testare il contenuto del file, sto testando un pezzo di codice che richiede l'analisi di un XDocument. Il file si trova con i test se non viene caricato, quindi il test mostrerà un errore e non un falso negativo. Mi rendo conto che non è l'ideale, ma non conosco altro modo. Ho aggiornato la mia domanda. –

+0

Di sicuro è questo il punto, non è vero? Il tuo codice sta testando il tuo parser, quindi da dove ottiene i dati xml è irrilevante e non dovrebbe fallire perché il file non esiste. Personalmente, andrei con l'opzione stringa o potrei averla come risorsa incorporata – zebrabox

3

di solito per i test unitari aggiungo il file XML come risorse incorporate al progetto e caricarli utilizzando un metodo come questo:

public static string LoadResource(string name) 
{ 
    Type thisType = MethodBase.GetCurrentMethod().DeclaringType; 
    string fullName = thisType.Namespace + "." + name + ".xml"; 

    using (Stream stream = thisType.Module.Assembly.GetManifestResourceStream(fullName)) 
    { 
     if(stream==null) 
     { 
     throw new ArgumentException("Resource "+name+" not found."); 
     } 

     StreamReader sr = new StreamReader(stream); 
     return sr.ReadToEnd(); 
    } 
} 
2

Modifica: Sto iniziando da zero poiché immagino di aver interpretato la tua domanda nel modo sbagliato inizialmente.

Il modo migliore per caricare un file XML nel test dell'unità per iniettarlo poi in alcune delle classi è utilizzare l'attributo DeploymentItem nei test delle unità MS.

Questo sarà simile al seguente:

[TestMethod] 
[DeploymentItem(@"DataXmlFiles\MyTestFile.xml", "DataFiles")] 
public void LoadXMLFileTest() 
{ 
    //instead of "object" use your returning type (i.e. string, XDocument or whatever) 
    //LoadXmlFile could be a method in the unit test that actually loads an XML file from the File system 
    object myLoadedFile = LoadXmlFile(Path.Combine(TestContext.TestDeploymentDir, "DataFiles\\MyTestFile.xml")); 

    //do some unit test assertions to verify the outcome 
} 

non ho la prova il codice ora in un debugger, ma dovrebbe funzionare.

Edit: Btw, quando si utilizza DeploymentItem considerano questo post here.

1

Classi:

internal class FakeHttpContext : HttpContextBase 
{ 
    public override HttpRequestBase Request { get { return new FakeHttpRequest(); } } 
} 

internal class FakeHttpRequest : HttpRequestBase 
{ 
    public override string MapPath(string virtualPath) 
    { 
     return /* your mock */ 
    } 
} 

Usage:

[TestMethod] 
public void TestMethod() 
{ 
    var context = new FakeHttpContext(); 
    string pathToFile = context.Request.MapPath("~/static/all.js"); 
} 
Problemi correlati