sobota, 30 listopada 2013

Typy transformacji pól w ETL


Dalszy ciąg krótkiego wprowadzenia do ETL. Tym razem chciałbym Ci troszkę więcej powiedzieć o transformacji, a dokładnie o transformacji elementów ściąganych z źródła,


Transformacje pojedynczych elementów w procesie ETL dzielą się na transformacje pojedynczego pola oraz transformacje wielu pól. Transformacje pojedynczego pola są bardzo proste. Najczęściej jest to pewna modyfikacja starej wartości w nową wartość. Oprócz wartości możemy zmienić sposób prezentacji tej wartości. Można np. zamienić stopnie Fahrenheita na stopnie Celisiusza. Takie zamiany nazywają się transformacjami algorytmicznymi (algorithmic transformation), które wymagają obliczeń matematycznych.



Szczególnym przypadkiem transformacji pojedynczego pola jest transformacja wymagająca dodatkowej tabeli z odnośnika wartości (table lookup). Poniżej przedstawiona jest transformacja, która zamienia wartość „code” ze źródła na wartość „name”, wykorzystując przy tym dodatkową tabelę z wartościami. Taka transformacja może zamieniać skróty państw na całe nazwy państw. Głównym zadaniem tej transformacji jest zwiększenie czytelności docelowych danych.



Wśród transformacji wielu pół mamy transformacje „wiele do jeden” (zapisuje się w postaci M:1). Modyfikacja ta ma za zadanie scalanie wartości z różnych pól i zapisanie ich w jednym polu. Taką transformację stosuje się, w celu identyfikacji danego elementu. Przykładem może być tworzenia unikatowego klucza, który składa się z nazwy produktu, producenta oraz numeru dodatkowego.



Drugim typem transformacji wielu pól jest transformacja z jednego pola do wielu pól (1:M). Przykładem takiej transformacji może być w sytuacji kiedy wartość źródła składała się z wielu zmiennych. Powodem, dla którego przechowuje się kilka wartości w jednym polu jest wydajność jej metody archiwizacji w bazie danych. Jednak przy takiej transformacji istnieje niebezpieczeństwo zmiany formatu przechowywania wartości w źródle co może prowadzić do zakłócenia przebiegu procesu.



Oczywiście istnieje transformacja "wiele do wielu", ale jest to połączenie dwóch powyższych typów transformacji.

czwartek, 28 listopada 2013

Przemilczana premiera PowerShell 4.0

