piątek, 31 października 2008

PG.NET: Po XII spotkaniu

Wczoraj odbyło się dwunaste spotkanie "Poznańskiej Grupy .Net". Tematem spotkania było:
Composite Application Guidance for WPF: temat zaprezentował Szymon Kobalczyk. Drugi temat to: IronPython zaprezentowany przez Aleksandra Sumowskiego.
Refleksje. CAG dla WPF, wydał się dla mnie świetnym uzupełnieniem Inversion of Control omawianago dzień wcześniej podczas spotkania grupy .Net na Politechnice Poznańskiej. Było widać, że Szymon godzinami może rozmawiać o WPFie... i tak też się stało :-) Dwugodzinna prezentacja okazała się niezłą przeprawą szczególnie dla ludzi niesiedzących w core WPF-a, czyli np. mnie :-) Do następnego wykładu przetrwała garstka :-)
Inaczej poprowadził spotkanie Aleksander, który w dość prosty, a zarazem jak najmniej "sztywny" (nie to,żeby Szymon prowadził sztywno!) sposób omawiał potęgę IronPythona i CPythona przy okazji. Sporo się uśmiałem, ale prezentacja była bardzo, bardzo interesująca.
Oby tak dalej!!

czwartek, 30 października 2008

MS Groups: It's not a bug, it's feature?

Ostanio postanowiłem sie zalogować na MS Groups, aby dodać zaktualizowane informacje w moim profilu. Ok, hasło mi umknęło z pamięci więc skorzystałem z opcji "Przypomnij hasło", dostałem nowe na maila, loguję się i wchodzę do profilu. Tylko chwila to chyba nie mój profil?!!

Ok, to teraz przejrzyjmy innych biednych userów grupy. Szukamy naszego Iroen... jest!

Patrzymy kto kryje się pod tym zagadkowym nickiem...

I w ten sposób wystarczy przejrzeć inne osoby, aby dowiedzieć się, że profile linkują do kompletnie innych wpisów, coś mi się wydaję, że komuś się pomieszały ID-iki w bazie :-)
Perhaps - It's not a bug, it's feature.

środa, 29 października 2008

Darmowa wersja CodeRush wydana!

Światło dzienne ujrzała darmowa wersja świetnego dodatku do Visual Studio ułatwiająca pracę programiście poprzez kombinacje klawiaturowe oraz nowe metody refaktoringu kodu. Sam używam tego dodatku i wg. mnie jest on bardzo dobry, niestety za pełną wersję produktu trzeba słono zapłacić.
Strona produktu: DevExpress CodeRushX

wtorek, 28 października 2008

Gotowiec: Mega prosta ankieta w 2 minuty

Niedawno zostałem poproszony przez mojego kolegę, czy nie mógłbym mu stworzyć prostej ankiety? I tak oto w ciągu 1 minuty, 40 sekund powstała mega prosta ankietka, opierająca swoją konfigurację o plik Web.config - to tutaj definiowane są pytania i odpowiedzi na nie. Z tego względu, że często można przeczytać prośby początkujących o podobne rozwiązania postanowiłem tu niniejszy projekt umieścić. Zastrzegam, rozwiązania ma swoje wady, jednak w niecałe dwie minuty nie da się wszystkiego dorobić :-)

Strona Default.aspx
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="pnlSurvey" runat="server">
<asp:Label ID="lblQuestion" runat="server" />
<asp:Repeater ID="rptAnswers" runat="server"
onitemdatabound="rptAnswers_ItemDataBound">
<HeaderTemplate>
<h3>Możliwe odpowiedzi:</h3>
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="chckAnswer" runat="server" /><br />
</ItemTemplate>
</asp:Repeater>
</asp:Panel>
<asp:Button ID="btnNext" runat="server" Text="Dalej >>" onclick="btnNext_Click"
Visible="False" />
<br /> <asp:HyperLink ID="HyperLink1" runat="server"
NavigateUrl="Default.aspx?QuestionNo=0">Początek ankiety</asp:HyperLink>
</div>
</form>
</body>
</html>


