2012-01-07 12 views
6

Chcę utworzyć kod testowalny jednostki, który wyśmiewa połączenia z klasami .Net System.IO, więc mogę naprawdę testować jednostki zamiast zależeć od systemu plików. Używam klas SystemWrapper do owijania wokół klas BCL.Jak utworzyć testowalny kod za pomocą klas .Net IO?

Próbuję uzyskać prosty przykład pracy, aby sprawdzić, czy plik istnieje.

Problem polega na tym, że wstrzyknięcie zależności w klasie nie działa, ponieważ utworzenie instancji zależnej (przez StructureMap) wymaga znajomości parametru konstruktora, który nie będzie dostępny w tym czasie, brak domyślnego konstruktora.

przykładowy kod:

// don't want to create dependency here like so 
//IFileInfoWrap fileInfoWrap = new FileInfoWrap(filename); 

// using service locator (anti-pattern?!) since it can't be 
// injected in this class 
var fileInfoWrap = ObjectFactory.GetInstance<IFileInfoWrap>(
    new ExplicitArguments(new Dictionary<string, object> 
    { 
     {"fileName", filename} 
    })); 

Console.WriteLine("File exists? {0}", fileInfoWrap.Exists); 

Co mi się nie podoba to, że zależność nie jest wstrzykiwany, ObjectFactory nie powinno tu być (ale nie widzę innego sposobu tworzenia tego). ExplicitArguments sprawia, że ​​jest on brudny, a nazwa-argumentu jest ciągiem magicznym.

Dla mnie to do pracy klasę StructureMap config musi wiedzieć explict który konstruktor chcę użyć (I właśnie rozpoczęła z StructureMap więc to nie może być właściwym sposobem, aby ustawić go w górę):

ObjectFactory.Initialize(x => 
{ 
    x.Scan(scan => 
    { 
     scan.AssembliesFromPath("."); 
     scan.RegisterConcreteTypesAgainstTheFirstInterface(); 
     scan.WithDefaultConventions(); 
    }); 

    // use the correct constructor (string instead of FileInfo) 
    x.SelectConstructor(() => new FileInfoWrap(null as string)); 

    // setting the value of the constructor 
    x.For<IFileInfoWrap>() 
     .Use<FileInfoWrap>() 
     .Ctor<string>("fileName") 
     .Is(@"."); 
}); 

Czy ktoś znalazł lepsze rozwiązanie, aby utworzyć testowalny kod na klasach System.IO? Wiem, że część problemu tkwi w projektowaniu klas System.IO.

+3

SystemWrapper zawiera przede wszystkim bardzo nieszczelne abstrakcje. Byłoby znacznie prostsze i łatwiejsze modelowanie IO przeciwko strumieniom, TextWriter, TextReader itp. Klasy te są już abstrakcyjne, całkowicie eliminując potrzebę SystemWrapper. –

+0

Kolejny głos na strumienie –

+0

Moje odkrycia z SystemWrapper są takie, że wydaje się być ładnym opakowaniem z interfejsami, jednak jest to ślepy zaułek ze względu na sposób działania oryginalnych klas. Na przykład zwracanie tablicy obiektów FileInfo nie może być wyśmiewane poprawnie. Przetwarzanie mojej własnej, bardziej uproszczonej owijki, która nie musi naśladować istniejących klas, podczas gdy więcej pracy prowadzi do praktycznego rozwiązania IMHO. –

Odpowiedz

5

Podejście, którego bardzo skutecznie użyłem, to przetasowanie własnych typów proxy dla typów znalezionych w System.IO i innych częściach FCL. Na przykład. Chcę wziąć zależność od System.IO.File. Tworzę bibliotekę o nazwie System.IO.Proxies i dodaję typ betonu File i interfejs IFile. Interfejs IFile udostępnia elementy równoważne wszystkim tym, których potrzebuję od System.IO.File, a typ betonu implementuje te elementy, wykonując jedynie wywołania metody przesyłania do System.IO.File. System.IO.Proxies jest wykluczone z testowania jednostkowego i zasięgu kodu. W moim zespole konsumującym biorę zależność tylko na System.IO.Proxies i, konkretnie, biorę tylko zależność od IFile. W ten sposób mogę z łatwością kpić z tej zależności i osiągnąć 100% pokrycia kodu dla mojego zużywającego się zespołu.

(Zauważ, że jest to wersja dostosowane moja more general answer w poprzednim pytaniu.)

+0

Tak, tak też skończyłem. Można delegować połączenie do np. System.IO.FileInfo do oddzielnej klasy. Przetwarzanie własnego serwera proxy wymaga utworzenia proxy dla każdej klasy, co może być dość pracochłonne, oczywiście może rosnąć wraz z potrzebną funkcjonalnością z klasy BCL. Również, gdy zwracane są inne typy (FileInfo), wszystkie wymagane atrybuty powinny zostać utworzone w proxy (Name, FullName, Length). Można by sądzić, że ten problem zostanie "rozwiązany", unikając już proxy dla wszystkich. –

+0

Z powodzeniem zastosowano to samo podejście. Zwykle nie potrzebujesz wszystkich metod/właściwości/zdarzeń klasy takiej jak _File_. A dla garstki, której potrzebujesz, naprawdę łatwo jest pisać takie opakowania. –

Powiązane problemy