Korzystałem z Windows 8 i miałem możliwość aktualizacji systemu do Windows 8.1. Po aktualizacji (oprócz problemów z Apple Boot Camp'em oraz powrotu starego-dobrego przycisku Start) to nie zauważyłem jakieś większej różnicy z poprzednią wersją.

Któregoś dnia wywołałem polecenie $PSVersionTable, a tu dostaje taki wynik:


Okazuje się, że mam PowerShell w wersji 4.0. Ta wersja należy do pakietu Windows Management Framework 4.0 i ten pakiet można pobrać stąd. Instalując Windows 8.1 dostajesz do tego PowerShell v4. Nowy PS jest domyślnie dostępny dla systemów operacyjnym Windows 8.1 oraz Windows Server 2012 R2.


Wiem, już dawno nie byłem na PowerShell Magazine, a polska grupa użytkowników PowerShell jeszcze ma do nadrobienia i mogłem sam zainteresować się nową wersja, ale dziwi mnie to, że premiera PS została przemilczano przez Microsoft i pozostawiona bez większego odzewu.


W najbliższych dniach sprawdzę co jest nowego w PS v4.


niedziela, 24 listopada 2013

Wyciągnięta nauka z statystyki

Zastanawiam się, po co te wszystkie metody statycznej analizy danych, szukanie reguł i zależności, grupowanie elementów i inne bardziej lub mniej złożone metody analizy skoro możemy mieć błędne dane. Najważniejszą naukę jaką zdobyłem z analizy danych jest to, że metody analizy dobrze działają, ale dane zostały źle uzyskane.

Znasz to przysłowie:

Statystyka nie kłamie,
ale kłamcy liczą.


albo jak J.Stalin mawiał:
Nieważne, kto głosuje, ważne, kto liczy głosy.

Nawet w dzisiejszych czasach da się oszukać ludzi na wielką skalę.



sobota, 23 listopada 2013

Data Marty - uszczuplona hurtownia danych

Wcześniej pisałem o procesie ETL i w nim wspominałem o Data Martach jako zbiorze załadowanych danych. Data Marty są nazywane tematycznymi hurtownie danych. Są tworzone w celu zapewnienia wsparcia procesu podejmowania decyzji osobom odpowiedzialnym za konkretny obszar biznesowy. W przypadkach, gdy tylko część danych jest poddawana analizie, warto rozważyć użycie data martów. Data Mart jest generowany na bazie hurtowni danych i zawiera zagregowane dane zorientowane na jeden wybrany temat, które są często wyświetlane oraz łatwo i szybko dostępne dla użytkowników.

Hurtownia danych operuje na poziomie wszystkich dostępnych danych, przy czym tematyczna hurtownia danych jest używana z reguły przez jeden obszar danych w niej zawartych dotyczących jednego konkretnego tematu biznesowego. Dla banków może to być obszar produktów kredytowych lub obszar transakcji kontraktów krótkoterminowych

Typową i najczęściej spotykaną architektura w korporacjach jest jedna globalna hurtowna danych i bezpośrednio zależne i czerpiących z niej dane Data Marty.



Powyżej przedstawiony jest sposób działania Data Martów. Dostęp do nich mają tylko wyznaczone osoby (lub grupy ludzi). Dział raportujący ma dostęp tylko do danych potrzebnych do tworzenia i przeglądania raportów. Dział analiz ma dostęp do Data Martów do analizy oraz raportów. Natomiast grupa administracyjna ma dostęp do wszystkich obszarów danych, gdyż zarządzają tymi danymi.

Kluczowym problemem jest utrzymanie spójności z hurtownią danych. Najważniejsze te kwestie to definicja danych, sposób aktualizacji oraz zarządzanie danymi. Sposobem na ominięcie części problemów jest tworzenie data martu niezależnego od hurtowni danych. Zdarza się to najczęściej, gdy do utworzenia data martu wymagane jest dodatkowe źródło danych spoza hurtowni. Wtedy tematyczne hurtownie danych są zasilane i zarządzane przez procesy ETL (Extract-Translate-Load). Należy jednak pamiętać o tym, że tego typu architektura jest narażone na ryzyko niespójności w rozumowaniu danych.

Najczęstsze powody tworzenia data martów to:
- większa denormalizacja
- dane z zagregowanymi danymi
- dane ze specyficznego okresu czasowego istnienia tych danych ( np. wartość akcji z poprzedniego roku)
- dane dostępne tylko dla specyficznej grupy (dane tylko dla analityków, testerów lub administratorów)

poniedziałek, 18 listopada 2013

Kodowanie w stylu Rammstein

Natrafiłem na zdjęcie przedstawiające kod, który się czyta w melodii utworu "du hast" zespołu Rammstein. Jako wielki fan tego zespołu bardzo mi się spodobał ten kod :)





niedziela, 17 listopada 2013

Co to jest ETL ?

Jak zaczynałem swoją pracę przy bardzo dużym projekcie ETL, to na początku nie wiedziałem co to jest i do tej pory nie ma zbyt dużo materiałów na ten temat. Przedstawię Ci krótki wstęp do ETL.

Skrót ETL oznacza ekstrakcję (extract), transformację (transform) i ładowanie (load). Jest to metoda procesowania danych. ETL składa się z 3 etapów:
1) ekstrakcja danych ze źródła danych - proces ten determinuje bazowe źródła dla hurtowni danych.
2) transformacja danych - etap ten ma za zadanie standaryzować dane, filtrację oraz sprawdzanie reguł biznesowych.
3) ładowanie danych do docelowego zbioru danych - etap zapisu wygenerowanych danych.