Kod strony Default.aspx.cs
using System;
using System.Configuration;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
public string[] Questions
{
get
{
if (ConfigurationManager.AppSettings["Questions"] != null)
{
return ConfigurationManager.AppSettings["Questions"].ToString().Split(';');
}
return new string[] { };
}
}

public int ActualQuestionNo
{
get
{
if (Request.QueryString["QuestionNo"] != null)
return Convert.ToInt32(Request.QueryString["QuestionNo"]);
return -1;
}
}

protected void Page_Load(object sender, EventArgs e)
{
if (ActualQuestionNo >= 0)
{
PrepareAnswers(PrepareQuestion());
btnNext.Visible = true;
}
}

private void PrepareAnswers(int p)
{
if (ConfigurationManager.AppSettings["Answers"] != null)
{
string[] answers = ConfigurationManager.AppSettings["Answers"].ToString().Split(';');
if (p < answers.Length)
{
string[] anwersQ = answers[p].Split(',');
if (anwersQ.Length >= 1)
{
rptAnswers.DataSource = anwersQ;
rptAnswers.DataBind();
}
}
}
}

private int PrepareQuestion()
{
int questionNo = ActualQuestionNo;
if (questionNo >=0)
{
if (questionNo <= Questions.Length -1 )
lblQuestion.Text = Questions[questionNo];
}

return questionNo;
}


protected void rptAnswers_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
CheckBox chck = e.Item.FindControl("chckAnswer") as CheckBox;
if (chck != null)
chck.Text = e.Item.DataItem as string;
}
}
protected void btnNext_Click(object sender, EventArgs e)
{
if (ActualQuestionNo >= 0)
{
if (ActualQuestionNo < Questions.Length - 1)
{
//Zachowaj Odpowiedz
int toQuestion = ActualQuestionNo + 1;
Response.Redirect(string.Format("Default.aspx?QuestionNo={0}", toQuestion));
}
else
Response.Redirect("SurveyEnd.aspx");
}
}


}


Do Web.config-a należy dodać następujące wpisy:
    <appSettings>
<add key="Questions" value="Jakie masz ulubione danie?; Jakie lubisz marki samochodów?; Twoje zainteresowania"/>
<add key="Answers" value="Pizza, Gołąbki, Schabowy; Alfa Romeo, Audi, Fiat, Porshe; IT, Film, Muzyka"/>
</appSettings>


Tada!! Ankieta gotowa!!
Niech kod będzie za Wami!

poniedziałek, 27 października 2008

.NET ma nowe logo!

Faktem jest, że stare logo nie powalało na kolana, ale w sumie to nie miało być dzieło sztuki. Nowe nawiązuje min. do loga technologii Silverlight. Mnie osobiście podoba się średnio. Wolałbym, aby graficy odświeżyli stary layout loga, bo to jest takie nijakie... A Wam jak się podoba?

Mocking. Tworzymy i testujemy aplikację. odcinek II.

Zajmijmy się wykorzystaniem mockingu w praktyce. Do tego celu posłuży nam zaczątek kodu, który napisałem w poprzednim odcinku, czyli aplikacja TaxiService.
Główna logika biznesowa będzie zawarta w klasie TaxiService.cs, która to implementuje interfejs ITaxiService:


using System;

namespace MockingTaxiService
{
interface ITaxiService
{
bool Order(DateTime doDate, string toAddress, int clientID);
}
}

Implementacja interfejsu:


using System;

namespace MockingTaxiService
{
public class TaxiService : ITaxiService
{
#region ITaxiService Members

public bool Order(DateTime doDate, string toAddress, int clientID)
{
System.Threading.Thread.Sleep(5000);
return true;
}

#endregion
}
}
Prosta klasa klienta:


using System;

namespace MockingTaxiService
{
public class TaxiClient
{
public bool OrderTaxiTooday(string toAddress, int clientID)
{
TaxiService svc = new TaxiService();
return svc.Order(DateTime.Now, toAddress, clientID);
}

}
}

