Category Archives: Visual Studio

Visual Studio: Productivity Power Tools 2013

Dzisiaj chciałbym przedstawić dodatek do VS, a mianowicie Productivity Power Tools 2013. Kilka usprawnień jest naprawdę ciekawych i dlatego zachęcam do zainstalowania dodatku.

Moim najbardziej ulubionym rozszerzeniem jest sposób przedstawiania błędów w Solution Explorer. Bez dodatku, standardowo błędy związane z kompilacja są pokazywane w oknie Error List:

image

Productivity Tools pokazuje te same informacje również w Solution Explorer:

image

Każdy plik, projekt czy solucja, gdy zawiera jakieś błędy jest podkreślona na czerwono. Podobnie sprawa wygląda z “warnings”. Po najechaniu kursora dostaniemy dodatkowe informacje:

image

Inna drobna zmiana, ale bardzo przydatna to stworzenie skrótu Ctrl+Click, który umożliwia podgląd metody (Peek):

image

O wiele łatwiejsze jest naciśnięcie Ctrl + klik myszką niż korzystanie ze skrótów klawiaturowych czy też ręczne otwieranie menu podręcznego. Warto zwrócić uwagę, że Ctrl+Click jest używany przez Resharper jako “Go To Definition”. Jeśli mamy zainstalowany Resharper oraz chcemy uniknąć konfliktu, wtedy przechodzimy do Tools->Options i wyłączamy Ctrl+Click:

image

Inna nowość to Structure Visualizer:

image

Po lewej stronie możemy zauważyć, pionowe linie określające poziom zagnieżdżania kodu.

Najeżdżając na jedną z linii, dostaniemy informacje o strukturze kodu w formie tooltip’a, wyświetlanego u góry:

image

Wprowadzono również usprawnienie w formie Peek Help. Naciskając Alt+F1 na danej funkcji, wyświetlona zostanie dokumentacja MSDN:

image

Jest to dużo wygodniejsze niż otwieranie dokumentacji w osobnym oknie przeglądarki.

Kolejny drobiazg, to Power Commands, opcja dostępna z menu kontekstowego Solution Explorer:

image

“Remove And Sort Usings” jest prawdopodobnie najbardziej przydatną opcją.

Kolejna zmiana to maksymalizacja okna. Klikając dwukrotnie na jakimkolwiek okienku, automatycznie zostanie one zmaksymalizowane, co nie było możliwe bez Power Tools. Przydatne szczególnie dla okienek Debug, Watch itp. Czasami mamy sporo zmiennych tam i chcemy tylko na chwilę zobaczyć szczegóły. Za pomocą dwukrotnego kliknięcia, możemy zrobić to bardzo szybko.

Okienko Quick Launch może posłużyć teraz do wykonywania różnych zadań. W celu wyświetlenia wszystkich możliwych zadań do wykonania, wystarczy wpisać “@tasks e”:

image

Na przykład, w celu włączenia numeracji linii wystarczyć uruchomić komendę LineNumOn.

W Menu File, mamy teraz do dyspozycji “Recently Closed Documents”, które oczywiście zawiera ostatnio zamknięte dokumenty:

image

Usprawnienia również wprowadzono w wyświetlaniu i koloryzacji tabów:

image

Kolor tabu, zależy od lokalizacji pliku.

Visual Studio 2015–Diagnostic Tools

Nie dawno pojawił się VS 2015 CTP5 -  zachęcam do ściągnięcia.

VS 2015 posiada wiele nowych narzędzi pozwalających na diagnostykę i profilowanie aplikacji. Chcę poświęcić na to kilka wpisów, zaczynając dzisiaj od Diagnostic Tools.

Oczywiście wszystkie te udogodnienia były wcześniej dostępne w formie osobnych narzędzi typu Memory czy CPU profiler. Trend jednak jest taki, że Visual Studio z każdym wydaniem zawiera więcej narzędzi wbudowanych, które kiedyś były osiągalne wyłącznie różne profilery czy Resharper.  Wciąż oczywiście jest długa droga przed VS (zwłaszcza w przypadku Resharper), ale pomysł wbudowanych profilerów bardzo podoba mi się.

Okno diagnostic tools otwiera się automatycznie po rozpoczęciu debuggowania. Jest zatem uzupełnieniem klasycznych narzędzi diagnostycznych takich jak Output czy lista ze zmiennymi (Watch, Locals itp.).  Diagnostic Tools składa się z 3 elementów: Debugger Events, zużycie pamięci oraz zużycie procesora (CPU). Po uruchomieniu, okno prezentuje się następująco:

image

Pierwsza sekcja, Debugger events pokazuje zdarzenia takie jak wywołanie Debugger.Break czy odpalanie danego breakpoint’a. Innymi słowy, mamy tam informacje, co spowodowało zatrzymanie pracy i uruchomienie trybu debuggowania. Przyjrzyjmy się oknu Debugger w momencie, gdy zostanie wyrzucony wyjątek:

image

Całą historie zdarzeń mamy na wykresie, a mianowicie:

image

