Pokazywanie postów oznaczonych etykietą castle. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą castle. Pokaż wszystkie posty

sobota, 16 sierpnia 2014

[C#|Visual Studio] Rhino Mocks

Idealny Unit Test to taki, który jest:
  1. atomowy (testujemy tylko jeden fragment funkcjonalności)
  2. deterministyczny
  3. powtarzalny
  4. niezależny od kolejności wywoływania innych testów 
  5. szybki (czas wykonania rzędu milisekund)
  6. łatwy do uruchomienia
 Problemem zawsze będą zależności. Przeważnie metoda poddawana testowi woła metody innych klas, które mogą wykonywać wolne operacje, np. łączenie się z bazą danych. Co więcej testowanie metod w ten sposób narusza zasadę atomowości Unit Testów.

W tym przypadku z pomocą przychodzi zasada Dependency Inversion. Zamiast wstrzykiwać do testowanego serwisu właściwe zależności, możemy na etapie UT wstrzyknąć inną implementację danego interfejsu wykonującą tylko tyle kodu, ile potrzebujemy. Problem polega na tym, że wraz ze wzrostem komplikacji właściwych serwisów musimy także utrzymywać kod mockowanych serwisów.

Tu z pomocą przychodzi Rhino Mocks - framework budujący dynamiczne mocki (wykorzystując obiekty proxy) na potrzeby naszych testów.

 Mockowanie rozpoczynamy od użycia klasy MockRepository, która stworzy nam mock dla dowolnego interfejsu. Mock taki będzie zawierał puste metody (dla metod zwracających void) lub metody zwracające wartość domyślną dla pozostałych metod z interfejsu.

Przykładowo mamy klasę InvoiceService, która woła jedną ze swoich zależności - InvoiceRepository.

public class Invoice
{
    public string Id { get; set; }
    public decimal Amount { get; set; }
    public string UserId { get; set; }
}

public interface IInvoiceRepository
{
    bool Store(Invoice entity);
}

public class InvoiceRepository : IInvoiceRepository
{
    public bool Store(Invoice entity)
    {
        //Long running SQL operation
        return true;
    }
}

public class InvoiceService
{
    private IInvoiceRepository _invoiceRepository;

    public InvoiceService(IInvoiceRepository invoiceRepository)
    {
        _invoiceRepository = invoiceRepository;
    }

    public void SaveInvoice(Invoice item)
    {
        var success = _invoiceRepository.Store(item);
        if(!success)
            throw new Exception("Unable to save");
    }
}

Chcemy zamockować interfejs IInvoiceRepository w ten sposób, by zwracał true, jeżeli podamy fakturę różną od null.

[Test]
public static void InvoiceService_ShouldCallInvoiceRepository()
{
    //Arrange
    IInvoiceRepository repositoryMock = MockRepository.GenerateMock<IInvoiceRepository>();
    repositoryMock.Stub(x => x.Store(Arg<Invoice>.Is.NotNull)).Return(true);

    Debug.WriteLine(repositoryMock.GetType().FullName);
    var service = new InvoiceService(repositoryMock);

    //Act
    service.SaveInvoice(new Invoice());

    //Assert
    repositoryMock.AssertWasCalled(s => s.Store(Arg<Invoice>.Is.Anything));
}

Na konsoli wypisze się: Castle.Proxies.IInvoiceRepositoryProxyc9859a0ce17d461faef8b8deb39a407c, ponieważ RhinoMocks korzysta z mechanizmu Castle.DynamicProxy.

Zamiast parametru Anything moglibyśmy podać referencję do obiektu faktury przekazywanej do metody SaveInvoice. Często zdarza się też tak, że metoda przyjmuje kilka parametrów, na podstawie których budowany jest obiekt. Tu z pomocą przychodzi mechanizm constraintów.

Rozszerzamy nasz serwis o dodatkową metodę:

public class InvoiceService
{
    //...
    public void SaveInvoice(string id, string userId, decimal amount)
    {
        SaveInvoice(new Invoice()
                        {
                            Amount = amount,
                            Id = id,
                            UserId = userId
                        });
    }
}

I za pomocą metody Matches podajemy warunki, jakie muszą być spełnione.


[Test]
public static void InvoiceService_ShouldCallInvoiceRepository_AndSaveInvoiceWithSameProperties()
{
    //Arrange
    IInvoiceRepository repositoryMock = MockRepository.GenerateMock<IInvoiceRepository>();
    repositoryMock.Stub(x => x.Store(Arg<Invoice>.Is.NotNull)).Return(true);
    string id = "12";
    string userId = "5";
    decimal amount = 100;

    var service = new InvoiceService(repositoryMock);

    //Act
    service.SaveInvoice(id, userId, amount);

    //Assert
    repositoryMock.AssertWasCalled(s => s.Store(Arg<Invoice>.Matches(d => d.Amount == amount 
        && d.UserId == userId)));
}

niedziela, 29 czerwca 2014

[C#|Visual Studio] AOP: Method Interception

Opakowanie metody możemy wykonać zarówno w Castle.DynamicProxy jak i przez Postsharp. Oba podejścia nieco różnią się od siebie.

W przypadku Castle zakładamy, że będziemy korzystać z kontenera IoC. Mamy prosty interfejs IMyInterface oraz prosty typ MyType. Rozpoczynamy od stworzenia interceptora, który będzie logował zdarzenia. Musi on dziedziczyć po interfejsie IInterceptor z przestrzeni nazw IInterceptor. Interfejs ten wystawia metodę Intercept.

public class LoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("On Enter");
        try
        {
            invocation.Proceed();
        }
        catch (Exception)
        {
            Console.WriteLine("On Error");
            throw;
        }
        finally
        {
            Console.WriteLine("On Exit");
        }
    }
}