Na rysunku przedstawiony jest ogólny schemat działania procesu ETL. Do danych wejściowych (source) należą aplikacje (m.in. bazy danych), dodatkowe pliki oraz inne pomocne dane (m.in. metadane). Proces ETL przetwarza te dane i zapisuje je do wyznaczonego zbioru danych (target). Docelowy zbiór danych jest określony przez projektanta i może to być hurtowania danych, data mart lub zbiór plików zapisanych na serwerze. W praktyce 80% czasu spędzonego na rozwijaniu systemu BI jest poświęcone na rozwijanie procesu ETL. Jest to najbardziej czasochłonne zadanie przy budowaniu hurtowni danych i jednocześnie wymaga najwięcej wiedzy i doświadczenia. Bardzo często proces ETL nazywany jest procesem integracji danych, natomiast narzędzia ETL nazywane są platformą integracyjną.

Pierwsza częścią - ekstrakcja danych ze źródła danych - jest to najbardziej wymagający etap całego ETL. Większość hurtowni danych integruje dane z różnych źródeł danych. Każde źródło danych może się różnić organizacją danych czy sposobem dostępu. Najczęściej spotykanym źródłem danych są relacyjne bazy danych oraz pliki w formacie CSV. Celem ekstrakcji danych jest przekonwertowanie danych do ujednoliconego formatu, który jest przystosowany dla procesu transformacji.

Etap transformacji jest częścią opisującą przepływ danych, który zawiera zaaplikowane reguły biznesowe oraz szczegółowy opis kalkulacji i mapowania między danymi. Niektóre dane wymagają bardzo mało, a nawet brak modyfikacji danych. W większości przypadkach etap ten składa się pezynajmniej z jednego z poniższych zadań:
- wyselekcjonowanie tylko niektórych kolumn danych z tabeli
- transformacja sposobu zapisu danych. Jeżeli dana jest zapisana w postaci binarnej (1-0) to można zapisać słownie (Tak – Nie)
- transformacja formatu zapisu danych. Część dat jest zapisana w formacie skoordynowanego czasu uniwersalnego (UTC) lub czasu lokalnego
- wyliczenie nowej wartości, która jest zależna od wartości innych zmiennych
- sortowanie według danego kryterium
- łączenie danych z innymi danymi z innych źródeł - wzbogacanie danych
- rozdzielenie kolumny na kilka kolumn
- usuwanie zduplikowanych danych
- agregacja danych. Można wprowadzić data rollup ( wiersz podsumowujący dane)
- tworzenie unikatowego klucza (surrogate key) dla wszystkich elementów
- dokonywanie transpozycji oraz tworzenie tabeli przestawnej. Jest to proces, w którym kolumny zamieniane są na wiersze.

Faza ładowania danych odnosi się do zapisu do specjalnego obiektu docelowego (najczęściej jest to hurtowna danych). Obiekt docelowy jest zależny od wymagań danego projektu. Najczęściej istniejące dane w hurtowni danych są nadpisywane przez nowo wygenerowane. Inne hurtownie danych są czyszczone i na nowo załadowane, ale takie rozwiązanie niesie ze sobą problem „pustych danych”, gdyby wystąpił problem w etapie ładowania danych.

sobota, 16 listopada 2013

Szukanie referencji w projektach

Na moje szczęście programuje w solucji (.sln), która posiada ponad 100 projektów (.csproj). Czasami zdarza się szukać projektu, który korzysta ze specyficznej biblioteki lub może być nie spójne w referencjonowaniu (jeden projekt jest zależny od paczki nuget, a drugi od biblioteki zapisanej na dysku).

Poniżej jest skrypt w powershellu, który dla danej wzorca nazw projektów przeszukuje wszystkie csproje i zwraca wszystkie referencje jakie są w tych plikach. Przeszukiwanie rozpoczyna się od ścieżki uruchomienia skryptu.
function Get-Reference
{
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] 
     [string] $projectNamePattern
)