Screen został zrobiony na podstawie następującego kodu:

image

 

Czerwony romb oznacza wyjątek. Fioletowy pasek reprezentuje przerwanie wykonania aplikacji z powodu wyjątku, czerwony pasek oznacza odpalenie breakpoint’u, z kolei niebieski wywołanie Debugger.Break().

Jak widzimy wielkość pasku zależy od czasu. Pierwszy breakpoint został wykonany prawie natychmiast od czasu uruchomienia aplikacji, dlatego jest bardzo mały. Następnie są uruchamiane co 2 sekundy stąd są nieco większe. Najeżdżając kursorem na pasek, dostaniemy szczegóły:

image

Wykres jest interaktywny. Klikając na zdarzeniu w oknie debugger dostaniemy informacje o nim. Na przykład, klikając na niebieskim pasku, zobaczymy w oknie debugger następujące informacje:

image

Można również zaznaczyć kilka zdarzeń jednocześnie, zaznaczając kursorem przedział czasu, który nas interesuje:

image

image

Kolejne okno pozwoli nam monitorować całkowite zużycie pamięci w aplikacji. Bardzo wygodne, bo debuggując na bieżąco widzimy, czy coś złego nie dzieje się w aplikacji. Załóżmy, że mamy następujący kod:

int a = 0; Thread.Sleep(2000); int[] data = new int[100000000]; Thread.Sleep(2000); data = null; GC.Collect(); GC.WaitForFullGCComplete(); Thread.Sleep(2000); data = new int[10000000]; Thread.Sleep(2000); Debugger.Break();

Na początku deklarujemy jedną zmienną, potem dużą tablice, następnie ją zwalniamy i alokujemy nieco mniejszą. Na wykresie zobaczymy zatem następujące dane:

image

 

Pierwsze 2 sekundy to deklaracja tylko jednej zmiennej, następnie tworzymy dużą tablicę zatem zużycie pamięci rośnie. Kolejna linia kodu usuwa tablicę z pamięci, stąd następne dwie sekundy to znów niskie zużycie pamięci. Ostatni fragment to alokacja małej tablicy i widzimy wyraźnie, że zużycie trochę wzrosło. Oczywiście jest to całkowite zużycie pamięci, a nie tylko zmiennych, które sami tworzymy.

Ostatnie okno, które chciałem pokazać to po prostu zużycie CPU:

image

Wykres, jak każdy poprzedni jest interaktywny i najeżdżając kursorem dostaniemy dodatkowe informacje.

Jak wspomniałem, nie są to żadne nowości, ale ze względu na to, że są zintegrowane z VS i tak łatwo dostępne, debugging aplikacji pozwala na bieżąco monitorować, co dzieje się z kodem.

Visual Studio 2015 – debugowanie wyrażeń Lambda

Kolejne, drobne ulepszenie w VS 2015, to możliwość debugowania wyrażeń lambda.  Załóżmy, że mamy następujący kod:

int[] numbers = new int[] { 3, 5, 1, 10, 15, 2, 5, 10 }; var results = numbers.Where(x => x % 2 == 0);

Jeśli odpalimy powyższy kod w VS 2013 (lub w starszej wersji) niemożliwe byłoby debugowanie lambda. W okienku watch następująca wiadomość zostałby wyświetlona:

image

Analogicznie sprawa wygląda z Immediate Window:

image

Z kolei VS 2015, bez problemu ujrzyjmy końcowy wynik:

image

image

Bardzo drobna zmiana, ale wiele razy ta niedogodność utrudniała debugowanie. Mam na myśli sytuacje, kiedy warunek jest bardziej skomplikowany niż proste modulo 2. Wciąż są pewnie ograniczenia m.in. brak Mark Object ID czy debugowanie wywołań do kodu natywnego.

Visual Studio 2015: Analiza jakości kodu

VS 2015 usprawnił, moim zdaniem znaczącą sposób analizy kodu. Sam sposób podpowiedzi również został poprawiony i coraz bliżej im do Resharper, a w pewnych sprawach, jak zobaczymy, daje potencjalnie większe możliwości.

Zacznijmy od przykładu pokazanego w prezentacji VS 2015. Załóżmy, że chcemy zaimplementować interfejs ISerializable:

class Sample :ISerializable
{
}

Jeśli tylko namespace zawierający ISerializable nie został dołączony, naciskamy ctrl+. i wyświetli się następujące menu:

image

Nowością jest tutaj podgląd tego, co zostanie zmienione (w tym przypadku dołączenie przestrzeni za pomocą using).

Kolejnym etapem będzie implementacja metod wymaganych przez interfejs. Podgląd będzie wtedy wyglądać następująco:

image

W tej chwili mamy następujący kod:

class Sample : ISerializable
{
   public void GetObjectData(SerializationInfo info, StreamingContext context)
   {
       throw new NotImplementedException();
   }
}

Wspomniałem na początku o nowym mechanizmie analizy kodu. W VS 2015 będzie to rozszerzalne i każdy taki analizator można zainstalować w formie pakietu NuGet. Daje to ogromne możliwości. Zainstalujmy więc jeden z nich:

image

Zaglądając do References->Analysers zobaczymy listę reguł:

image

Wracając do naszego przykładu. Po instalacji powyższego pakietu, zostaniemy poinformowani, że powinniśmy dodać atrybut [Serializable] do stworzonej klasy.

image

Widzimy również numer reguły (CA2237).

Dostępny jest już teraz pakiet reguł dla aplikacji Azure. Opisywany mechanizm, potencjalnie może przydać się do pisania reguł, specyficznych dla danego projektu.

Z drugiej strony, długa jeszcze droga zanim zastąpi to nDepend i Resharper, ale moim zdaniem idzie to w tym kierunku…

Visual Studio 2015: Smart Unit Tests

Jeśli jeszcze nie ściągneliście VS 2015, to zapraszam:

http://www.visualstudio.com/en-us/downloads/visual-studio-2015-downloads-vs.aspx

Smart Unit Tests to sposób automatycznego wygenerowania scenariuszy dla testów jednostkowych. Wiele o tym już na blogu jak i w artykułach na MSDN pisałem, a konkretnie o Pex & moles. Chciałbym dzisiaj jednak zaprezentować wbudowaną funkcję w VS 2015. Nie wymaga ona instalacji zewnętrznych narzędzi.

Na początku od razu poważna wada.. Póki co, wspierany jest wyłącznie MSTest. Mam nadzieję, że zmieni się to w oficjalnym wydaniu bo inaczej większość osób nie będzie mogła z tego korzystać.

Załóżmy, że zaimplementowaliśmy już jakąś metodę i chcemy wygenerować test:

public double AnyMethod(int a,int b,double c,string text)
{
  if (a == 3)
      return 1;
  else if (b == a && a == 1)
      return 2;
  else if (b == (int)c && a == 6 && text == "Hello")
      return 3;
  else if (Int32.Parse(text) == a)
      return 4;
  else if (c == a + b)
      return 5;

  return 6;
}

Smart Unit tests postara się wygenerować tak dane wejściowe, aby każda gałąź została pokryta. Zaznaczamy zatem metodę i z menu kontekstowego wybieramy Smart Unit Test:

image

Zostanie wyświetlone następujące okno ze scenariuszami:

image

Widzimy po kolumnie result, że wszystkie scenariusze zostały wygenerowane – wartości od 1 do 6 (wszystkie możliwe ścieżki).

Ponadto, część scenariuszy generuje wyjątki. Wiąże się to z tym, że nie można przekazać tekstu do parametru text i potem wywoływać Int32.Parse. Klikając na każdym z nich, po prawej stronie wyświetli się wygenerowany test:

image

Możemy dowolny scenariusz zapisać do pliku np.:

public partial class SampleTest
{
   [TestMethod]
   [PexGeneratedBy(typeof(SampleTest))]
   public void AnyMethod898()
   {
       double d;
       Sample s0 = new Sample();
       d = this.AnyMethod(s0, 6, 0, 0, "Hello");
       Assert.AreEqual<double>(3, d);
       Assert.IsNotNull((object)s0);
   }
}

Oczywiście możemy wszystkie pozycje zaznaczyć i zapisać.

Powyższe okno również zawiera uwagi i wskazówki:

image

Warto zawsze przejrzeć listę bo czasami możemy dowiedzieć się czegoś, czego nie spodziewaliśmy się po naszym kodzie.

Zmodyfikujmy teraz trochę AnyMethod, aby sprawdzał, że text jest zawsze liczbą:

public double AnyMethod(int a, int b, double c, string text)
{
  if (text == null || !text.All(x => char.IsDigit(x)))
      throw new ArgumentException();

  if (a == 3)
      return 1;
  else if (b == a && a == 1)
      return 2;
  else if (b == (int)c && a == 6 && text == "Hello")
      return 3;
  else if (Int32.Parse(text) == a)
      return 4;
  else if (c == a + b)
      return 5;

  return 6;
}

Po ponownym wygenerowaniu scenariuszy zobaczymy:

image

Smart Unti Test rozpoznał sam, że wyrzucenie ArgumentException jest spodziewanym wynikiem, gdy przekazujemy tekst zamiast liczby. Niestety nie uwzględniliśmy przypadku, gdy Text jest pusty, stąd 4 testy zakończone błędem. Zmodyfikujmy kod i odpalmy analizę ponownie:

public double AnyMethod(int a, int b, double c, string text)
{
  if (string.IsNullOrEmpty(text) || !text.All(x => char.IsDigit(x)))
      throw new ArgumentException();

  if (a == 3)
      return 1;
  else if (b == a && a == 1)
      return 2;
  else if (b == (int)c && a == 6 && text == "Hello")
      return 3;
  else if (Int32.Parse(text) == a)
      return 4;
  else if (c == a + b)
      return 5;

  return 6;
}

image

Jak widzimy, wszystkie gałęzie zostały pokryte, włączając te, które wyrzucają ArgumentException.