Pozostaje jeszcze skonfigurować odpowiednio kontener i można korzystać z typu opakowanego w intereceptor.

using (var windsorContainer = new WindsorContainer())
{
    windsorContainer.Register(Component.For<LoggingInterceptor>());
    windsorContainer.Register(Component.For<IMyInterface>()
                                    .ImplementedBy<MyType>()
                                    .Interceptors<LoggingInterceptor>());

    var type = windsorContainer.Resolve<IMyInterface>();

    type.Run();
}

W Postsharpie dziedziczymy po bazowej klasie OnMethodBoundaryAspect, która definiuje potrzebne nam metody wirtualne. Ważne, by nasz aspekt oznaczyć atrybutem Serializable.

[Serializable]
public class LoggingInterceptor2: OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("On Entry");
        base.OnEntry(args);
    }

    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine("On Error");
        base.OnException(args);
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Console.WriteLine("On Exit");
        base.OnSuccess(args);
    }
}

Nasza klasa jest atrybutem, który możemy umieścić nad definicją dowolnej metody.

[C#|Visual Studio] AOP: Wprowadzenie

Programowanie aspektowe powstało niejako w odpowiedzi na powtarzające się schematy pisania kodu takie jak na przykład instrukcje try-catch, transakcje, logowanie itd. Aby uniknąć kopiowania kodu wprowadzono komplementarne do OOP pojęcie aspektów. AOP wprowadza kilka ważnych pojęć:
  • cross-cutting concerns - funkcjonalności używane w wielu miejscach w systemie, np. logowanie zdarzeń
  • advice - kod wykonujący cross-cutting concern
  • pointcut - miejsce występowania aspektów (np. wyjście metody abc)
  • tangling - określenie opisujące wymieszanie kodu typu advice z logiką biznesową
  • weaving (dzierganie) - proces wstrzykiwania kodu związanego z aspektem do logiki biznesowej
Cała idea polega na tym, że chcemy oddzielić cross-cutting concerns od właściwej logiki. Często nie wystarczy wydzielenie do nowej klasy, bo mamy np. do czynienia z konstrukcjami języka typu try-catch-finally. AOP daje nam kod czystszy i napisany w bardziej deklaratywny sposób. Programowanie aspektowe zapewnia zachowanie Single Responsibility Principle, a co za tym idzie znacznie ułatwia refaktoryzację. W wielu przypadkach można je porównać do wzorca dekoratora (np. opakowujemy wywołanie metody w dodatkowy kod).

Przykładowe lokalizacje aspektów:
  • przed wywołaniem metody
  • po pomyślnym zakończeniu metody
  • przy rzuceniu wyjątku
  • zawsze po metodzie (wewnątrz bloku finally)

Najpopularniejsze narzędzia związane z  AOP to Postsharp oraz Castle Dynamic Proxy. Postsharp działa na zasadzie postkompilacji modyfikując wytworzone przez Visual Studio assembly. Dodaje on instrukcje IL w wybranych przez nas miejscach. Jest zintegrowany z VS tak, że możemy przeprowadzać dzięki niemu np. walidację postkompilacyjną. Jeżeli assembly nie przejdzie takiej walidacji, to VS zwróci błąd kompilacji.
Instalujemy go NuGetem, przy czym za pełną wersję trzeba zapłacić.



Drugie narzędzie co Castle Dynamic Proxy. Wstrzykiwanie aspektów odbywa się tu runtime-owo i jest powiązane z kontenerami IoC. Odpytując kontener o dany interfejs dostajemy jego implementację opakowaną w kod przerywający wykonanie metody i dodający do niej kod aspektowy. Jest częścią większego frameworka Castle, w którym kontenerem IoC jest Castle Windsor.