Jak można zauważyć samo wykonanie metody Order trwa niekrócej niż 5 sekund, ale prosto sobie wyobrazić przypadki, kiedy taka metoda może zabrać znacznie więcej z życia użytkownika tej właśnie metody. Spełniony w tym przypadku został jeden z podpuntów, kiedy warto stosować mocking: przypadek testowy trwa znaczny okres czasu zanim zwróci jakikolwiek wynik.
Czas najwyższy, aby na scenę wszedł wybrany przez nas framework do mockingu i abyśmy zrobili użytek z testów jednostkowych. Tworzymy projekt testów jednostkowych przy użyciu dowolnego środowiska - ja wybrałem darmowy MbUnit (http://mbunit.com), który mogę polecić.
Klasa testująca SmokeTest.cs


using System;
using MbUnit.Framework;

namespace MockingTaxiService.Tests
{
[TestFixture]
class SmokeTest
{
[Test]
public void TestShouldReturnTrue()
{
TaxiService svc = new TaxiService();
bool result = svc.Order(DateTime.Now.AddHours(5), "ul. Nieznana 12, Poznań", 1234);
// w przyp. sukcesu wynik powinien być typu: true
Assert.IsTrue(result);
}
}
}

Wykonajmy za ten pierwszy przypadek testowy, jego rezultat nie powinien być zaskoczeniem :-)






Test trwał długo - za długo aby mógł być wykonywany wraz z innymi. Tworzymy obiekt symulujący za pomocą RhinoMocks.
  1. Pobieramy pliki pakietu RhinoMocks ze strony: http://ayende.com/projects/rhino-mocks/downloads.aspx
  2. W projekcie testowym dodajemy referencję do pliku Rhino.Mocks.dll pobranego wcześniej pakietu RhinoMocks
  3. Zamieniamy ciało przypadku testowego stworzonego wcześniej, na nowe: korzystające z RhinoMocks:

using System;
using MbUnit.Framework;
using Rhino.Mocks;

namespace MockingTaxiService.Tests
{
[TestFixture]
class SmokeTest
{
MockRepository _mocks;

[SetUp]
public void initialize()
{
_mocks = new MockRepository();
}

[Test]
public void TestShouldReturnTrue()
{
var taxiService = _mocks.DynamicMock<ITaxiService>();
using (_mocks.Record())
{
SetupResult.For(taxiService.Order(DateTime.MinValue, null, 0)).IgnoreArguments().Return(true);
}

using (_mocks.Playback())
{
Assert.IsTrue(taxiService.Order(DateTime.Now, "ul. Nieznana 12, Poznań", 123));
}

}
}
}

Ok, przykład wymaga małego wyjaśnienia. Obiekt symulujący działanie tworzony zostaje przy pomocy:


var taxiService = _mocks.DynamicMock<ITaxiService>();

Jako parametr metody DynamicMock, której zadaniem jest dynamiczne stworzenie naszego "głupiego" obiektu jest interfejs bazowy obiektu, który będziemy symulować. W kolejmy fragmecie:


using (_mocks.Record())
{
SetupResult.For(taxiService.Order(DateTime.MinValue, null, 0)).IgnoreArguments().Return(true);
}

"nagrywamy" oczekiwane przez symulowany obiekt zachowanie. Uważny czytelnik zauważy, że przekazujemy do naszej metody Order puste parametry, gdyż one i tak nie będą brane pod uwagę przez środowisko testowe. Ignorowanie paramterów sygnalizujemy metodą IgnoreArguments() a po niej "na sztywno" podajemy jaki wynik zwóci nasza metoda: w tym przypadku true. Tak jak wcześniej pisałem kiedy tworzymy "głupi" obiekt, chcemy jedynie zasymulować działanie obiektu testowanego bez fizycznego zaprzęgania logiki biznesowej jaką w sobie zawiera.
Pozostaje nam teraz jeszcze wykonać test przy pomocy środowika klas testowych:



using (_mocks.Playback())
{
Assert.IsTrue(taxiService.Order(DateTime.Now, "ul. Nieznana 12, Poznań", 123));
}