Podsumowując… Przede wszystkim, należy pamiętać, że pokrycie kodu 100% nie znaczy, że wszystko zostało przetestowane. Sprawdza to, czy każda gałąź zostanie wywołania, a nie czy każda kombinacja gałęzi jest pokryta.

Kolejna wątpliwość to bardzo popularne dzisiaj TDD. Tak naprawdę, kod powinien być pokryty już testami, jak kończymy implementację czegoś.

Dla mnie największą jednak wadą jest brak wsparcia dla nUnit. Gdybym mógł z niego korzystać, to nawet w połączeniu z TDD może to się przydać. Zawsze mamy legacy kod i możemy popełnić jakiś błąd więc Smart Unit Test byłoby fajnym sposobem na weryfikację naszego kodu. Innymi słowy, nie korzystałbym z tego jako coś bazowego dla moich testów, ale wyłącznie uzupełniającego.

Co prawda, nie testowałem tego jeszcze, ale podobno Microsoft Pex oraz Code Contracts współpracują ze sobą, co oznacza, że moglibyśmy generować bardziej trafne scenariusze użycia za pomocą Smart Unit Test.

Visual Studio–kilka skrótów ułatwiających pracę

1. Załóżmy, że mamy klasę z wieloma polami, np.:

public struct Employee
{
   internal string Field1;
   internal string Field2;
   internal string Field3;
   internal string Field4;
   internal string Field5;
   internal string Field6;
}

Następnie chcemy zmienić modyfikator internal na public. Naturalne podejście byłoby zmodyfikowanie wszystkich wywołań jeden po drugim. Inne rozwiązanie to CTRL+H i automatycznie zastąpienie. W zależności od konkretnej klasy, może być to wygodne lub nie. Trzeba być uważnym jednak, aby wszystkich wystąpień w pliku nie zamienić(jeśli np. chcemy tylko kilka z nich zaktualizować).

W VS możemy nacisnąć klawisz ALT, który umożliwi nam pionowe zaznaczenie tzn.:

image

Następnie możemy zmodyfikować tekst do public i zostanie to odzwierciedlone we wszystkich zaznaczonych liniach tzn.:

image

To jest dość stara funkcja Visual Studio, ale niezbyt popularna, a czasami potrafi ułatwić życie.

2. Jeśli użyjemy CTRL, a potem – (myślnik), to kursor zostanie przeniesiony do ostatnio używanej linii Przydatne, gdy skaczemy gdzieś dalej w pliku (aby zmodyfikować np. parametr wejściowy), a potem chcemy z powrotem powrócić bez używania myszki. Analogicznie możemy skorzystać z kombinacji CTRL, Shift, – aby przejść do przodu.

3. Generalnie zasada jest taka, że im mniej używamy myszki, tym szybciej jesteśmy w stanie pisać kod. Ciekawym skrótem jest CTRl+X, który usuwa całą linie. Jeśli zaznaczymy ręcznie linie, to usunięta zostanie wyłącznie sama linia, bez znaku nowej linii. Na przykład, jeśli mamy kursor na polu Field3 to naciśnięcie CTRL+X, spowoduje usunięcie go wraz ze znakiem nowej linii, co poskutkuje:

public struct Employee
{
   public string Field1;
   public string Field2;
   public string Field4;
   public string Field5;
}

Jeśli z kolei cała linia byłoby zaznaczona, to znak nowej linii zostanie pozostawiony bez zmian:

public struct Employee
{
   public string Field1;
   public string Field2;

   public string Field4;
   public string Field5;
}

4. Jeśli chcemy przejść do innego aktywnego pliku (bez używania myszki) możemy użyć kombinacji CTRL+Tab, co wyświetli następujące okno:

image

5. Shift+Alt+Enter – szybkie przełączanie się między FULLSCREEN a normalnym trybem. Przydatne, gdy mamy dużo kodu i nie chcemy szukać tej opcji w menu.

6. Jeśli chcemy przesunąć daną linię w dół albo w górę również nie musimy używać myszki. Wystarczy nacisnąć alt i strzałkę w górę (up) lub w dół (down).

7. Zoom chyba jest dość oczywisty w VS. Wystarczy nacisnąć ctrl i poruszać kółkiem myszki, a rozmiar czcionki automatycznie będzie modyfikowany (tak jak to w większości przeglądarkach internetowych).

image

image

8. Pamiętam, że kiedyś aby sformatować kod w VS, używałem zwykle zamknięcia klamr itp. Na szczęście istnieje skrót, który to zrobi dla nas w dowolnym momencie. Załóżmy, że mamy źle sformatowany kod tzn.:

image

W menu głównym możemy przejść do Edit->Advanced->Format Document:

image

Warto również zapamiętać pokazane skróty klawiszowe – klikanie po menu jest zdecydowanie zbyt powolne…

9. Przewijanie bez myszki i zachowanie pozycji kursora – CTRL + UP albo DOWN.