BEGIN {
     Write-Host "Getting project References" 
     $projectFileExtension = ".csproj"
}

PROCESS{
    $searchingProjectPattern = $projectNamePattern + $projectFileExtension 
    $projectFullNames = Get-ChildItem  -Filter $searchingProjectPattern -Recurse | %{$_.FullName}

    foreach ( $projectFullName in $projectFullNames)
    {

        if( Test-Path $projectFullName)
        {       
            $projectName = [System.IO.Path]::GetFileNameWithoutExtension($projectFullName)
            [xml]$projectXml = Get-Content $projectFullName
            $itemGroups = $projectXml.Project.ItemGroup
            
            foreach ( $itemGroup in $itemGroups)
            {
                if($itemGroup.Reference -ne $null)
                {
                    foreach( $ref in $itemGroup.Reference)
                    {
                      $Object = New-Object PSObject -Property @{
                           RefPath = $ref.HintPath 
                           Name = $ref.Include
                           ProjectPath = $projectFullName
                           ProjectContent = $projectXml 
                           ProjectName = $projectName
                      }
                      $Object
                    }
                }
                
                if($itemGroup.ProjectReference -ne $null)
                {
                    foreach( $ref in $itemGroup.ProjectReference)
                    {
                      $Object = New-Object PSObject -Property @{
                           Name = $ref.Name 
                           RefPath = $ref.Include
                           ProjectPath = $projectFullname
                           ProjectContent = $projectXml 
                           ProjectName = $projectName
                      }
                      $Object
                    }
                }
            }
        }
    }
}

END{    
Write-host "`n`nHintPath => RefPath and Include => Name FOR Reference"
Write-host "Include => RefPath and Name => Name FOR ProjectReference"
}
}

A poniżej jest przykład użycia. Dajmy na to, że chcesz znaleźć referencje, które w ścieżce zawierają słowo Unity we wszystkich projektach, których nazwa zaczyna się od słowa ProjectA:
$projectNamePattern=  "ProjectA*"
$projectsPattern = @($projectNamePattern)
$projectsPattern  | Get-Reference | ? {$_.RefPath -ne $null} | ?{ $_.RefPath.Contains("Unity") } 



czwartek, 14 listopada 2013

Doc Sprint

Jak myślisz, kiedy należy pisać dokumentację do projektu?


Wiesz jak to jest, zespół nie ma czasu na pisanie dokumentacji do istniejącego projektu, bo PM zawsze mówi, że brakuje jakieś bardzo ważnej funkcjonalności bez której to projekt nie ma sensu i dopiero jak skończy się tą jedną funkcjonalności to będzie czas na zrobienie dokumentacji. Ale później będzie błąd krytyczny lub inna bardzo ważna i potrzebna funkcjonalność i nadal nie będzie czasu na pisanie dokumentacji.

W metodyce scrum, sprint (przebieg) to przedział czasowy dostarczający klientowi działający produkt. Ale co z dokumentacją? Kiedy należy pisać dokumentację? Na końcu bieżącego sprintu czy na początku następnego sprintu? Czy lepiej jest na bieżąco pisać, czy jednak zmiany są zbyt często i lepiej jest pisać dokumentacje co kilka sprintów. Może jednak należy wyznaczyć osobny sprint do pisania dokumentacji?

Nie znam odpowiedzi na te pytania, ale podoba mi się koncepcja krótkiego sprintu poświęconego tylko na dokumentacje. Ten sprint nazywa się Doc Sprint. Doc Sprint może trwać od 2 do 3 dni. Jeżeli ten sprint potrzebowałby trwać dłużej niż 3 dni to oznacza, że dokumentacja jest źle zarządzana, za bardzo było się skoncentrowany na samej funkcjonalności projektu, albo mamy problem ze zbieraniem danych potrzebnych do dokumentacji (problem z knowledge sharingiem).