Wynik testów jest zadowalający! Wystarczy sprawdzić, że test wykonał się błyskawicznie. Na tym zakończymy przygodę z mockingiem. Prawdą jest, że przedstawiony przypadek testowy to jedynie wzmianka o tym co potrafi cały framework RhhinoMocks oraz jego przyjaciel MbUnit. Zachęcam do przejrzenia dokuemntacji w/w frameworków.
Niech kod będzie za Wami!

niedziela, 26 października 2008

Mocking, a co to jest? odcinek I.

W dzisiejszym programie chciałbym przybliżyć nieco bliżej technikę Mocking. Czym jest mocking, do czego służy i czy można na tym zarobić? :-)
Mock (czyt. Mok), to specjalny obiekt tworzony w celu symulowania działania wybranych obiektów Twojej aplikacji. Stosuje się go głównie w technikach wytwarzania oprogramowania sterowanego testami jednnostkowymi, czyli TDD. Taki obiekt nie posiada w swojej implementacji pełnego zachowania obiektu, który naśladuje. No bo przecież inaczej po co miałby być wogóle tworzony. Jego zadaniem jest jedynie odpowiednie, ustalone reagowanie na jego wywoałania celem szybkiego testowania zachowania szerszego fragmentu systemu.
Dla jakich obiektów mocking jest najbardziej przydatny?
  • Obiekty, których stan trudno odtworzyć
  • Obiekty, których metody trwają znaczący czas (połączenia z bazami danych, zewnętrzne źródła danych)
  • Obiekty klas, których zależności z innymi obiektami są wysokie (trudno stworzyć instancję testowanego obiektu, gdyż posiada on rozległe zależności z resztą systemu)
  • Trudno przewidzieć wynik testowanej metody w trakcie testu (np. zwróć aktualną sekundę, aktualnej minuty, aktualnej godziny...)
Najpopularniejszymi środowiskami są: RhinoMocks (http://ayende.com/projects/rhino-mocks.aspx), Moq (http://code.google.com/p/moq/) i parę innych.
Przykład. Wyobraź sobie system internetowej rezerwacji taksówek. Podstawową funkcjonalnością systemu niech będzie możliwość złożenia zamówienia na określoną godzinę, określony adres - taksówki. Ok, nadal nie widzisz związku z tym co napisałem wyżej, spokojnie :-)
Główna metoda klasy TaxiService : ITaxiService:

public class TaxiService : ITaxiService
{
public bool OrderTaxi(DateTime toDateTime, string toAddress, int clientID)
{
//logika biznesowa
}
}
zanim złoży odpowiednią dyspozycję w systemie musi sprawdzić: dostępność taksówki, czas: czy jest możliwe przybycie auta na wybraną godzinę, odnaleźć dane klienta na podstawie jego numeru ID oraz sto innych rzeczy. Proces złożenia dyspozycji w samym systemie trwa np. ok 40 sekund, a jego wynikiem wartość boolowska.
40 sekund (albo i dłużej) to zbyt długo aby przeprowadzać na takim obiekcie testy: dobry test jednostkowy powinien być wykonany możliwie szybko i niezależnie od zewnętrznych danych, tak aby jego powtarzalność była możliwie prosta.
Wykorzystanie mockingu w tym przypadku polegać będzie na stworzeniu obiektu implementującego interfejs ITaxiService. Metoda Order nie będzie w takim obiekcie przetwarzała danych, zwróci jedynie wartość true, po to abyśmy mogli dalej testować system.

Tak pokrótce można scharakteryzować całą filozofię tego podejścia. W następnym odcinku postaram się wytłumaczyć mocking w przykładzie.

Console.WriteLine("Hello world!");

I wystartowałem.
Nazywam się Dariusz Tarczyński. Jestem programistą pasjonatą, programistą zawodowym oraz studentem technologii wytwarzania oprogramowania z Poznania.
Na blogu będę starał się przedstawiać głownie obszar moich zamiłowań, czyli platformy Microsoft .Net - pewnie ze szczególnym uwzględnieniem ASP .Net - bo w technologiach webowych najlepiej się czuję.
Ok, koniec bajdurzenia, do zobaczenia na blogu!