10. Bardzo dużo osób używa resharper’a. Często zamiast korzystać z Solution Explorer, klikamy CTRL+T, aby przejść do konkretnej klasy. Niestety, czasami mamy tak skomplikowaną strukturę folderów, że nie mamy pojęcia gdzie znaleziony plik przez Resharper znajduje się. A co jeśli chcemy przenieść dany plik do innego folderu? Musimy jakąś zlokalizować dany element w Solution Explorer. W VS możemy kliknąć na Sync With Active Document w SE:

image

Mam na myśli tą ikonę dwóch strzałek. Wtedy automatycznie zostaniemy przeniesieni do danego pliku w Solution Explorer:

image

11. Peek Definition. To nowa opcja w VS o której pisałem tutaj. Wspominam o niej ponownie, ponieważ moim zdaniem jest niedoceniana i przez to nie zawsze programiści wyrabiają sobie odpowiedni nawyk z nowym narzędziem.

Debugowanie obiektów bez jawnej referencji

W Visual Studio istnieje pewna opcja, która umożliwia sprawdzenie wartości obiektu, do którego nie ma się jawnej referencji. Spójrzmy na następujący przykład:

class Program
{
   static void Main(string[] args)
   {        
       DoSomething();
   }

   private static void DoSomething()
   {
       var person=new Person();
       person.FirstName = "Piotr";
       person.LastName = "Zielinski";
   }
}

Jeśli ustawimy breakpoint w metodzie DoSomething naturalnie zobaczymy wartość obiektu person w oknie watch:

image

Jeśli jednak przejdziemy dalej, do funkcji main wtedy zmienna person będzie poza scope i watch nic nie pokaże:

image

Jest to naturalne zachowanie, ponieważ nie ma jawnej referencji do Person. Czasami jednak, mimo wszystko chcemy śledzić taki obiekt. Oczywiście w celu wykrycia memory leak służą profilery, ale czasami dostęp do referencji może być utrudniony a mimo to, chcemy mieć łatwy do niego dostęp. Łatwo wyobrazić sobie przykład z jakimś własnym kontenerem, gdzie nie ma publicznego dostępu do instancji. W VS można z menu podręcznego wybrać “Make Object ID”. Należy zatem kliknąć na zmiennej Person w watch i wybrać wspomnianą opcję:

image

Koło zmiennej wtedy pojawi się identyfikator w formie 1# :

image

Jest to tzw. słaba referencja. Możemy mieć do niej dostęp nawet w funkcji Main:

image

Wystarczy wpisać ją, jak każdą inną zmienną w oknie watch. Oczywiście, gdy GC zwolni zasoby, wtedy pojawi się NULL.  Obiekt wciąż jest widziany jako nieosiągalny i nie ma to wpływu na działanie GC.

Wprowadzenie do SpecFlow, BDD

Dzisiaj oprogramowanie jest naprawdę skomplikowane. W większości przypadków, skomplikowanie oprogramowania nie polega na zaawansowanych algorytmach. W praktyce, algorytmy tworzone są przez wąską grupę specjalistów i potem są po prostu wykorzystywane w formie DLL przez innych użytkowników (programistów). Oczywiście każda aplikacja ma jakieś algorytmy, ale w aplikacjach biznesowych, zwykle większym problemem jest utrzymanie całości i możliwość szybkiego dostarczenia produktu. Słaby kod cechuje się długimi cyklami testowania regresyjnego. W dobrym projekcie, z dużą liczbą automatycznych testów, taki czas jest znaczenie skrócony.

Innym poważnym problemem jest zgromadzenie wymaganiach i ich poprawna interpretacja. Wiele projektów cierpi ze zbyt częstych zmian i niejasnych wymaganiach. Jest to jednak nieodłączna część towarzyszącą programistom i po prostu trzeba znaleźć sposób, aby minimalizować problemy komunikacyjne między programistami a np. ekspertami z danej dziedziny.

W dzisiejszym wpisie chciałbym przestawić framework SpecFlow, ułatwiający pisanie testów BDD – Behaviour Driven Development. Podejście bardzo popularne, w szczególności w aplikacjach biznesowych, gdzie testy są tak naprawdę opisane przez user story.

Nie będę opisywał tutaj BDD bo to temat prawdopodobnie na inny wpis. Skupie się głównie na samym framework’u i podam tylko kilka różnić między BDD a klasycznym testowaniem.

Przede wszystkim należy wiedzieć jak działa Test-Driven-Development. W skrócie jest to podejście, gdzie najpierw piszę się testy jednostkowe, a potem dopiero implementacje interfejsów. Takim sposobem, każdy nowy kod, powinien być już pokryty testami. Daje to taką przewagę, że programiści są zmuszeni do pisania elastycznego kodu  – inaczej ciężko będzie napisać jakiekolwiek testy jednostkowe. Kolejną zaletą jest większa świadomość możliwych parametrów wejściowych i nieoczekiwanych scenariuszy.

Niestety bardzo często tworzone testy są ciężkie w utrzymaniu. Wiele testów to kontenery na metody, które z nazwy nic nie mówią. Skupiają się one na testowaniu API, a nie realnych scenariuszy. Jeśli mamy w kodzie metody o nazwach Method1NullTest to niewiele one mówią użytkownikowom czy testerom.