Wiem, że sprint bardziej jest dla klienta produktu niż dla zespołu, gdyż zasadą jest, że zmiany wprowadzone w sprincie musza być namacalne dla użytkowników (musza mieć nową widoczną funkcjonalność). Ale w trakcie sprintu można wprowadzić wewnętrzny, mniejszy sprint poświęcony tylko dokumentacji.

wtorek, 12 listopada 2013

Dzwonienie do klientów banków

Jak wiadomo (albo nie) socjotechniki są bardzo ważne i nie doceniane. Natrafiłem na filmik przedstawiający jak pewien holender wyciągnął ponad 100 tyś euro dzwoniąc do klientów banków i prosząc od nich ich dane.

Automatyczna aktualizacja SVN

Chcę Ci przedstawić jeden z najbardziej używanych przeze mnie skryptów ( przynajmniej 2 razy w ciągu dnia m.in jak zaczynam prace przy komputerze ) - skrypcik do aktualizacji kodu źródłowego w SVN napisany w PowerShell. Skorzystamy z programu TortoiseProc, który może wykonać polecenia z linii komend:
function Invoke-Svn{
Param(
     
     [string] $command = "properties",
     [string] $path = ((get-location).Path)

)
$svnPath = "C:\Program Files\TortoiseSVN\bin\"
$appSvn = $svnPath+ "svn.exe" 
& $appSvn info


$appTortoise = $svnPath + "TortoiseProc.exe"
$commandLine = '/command:' + $command + ' /notempfile /path:"' + $path + '"'
& $appTortoise $commandLine

}
Aby wykonać aktualizację kodu źródłowego to wystarczy wywołać komendę update:
function Update-Svn{
Param(
     [string] $path = ((get-location).Path)
     )
     
Invoke-Svn -path $path -command "update"
}
Więcej komend można znaleźć na stronie automatyzacji TortoiseSVN. Możemy sprawdzić rejestr svn za pomocą komendy log:
function Log-Svn{
Param(
     [string] $path = ((get-location).Path)
     )
     
Invoke-Svn -path $path -command "log"
}
Aby zaktualizować kilka projektów wystarczy wykonać:
Write-Host "Updating"
Update-Svn -path C:\Code\Path1\Project1
Update-Svn -path C:\Code\Path2\Project2
Update-Svn -path C:\Code\Path3\Project3
Update-Svn -path C:\Code\Path4\Project4
Wywołanie takiego skryptu za pomocą pliku bat jest podane poniżej:
powershell -File "C:\Scripts\Invoke-Svn.ps1"



niedziela, 10 listopada 2013

Zabawa z blockly

Pamiętasz może Logo Komeniusza - prosty graficzny język programowania powstały na Massachusetts Institute of Technology w USA w latach '70, aby wprowadzić dzieci w świat programowania :) Super idea i gdyby nie ten język, wiele dzisiejszych programistów (w tym ja - tak uczyłem się go w gimnazjum i jestem z tego dumny ) nie robiło by togo, co teraz robi.
Znalazłem projekt blockly, który bardzo przypomina programowanie żółwia.

Oczywiście można zagrać w układnie algorytmu do rozwiązywania labiryntów.



Oprócz tego blockly można programować za pomocą schematów blokowych:





Istnieją też projekty, w których blockly jest wykorzystywany do bardziej trudniejszych problemów.


Myślę, że za kilka (kilkanaście) lat każda osoba będzie w stanie zaprogramować swojego 'robota' za pomocą schematu blokowego.

sobota, 9 listopada 2013

Nie trzymaj sie zasad w programowaniu

Wcześniej pisałem o Testivusie. Jednym ze wskazówek wielkiego mistrza był to, że test uświęca środki. Opowieść Testvusa na ten temat była o młodym programiście i dwóch mistrzach programowania, którzy mieli inny punkt widzenia.




Uczeń zapytał dwóch mistrzów programowania
"Nie mogę przetestować ten kod bez mockowania i naruszenia hermetyzacji. Co powinienem zrobić?"


Jeden mistrz programowania odpowiedział
"Mockowanie jest złe i nie powinieneś naruszać encapsulacji.
Zrefaktoryzuj kod, abyś mógł przetestować go właściwie"


Inny mistrz programowania odpowiedział:
"Mockowanie jest dobre, a testowanie jest ważniejsze niż hermetyzacja."


Uczeń zastanawiając się, poszedł na piwo.
W miejscowej tawernie ujrzał wielkiego mistrza programowania, który pił piwo i jadł skrzydełka z kurczaka.
"Wielki mistrzu"- powiedział uczeń - "Myślałem, że nie pijesz. Czy nie jesteś przypadkiem wegetarianinem?"


Wielki mistrz uśmiechną się i odpowiedział:
"Czasami pragnienie najlepiej ugasić piwem, a głód przez skrzydełka kurczaka"


Uczeń już nie był zdezorientowany


Po tym tekście uświadomiłem sobie, że nie jest ważne czy implementacja rozwiązania problemu spełnia pewne standardy czy zasady programowania, ale ważne jest czy rozwiązuje problem i czy da się ten problem rozwiązać w zgodzie i porozumieniu z innymi użytkownikami.

piątek, 8 listopada 2013

Róźne zwracane wartości w Moq

Moq jest fantastyczne, bardzo przejrzyste, proste w nauczeniu się oraz popularne narzędzie do mockowania obiektów. Przedstawiam Ci prosty extension metod dla Moq. Metoda dzięki której mock zwraca rożne wartości z podanej listy za każdym wywołaniem.
Będziemy mockować interfejs:
    public interface INumerable
    {
        int GetNumber { get; }
    }
Standardowo, aby uzyskać różne wartości przy wywołaniu należało wykonać coś takiego:
var listOfReturnedelements = new List<int> {1, 4, 1, 8};
int number = 0;
var numMock= new Mock<INumerable>();

numMock
   .Setup(x=>x.GetNumber)
   .Returns(() => listOfReturnedelements[number])
    .Callback(() => number++)
;
Musimy zdefiniować dodatkową zmienną określająca index listy oraz wywołać metodę Callback, aby zwiększać ten index.
Za każdym razem jak wywołamy num.GetNumber() to dostajemy inną wartość tablicy. Poniżej przykład użycia:
var num = numMock.Object;
var list = new List<int>();
for (int i = 0; i < 4; i++)
{
    list.Add(num.GetNumber);                
}

Zamiast tego to możesz skorzystać z metody:
public static void ReturnsInOrder<T, TResult>(this ISetup<T, TResult> setup, IEnumerable<TResult> results)
    where T : class
{
            setup.Returns(new Queue<TResult>(results).Dequeue);
}
A kod ustawiający obiekt mockujący wygląda następująco:
numMock
.Setup(x=>x.GetNumber)
.ReturnsInOrder(listOfReturnedelements)

Troszkę krótkie wywołanie, ale bardzo mi się podoba użycie kolejki w rozwiązaniu.

poniedziałek, 4 listopada 2013

Droga Testivusa

Już pisałem o Mistrzu Testivusie i jego pokryciu kodu w testach. Bardzo mi się spodobały opowieści o testowaniu w stylu starożytnych chin. Wiele innych wskazówek można pobrać z tego dokumentu. Postanowiłem przetłumaczyć podstawowe wskazówki.




Jeżeli piszesz kod, to pisz testy
Myśl o teście i kodzie jako całość
Najlepszy czas do testowania to wtedy, gdy kod jest świeży
Test jest ważniejszy niż jednostka
Tylko głupcy nie stosują narzędzi
Dobry test nie przechodzi
Test nie marnieją
Czasami test uswięca środki
Nieperfekcyjny test dzisiaj jest lepszy niz perfekcyjny test któregoś dnia
Brzydki test jest lepszy niż brak testu