W BDD skupiamy się zatem na scenariuszach użycia aplikacji. Dlatego stanowią one tak naprawdę cześć opisu każdej, dobrze zdefiniowanej user story. Nie zapominajmy, że testy jednostkowe to black-box testing – nie interesuje nas jak coś jest zaimplementowane. Skupiamy się na tym, jak użytkownik może korzystać z naszej aplikacji. Rozważamy workflow’y, a nie konkretne gałęzie IF’a – bo jak zostało wspomniane, w black-box testing nie analizujemy kodu. Oczywiście metryki pokrycia kodu wciąż są pożytecznie i należy z nich korzystać. Chodzi mi bardziej, że programista powinien myśleć w kategoriach scenariuszy użycia, a nie pokrycia każdej gałęzi IF, przekazując tym samym wszystkie możliwe parametry wejściowe. Naturalnie, że w pewnych sytuacjach (algorytmy) sprawa wygląda inaczej i tam faktycznie testowanie polega na po prostu definiowaniu wejścia i wyjścia. W praktyce jednak, często błędy występującą w UI i w sposobie nawigacji (workflows).

BDD czy TDD to moim zdaniem bardzo naturalne podejścia – po prostu najpierw myślimy jak kod ma działać, a potem go implementujemy. Jeśli mamy sprzeczne wymagania to dowiemy się o tym na etapie definiowania testów, a nie na koniec, gdy 90% kodu jest już zaimplementowane.

W SpecFlow, zachowania czyli scenariusze opisane są za pomocą trzech kroków Given, When, Then (GWT):

1. Given stanowią preconditions czyli warunki wstępne. Innymi słowy, wyjaśniają one, jaki stan mamy przed danym scenariuszem.

2. When – zdarzenie, które powoduje wywołanie danego zachowania. Może być to np. kliknięcie w link na stronie internetowej czy wywołanie jakieś metody WebAPI.

3. Then – etap, w którym mamy jakiś rezultat do testowania.

Podsumowując, mając warunki Given, w momencie zdarzenia When, otrzymujemy jakieś dane, które chcemy zweryfikować.

SpecFlow integruje się ze środowiskiem Visual Studio. Musimy najpierw zainstalować odpowiedni plugin. Klikamy więc Tools->Extensions and Updates:

image

Następnie tworzymy nowy projekt (Windows Library) i instalujemy pakiet z NuGet, który zintegruje SpecFlow z nUnit:

image

Czas zacząć napisać nasz pierwszy scenariusz. W tym celu, przechodzimy do Add New Item i wybieramy SpecFlow Feature File:

image

Domyślnie zostanie wygenerowany przykładowy scenariusz:

Feature: SpecFlowFeature1
    In order to avoid silly mistakes
    As a math idiot
    I want to be told the sum of two numbers

@mytag
Scenario: Add two numbers
    Given I have entered 50 into the calculator
    And I have entered 70 into the calculator
    When I press add
    Then the result should be 120 on the screen

Jak widać, jest on opisany naturalnym językiem. Oznacza to, że taki scenariusz jest zrozumiały nie tyko dla programistów czy testerów, ale również osoby nietechnicznej. W tym wpisie, wykorzystamy powyższy, automatycznie wygenerowany scenariusz. W kolejnym poście, zajmiemy się ASP.NET MVC i jakimś bardziej praktycznym przykładem.

SpecFlow, jak wspomniałem integruje się z Visual Studio. Dzięki temu możemy kliknąć w Generate Step Definitions w kontekstowym menu i zobaczymy następujące okno:

image

Po naciśnięciu Generate, zostanie wygenerowany nowy plik z testami:

[Binding]
public class SpecFlowFeature1Steps
{
   [Given(@"I have entered (.*) into the calculator")]
   public void GivenIHaveEnteredIntoTheCalculator(int p0)
   {
       ScenarioContext.Current.Pending();
   }

   [When(@"I press add")]
   public void WhenIPressAdd()
   {
       ScenarioContext.Current.Pending();
   }

   [Then(@"the result should be (.*) on the screen")]
   public void ThenTheResultShouldBeOnTheScreen(int p0)
   {
       ScenarioContext.Current.Pending();
   }
}

Na razie testy są oczywiście puste. Możemy przejść do pliku feature i wybrać z menu kontekstowego “Run SpecFlow Scenarios”:

Implementacja takiego testu mogłaby wyglądać tak:

[Given(@"I have entered (.*) into the calculator")]
public void GivenIHaveEnteredIntoTheCalculator(int p0)
{
  _calculator.EnterNumber(p0);
}

[When(@"I press add")]
public void WhenIPressAdd()
{
  _calculator.Add();
}

[Then(@"the result should be (.*) on the screen")]
public void ThenTheResultShouldBeOnTheScreen(int p0)
{
  Assert.AreEqual(p0, _calculator.Output);
}

Proszę zwrócić uwagę na (.*)-  to są parametry przekazane z pliku features tzn.:

Given I have entered 50 into the calculator
And I have entered 70 into the calculator
When I press add
Then the result should be 120 on the screen

W kolejnym wpisie dokładnie przyjrzyjmy się jak parametry są rozpoznawalne i przekazywane oraz jak wykonywany jest scenariusz. Na razie warto zapamiętać kroki GWT – Given, When, Then ponieważ są one najczęściej stosowane.

Moim zdaniem, najtrudniejsze w tym jest poprawne opisywanie testów. Tego oczywiście nie da się nauczyć z dokumentacji i po prostu trzeba praktykować. Złe testy to marnowanie czasu i zwiększanie kosztów na utrzymanie tego. Testy po prostu muszą redukować czas przeznaczany na manualne testowanie i naprawianie bug’ów. Jeśli poprawa nie jest zauważalna to oznacza, że coś źle robimy. W momencie zgłoszenia bug’a oprócz jego naprawienia powinno również się uzupełnić brakujący zestaw testów jednostkowych – jasne jest, że coś zostało w nich pominięte skoro błąd został zgłoszony.

Visual Studio 2013 – debugowanie asynchronicznego kodu

W VS 2013 usprawniono debugowanie kodu asynchronicznego. Wszyscy jesteśmy przyzwyczajeni już do async\await. Znacząco to ułatwia wykonywanie operacji asynchronicznych. Niestety, debugowanie w VS 2012 jest dość uciążliwe. Załóżmy, że mamy kod z wieloma metodami asynchronicznymi, które z kolei są pozagnieżdżane. W przypadku wyrzucenia wyjątku lub ustawienia breakpoint’a, call stack nie zawierał żadnych informacji. Przetestujmy opisany problem  na następującym kodzie:

public partial class MainWindow : Window
{
   public MainWindow()
   {
       InitializeComponent();
       DoSomething();
   }   
   private async void DoSomething()
   {
       await RunAsync();
   }

   private async Task RunAsync()
   {
       await Task.Delay(100);
       
       await DownloadNumberAsync();
   }
   private Task<int> DownloadNumberAsync()
   {
       return Task<int>.Factory.StartNew(DownloadNumber);
   }
   private int DownloadNumber()
   {   
       return 1;
   }
}

W Visual Studio 2012, gdy ustawimy breakpoint na linię await DownloadNumberAsync, call stack wyglądał następująco:

image

Z kolei w VS 2013 mamy pełny stos:

image

Dzięki ulepszeniom w VS 2013 i Windows 8.1, dużo łatwiej zrozumieć kod asynchroniczny. Wcześniej call stack pokazywał wyłącznie ostatnią metodę i nie wiadomo było, jak ona została wywołana. Na blogu kiedyś opisywałem internale async\await. Wiemy, że jest tam w rzeczywistości maszyna stanu, oparta na callbackach. Z tego względu, w poprzednich wersjach VS, nie wiadomo było jak metoda była wywołana. VS 2013 rozpoznaje konstrukcje async\await i można już je zaprezentować w sposób, który wynika z kodu c#, a nie implementacji w CLR.

SideWaffle–standardowe szablony dla Visual Studio

Zanim przejdziemy do tworzenia własnych szablonów dla ASP.NET MVC scaffolding, warto zainstalować SideWaffle. To nic innego jak zestaw szablonów i projektów. Link:
http://sidewaffle.com/

Pełną listę dostępnych szablonów można zobaczyć na stronie sidewaffle. Głównie są to szablony związane z web, szczególnie z AngularJS. Po zainstalowaniu zobaczymy nowe typy projektów jak i szablony dla nowych plików. Na przykład, w aplikacji Web mamy:

image

Wybierając szablon requirejs, zostanie wygenerowany szkielet modułu:

require(["helper/util"], function (util) {
    //This function is called when scripts/helper/util.js is loaded.
    //If util.js calls define(), then this function is not fired until
    //util's dependencies have loaded, and the util argument will hold
    //the module value for "helper/util".
});

Naprawdę przydatne, bo w końcu pisanie tego ręcznie dla każdego modułu jest monotonne. Ostatnio na blogu pisałem o knockout.js. Również i dla tej biblioteki istnieje szablon:

//Template taken from http://knockoutjs.com/documentation/custom-bindings.html
ko.bindingHandlers['binding1'] = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // This will be called when the binding is first applied to an element
        // Set up any initial state, event handlers, etc. here
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // This will be called once when the binding is first applied to an element,
        // and again whenever the associated observable changes value.
        // Update the DOM element based on the supplied values here.
    }
};

Najwięcej chyba szablonów jest dostępnych dla AngularJS. Przykład (AngularJS Module):

(function () {
    'use strict';

    var id = 'app1';

    // TODO: Inject modules as needed.
    var app1 = angular.module('app1', [
        // Angular modules 
        'ngAnimate',        // animations
        'ngRoute'           // routing

        // Custom modules 

        // 3rd Party Modules
        
    ]);

    // Execute bootstrapping code and any dependencies.
    // TODO: inject services as needed.
    app1.run(['$q', '$rootScope',
        function ($q, $rootScope) {

        }]);
})();