niedziela, 3 listopada 2013

Testowanie prywatnych metod

Ok, ważna zasada testowania prywatnych metod - prywatne metody nie testuje się!!!. Traktuje się je jako szczegóły implementacji, które nie muszą być przedstawiane, a testy tych metod są poprzez metody publiczne. Jednak, jeżeli chcielibyśmy przetestować te "szczegóły implementacji" to moglibyśmy ukrytą implementację wyekstraktować do oddzielnej klasy. Inne możliwości przedstawiam Ci w tym wpisie.


Przypuśćmy, że mamy klasę, która w logice zwraca GUID typu string:
public class GuidReturner
{
        public string GetGuidString(int arg)
        {
            var cmplexStr = GetGuid(arg);
            return cmplexStr == Guid.Empty ? "" : cmplexStr.ToString();
        }
 
        private Guid GetGuid(int arg)
        {
            return arg == 0 ? GetEmptyGuid() : Guid.NewGuid();
        }
 
        private static Guid GetEmptyGuid()
        {
            return Guid.Empty;
        }
}
 
W tej klasie mamy prywatną metodę GetGuid(int) : Guid. Aby ją przetestować to możemy zmienić prawa dostępu na internal. Wtedy wystarczy zastosować InternalsVisibleToAttribute i wpisać do AssemblyInfo.cs
[assembly: InternalsVisibleTo("YourLib.Tests")]
Minusem takiego rozwiązania jest modyfikacja zależności między projektem produkcyjnym, a projektem testującym, przymus pamiętania o zmianie stringa przy zmianie nazwy assembly oraz dodawanie dodatkowych InternalsVisibleTo dla każdego projektu testowego.

Inną możliwością testowania prywatnej metody jest zmiana prawa dostępu na protected i dziedziczenia po tej klasie. Dla naszego przykładu mamy:
public class GuidReturner
{
        protected Guid GetGuid(int arg)
        {
            return arg == 0 ? GetEmptyGuid() : Guid.NewGuid();
        }
}
A w projekcie w którym mamy testy tworzymy klasę testowalną:
public class TestableGuidReturner : GuidReturner
{
        public Guid TestGetGuid(int arg)
        {
            return base.GetGuid(arg);
        }
}
Minusem takiego rozwiązania jest tworzenie dodatkowej klasy testowalnej. Ma to też swoje plusy, gdzie możemy dodać dodatkową implementację, logowanie lub walidację dla testów.

Jeżeli nie podobają Ci się sposoby zmiany praw dostępu to można użyć mechanizmów refleksji. Do tego użyjemy obiektu dynamic.
public class NonPublicInvoker<T> : DynamicObject
{
        private readonly T _closeType;
        
        public NonPublicInvoker(T closeType )
        {
            _closeType = closeType;
        }
 
        public override bool TryInvokeMember(InvokeMemberBinder binder, 
                            object[] args, out object result)
        {
            result = _closeType.GetType()
                     .InvokeMember(binder.Name, 
                      BindingFlags.Instance 
                        | BindingFlags.NonPublic 
                        | BindingFlags.InvokeMethod
                     ,null, _closeType, args);
            return true;
        }
}
Test może wyglądać następująco:
[Test]
public void Dynamic_It_should_return_empty_guid_for_0()
{
      dynamic sbc = new NonPublicInvoker<GuidReturner>(new GuidReturner());
      Assert.AreEqual(sbc.GetGuid(0), Guid.Empty);
}
Do tego stwórzmy dynamika dla metod statycznych:
public class StaticNonPublicInvoker<T> : DynamicObject
{
        public override bool TryInvokeMember(InvokeMemberBinder binder, 
                             object[] args, out object result)
        {
            result = typeof(T)
                          .InvokeMember(binder.Name, 
                           BindingFlags.NonPublic 
                              | BindingFlags.Static 
                              | BindingFlags.InvokeMethod
                          ,null, null, args);
            return true;
        }
}
[Test]
public void Dynamic_It_should_return_empty_guid_for_static_private_method()
{
            dynamic sbc = new StaticNonPublicInvoker<GuidReturner>();
            Assert.AreEqual(sbc.GetEmptyGuid(), Guid.Empty);
}