Oczywiście, mamy również szkielety dla kodu c#, a nie tylko JavaScript. Zobaczmy co wygeneruje nUnit Fixture:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;

namespace MvcApplication1
{
    [TestFixture]
    public class NUnitFixture1
    {
        [SetUp]
        public void Setup()
        {

        }

        [Test]
        public void Test()
        {
            throw new NotImplementedException();
        }
    }
}

Nawet dla SVG mamy małą pomoc w formie:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200">
  <rect width="300" height="120" y="20" fill="green"/>
  <rect width="80" height="150" x="20" y="30" fill="red"/>
  <rect width="140" height="80" x="50" y="50" fill="blue"/>
</svg>

Dla programistów Azure, mamy 3 szkielety klas m.in. ułatwiające prace z Azure Table oraz Blob:

image

Przykład (Azure Blob):

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;

namespace WebApplication4.Controllers
{
    public class BlobUploadHelper1
    {
        private string _connectionString;
        private CloudStorageAccount StorageAccount { get; set; }

        public BlobUploadHelper1()
            : this(ConfigurationManager
                .ConnectionStrings["BlobUploadHelper1.ConnectionString"]
                    .ConnectionString)
        {
        }

        public BlobUploadHelper1(string connectionString)
        {
            _connectionString = connectionString;
        }

        public CloudBlobClient GetBlobClient()
        {
            StorageAccount = CloudStorageAccount.Parse(_connectionString);
            return StorageAccount.CreateCloudBlobClient();
        }

        public CloudBlobContainer GetBlobContainer(string containerName,
            BlobContainerPublicAccessType accessType = BlobContainerPublicAccessType.Blob)
        {
            var container = GetBlobClient().GetContainerReference(containerName);
            container.CreateIfNotExists();
            container.SetPermissions(new BlobContainerPermissions
            {
                PublicAccess = accessType
            });
            return container;
        }

        public string UploadFileToBlob(HttpPostedFileBase postedFile, string containerName = "uploads")
        {
            var container = GetBlobContainer(containerName);
            var blob = container.GetBlockBlobReference(Path.GetFileName(postedFile.FileName));
            using (postedFile.InputStream)
            {
                blob.UploadFromStream(postedFile.InputStream);
            }
            return blob.Uri.AbsoluteUri;
        }

        public List<string> GetBlobList(string containerName)
        {
            var container = GetBlobContainer(containerName);
            var ret = container.ListBlobs().Select(x => x.Uri.AbsoluteUri).ToList();
            return ret;
        }
    }
}

Warto zaznaczyć, że SideWaffle to nie tylko nowe typy projektów oraz szablonów, ale również snippet’y. Do dyspozycji mamy m.in. Nancy, WCF Client, Dispose czy Angular. Dispose wygląda następująco:

#region IDisposable Members

/// <summary>
/// Internal variable which checks if Dispose has already been called
/// </summary>
private Boolean disposed;

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(Boolean disposing)
{
    if (disposed)
    {
        return;
    }

    if (disposing)
    {
        //TODO: Managed cleanup code here, while managed refs still valid
    }
    //TODO: Unmanaged cleanup code here

    disposed = true;
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
    // Call the private Dispose(bool) helper and indicate 
    // that we are explicitly disposing
    this.Dispose(true);

    // Tell the garbage collector that the object doesn't require any
    // cleanup when collected since Dispose was called explicitly.
    GC.SuppressFinalize(this);
}

/// <summary>
/// The destructor for the class.
/// </summary>
~ClassNamePlaceholder() 
{ 
    this.Dispose(false); 
}


#endregion

Z kolei WCF Client:

try
{
  //todo: make calls to the client - should be as small a set of work here as possible
  client.Close();
}
catch (System.ServiceModel.CommunicationException e)
{
  //todo: handle communication exceptions, for example cannot connect to service.
  client.Abort();
}
catch (TimeoutException e)
{
  //todo: handle time outs when connecting, calling and closing
  client.Abort();
}
catch (Exception e)
{
  //todo: handle any other client issue
  client.Abort();
  throw;
}

Co do snippetów… Istnieje jeszcze nawet szablon na własne snippety, bo w końcu każdy z nich zawiera pewne elementy wspólne:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>snippet1</Title>
      <Shortcut>snippet1</Shortcut>
      <Description>A description for snippet1</Description>
      <Author>Microsoft Corporation</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <ID>message</ID>
          <ToolTip>The message</ToolTip>
          <Default>Hi there!</Default>
        </Literal>
      </Declarations>
      <Code Language="CSharp"><![CDATA[
            Console.WriteLine("$message$");
        ]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

Przedstawione w tym poście szkielety to bardzo mała część z tych, które są dostępne w pakiecie. Oczywiście z większości prawdopodobnie nie będziemy korzystać w tym samym projekcie, ale warto je przejrzeć, aby następnym razem już nie marnować czasu na pisanie powtarzalnych części kodu.