W poniższym filmiki jest przedstawiony podobny sposób testowania prywatnych metod za pomocą obiektu PrivateObject



sobota, 2 listopada 2013

Magia Derrena Browna

Bardzo lubię sztuczki Derrena Browna. Najlepsze jest to, że jak oglądasz sztuczki po raz n-ty okazuje się, że wszystko jest jasne i proste i zastanawiasz się jak mogłeś tego wcześniej nie zauważyć... .

Polecam poniższy jego pokaz:



piątek, 1 listopada 2013

BDD Test Template

Wcześniej pisałem już o Korniszoku i BDD, ale teraz chciałbym przedstawić Ci template z jakim u mnie w pracy się pracuje. Template ten jest w modzie BDD. Testuje jedną klasę przy jednym zachowaniu. Jeżeli mielibyśmy n ważnych zachowań to potrzebujemy stworzyć n takich klas testowych.
Template wygląda następująco:
   [Category("BDD_MySuperClass")]
    public class when_we_run_special_methods : InstanceSpecification<MySuperClass>
    {
        protected override MySuperClass Create_subject_under_test()
        {
            return new MySuperClass();
        }

        [Test]
        public void It_should_do_special_stuff()
        {
            
        }
    } 


Cała magia jest w dwóch klasach abstrakcyjnych:
    public abstract class Specification
    {
        [SetUp]
        public virtual void BaseSetUp()
        {}

        [TearDown]
        public virtual void BaseTearDown()
        {}

        [DebuggerStepThrough]
        protected virtual void Establish_context()
        {}

        [DebuggerStepThrough]
        protected virtual void Because()
        {}

        [DebuggerStepThrough]
        protected virtual void Dispose_context()
        {}

        [DebuggerStepThrough]
        protected virtual void Initialize_subject_under_test()
        {}
    }

I druga generyczna klasa:
    public abstract class InstanceSpecification<TSubjectUnderTest> : Specification
    {
        protected TSubjectUnderTest SubjectUnderTest { get; private set; }

        protected abstract TSubjectUnderTest Create_subject_under_test();

        public override void BaseSetUp()
        {
            Establish_context();
            Initialize_subject_under_test();
            Because();
        }

        public override  void BaseTearDown()
        {
            Dispose_context();
        }

        protected override  void Initialize_subject_under_test()
        {
            SubjectUnderTest = Create_subject_under_test();
        }
    }


'Super' przykład dla StringBuilder jest przedstawiony poniżej:
[Category("BDD_StringBuilder")]
[TestFixture]
public class when_we_insert_char_into_empty_stringBuilder : InstanceSpecification<StringBuilder>
{
    public string SubjectUnderTestString { get { return SubjectUnderTest.ToString(); } }

    protected override StringBuilder Create_subject_under_test()
    {
        return new StringBuilder();
    }

    protected override void Because()
    {
        SubjectUnderTest.Insert(0, "first word");
    }

    [Test]
    public void It_should_start_with_word_first()
    {
        Assert.IsTrue(SubjectUnderTestString.StartsWith("first"));
    }

    [Test]
    public void It_should_not_be_empty_string_because_words_ware_added()
    {
        Assert.NotNull(SubjectUnderTest);
        Assert.IsTrue(SubjectUnderTestString.Any()); //any char
    }
} 


SubjectUnderTestString to pole pomocnicze. Przykład przedstawia wzorzec AAA (Arrange-Act-Assert). Metoda Create_subject_under_test() przygotowuje obiekt, metoda Because() wykonuje pewne zachowania na tym obiekcie i mamy dwie metody testowe.

Więcej szczegółów o testowaniu w stylu BDD możesz zaleź na elegantcode.com. Możesz pobrać code snippet z MyCodeSnippets.