Thursday, 17 May 2012

TDD is a good teacher - both demanding and supportive

Thanks go to Kuba Miara for inspiration.

Last time I was debating my colleague on pros and cons of TDD, I made a mistake. I stressed too much that TDD is good, because it quickly shows you when the design is wrong and punishes you for not writing clean, properly encapsulated code. I did not talk so much about the support TDD provides, just about the punishment. By doing this, I got two counterarguments. Both of these are interesting and worth discussing. Today, I'd like to tackle the first one.

Teaching by punishment is too harsh.

The argument was like this: "If TDD is so demanding, it will be hard for people to satisfy these demands. There are better and worse developers. Some people are having hard times writing good code, now they would need to know how to do it plus how to write good unit tests. Since the latter is a challenge on its own, they're just gonna pay double.". This is a vision of TDD as a "strict teacher", that only makes demands and punishes you severely, when you're unable to make it. This is also a vision of a "bad teacher" that ONLY makes demands and it's the only kind of help it can provide.

This, however, is not true. TDD is a good teacher. How do you recognize a good teacher? Such teacher would:

  1. tell you when you're getting things wrong.
  2. support you in getting things right.

In TDD, the first is achieved by using various heuristics when looking at unit tests. If you're doing things wrong, your unit tests will tell you this (Amir and Scott have a nice summary in two parts of these heuristics). Now I'd like to concentrate more on the "supportive" stuff that I kind of missed during the original discussion - there are various ways TDD can support you in doing the right thing(TM). Among these things are:

Need Driven Design
helps in discovering new classes, methods and collaboration models as well as gaining high encapsulation and outside-in insight into your code (crafting method signatures from the view of their concrete usage, not from the top of your head), at the same time providing you a way to be more "lean" and write only the code you really need. I will do a separate post on Need Driven Design soon.
Test First
helping in many thing, but this time I'd like to stress that it lets you create classes with high testability by default - since you're writing the test before the code, you design the production code with testability in mind and it brings with itself stronger cohesion and encapsulation plus looser coupling.
Given When Then Analysis
helps you to think about the code in terms of behaviors, discover holes in requirements, prioritize the implementation and enables deliberate discovery of new behaviors the system must support.
TODO list
helping you in keeping track of the things left to specify, refactor and implement, so that you can know when you're done. Additionally, TODO list helps you in keeping focus on single behavior at once ("hmm, here, I'm passing two strings, but what if the first one is null? I'll just add it to TODO list and get to it after I specify current behavior"). Kent Beck's Test Driven Development By Example provides some good examples on how to work with TODO lists.
Triangulation
useful when driving implementation of a piece of code we don't have an idea how to implement. By specifying example after example of how we expect a single behavior to work from the outside and refactoring each time into something more general, we gradually gain understanding of the problem and drive towards the right implementation.

Each of these tools that come "bundled" with TDD has its own set of best practices that can make even a mediocre developer write good code quickly. There is only one requirement: motivation.

Ok, that's it for now, in the next post, I will try to discuss the second part of the argument. See ya!

Tuesday, 15 May 2012

Compiling... Linking... Unit Testing...

Today, I'd like to tell you two stories, draw a conclusion and then go on with the third story which I want to leave you with to think about.

Story 1: How unit test failures can look like compile time failures

Let me offer this as an introduction: ever heard of Continuous Testing for Visual Studio 2010? This is a nice little Visual Studio extension that runs all unit tests after each build automatically. No big deal, you can say, since adding a post-build action to a Visual Studio project will give you the same. This, however, is not the feature that made me think (and some nice heuristics did not as well). It does another thing that brought my attention.

It adds failed unit tests to Visual Studio compilation errors dialog.

So, each time a unit test fails, its results are displayed as you would expect compile time errors to appear. Only this time, instead of "Wrong syntax xyz", the message is "Assert.IsTrue(xyz) failed".

Story 2: How compile time checking becomes less important with unit tests around

Remember the post Bruce Eckel made somewhere in 2003 called Strong typing vs. strong testing? Bruce made a bold statement then, claiming that he doesn't need compile-time type checking when he's got a comprehensive suite of unit tests to back him up. This way he could move from Java to python without worrying too much about the type checking.

The conclusion

What's the conclusion? Let's paraphrase these two stories a little, then come up with the third story. The first story is about how unit testing output was "added" to the compiler toolchain. The second one is about how running unit tests replaced some of the program compilation benefits.

This begs the question: is there really such a sharp boundary between what we know as "compilation" and what we know as "testing"?

I wrote a compiler once. If we look at compilation phases, there are different kinds of checks involved: syntactic analysis, semantic analysis etc. What if we add another compilation step called "behavioral analysis" which consists of running your suite of executable specifications (AKA unit tests)? Sure, this kind of analysis is up to you more than any other, since you define the criteria for failure and success, but when it comes to running, it's just as any other compilation phase. As I mentioned, there are even tools that will add the result of this "behavioral analysis" in the same place as syntax errors (like misplacing a coma) or semantic errors (like trying to use a type that does not exist), so that it is indistinguishable. And, in my opinion, this is how running unit tests should be treated - as a part of build process. When unit tests are disabled, you disable one kind of analysis that is run on your code. Do you care? Well, when developing a program, if you could disable type checking in, let's say, C++, would you go for it? Even for a compilation speed up?

Ok, then, now that I've drawn my conclusions, it's time for the third story.

Story 3

Someone comes over to your desk and tells you "We couldn't get a successful build because of failing unit tests, so we disabled them".

Good night everyone and sweet dreams!

Saturday, 12 May 2012

Test First - why is it so important in TDD?

Some time ago, I've read a great post by Amir Kolsky and Scott Bain about why Test First technique is so important in TDD. I'd like to use it to elaborate on the few reasons I consider Test First to be an essential practice:

1. You don't know whether the test can ever fail.

This is one of the main points by Amir and Scott. When your write your test after the fact, you don't even know whether the test can ever fail even when the behavior described by the test is broken/changed.

The first time I encountered this argument (and it was long before the Amir and Scott's post), it quickly raised my self-defense mechanism: "But how can it be? I'm a wise person, I know what code I'm writing. If I make my unit tests small enough, it's self-evident that I'm describing the correct behavior. This is paranoid". However, this turned out not to be true. Let me describe, from my experience, just three ways (I know there are more, I just forgot the rest :-D) one can really put in a unit test that never fails:

a) Accidentially skipping addition to test suite.

However funny this may sound, it happens. The example I'm going to give is from C#, but almost every unit testing framework in almost every language has some kind of machanism of marking methods as "tests", whether by attributes (C#) or annotations (Java) or with macros (C and C++) or by inheriting from common class, or just a naming convention.

So, in my example, I'm using nUnit. In nUnit, to make a method "a test", you mark its class with [TestFixture] attribute, and every test must be marked with [Test] attribute in the following way:
[TestFixture]
public class CalculatorSpecification
{
  [Test]
  public void ShouldDisplayAdditionResultAsSumOfArguments() 
  {
    //... 
  }
}
Now, imagine that you're doing post-factum unit testing in an environment that has, let's say, more than thirty unit tests - you've written the code, now you're just making test after test "to ensure" (as you see, this is not my favourite reason for writing unit tests) the code works.Test - pass, test - pass, test-pass. You almost always launch the whole suite, since it's usually painful to point out each time, which test you wanna run, plus, you don't want to introduce a regression. So, this is really: Test - all pass, test - all pass, test - all pass... Hopefully, you use some kind of snippets mechanism for creating new unit tests, but if not (and many don't actually do this, myself included :-( ), once in a while, you do something like this:
[TestFixture]
public class CalculatorSpecification
{
  //... some unit tests here

  public void ShouldDisplayZeroWhenResetIsPerformed()
  {
    //... 
  }
}
And you don't even notice that this is not added to your test suite, because there are so many unit tests already that it's almost irrational to search for your new test in the test list. Also, this fact that you have omitted the addition, does not disturb your work flow: test - all pass, test all pass, test - all pass... So, what you end up is a test that not only will never fail - it will never be executed.

So, why does Test First help here?  Because, in Test First, a test passing right away is what DOES disturb your work flow. In TDD, the work flow is: test - fail - pass (ok, and refactor, but for the sake of THIS discussion, it doesn't matter so much), test - fail - pass, test - fail - pass... 

Once in a while, I stumble upon a situation where Test First saves me from this trap.

b) Misplacing mock setup

Ok, this may sound even funnier (well, honestly, every mistake sounds funny), but it happened to me a couple of times, so it's beneficial to mention this. The example I'm going to show uses manual mocks, but, just not to jump into "dynamic vs handmade mocks" discussion here, this can happen with dynamic mocks as well, especially if you're in a hurry.

So, without further ado, the code (yes, this is a stupid example I made up in two minutes, so don't criticize the design here, please ;-):
[Test]
public void ShouldRecognizeTimeSlotAboveMaximumAllowedAsInvalid()
{
  var frame = new FrameMock();

  var validation = new Validation();
  var timeSlotAboveMaximumAllowed = TimeSlot.MaxAllowed + 1;

  var result = validation.PerformForTimeSlotIn(frame);
  frame.GetTimeSlot_Returns = timeSlotAboveMaximumAllowed;

  Assert.IsFalse(result.Passed);
  Assert.AreEqual(
    ValidationFailureReasons.AboveAcceptableLimit, 
    result.Reason);
}
Note how the tested method (which is PerformForTimeSlotIn()) is called BEFORE the mock is actually set up and the set up return value is never taken into account. So, how did it happen that, despite this fact, the call yielded correct result? This sometimes happens, and it happens most often in case of various boundary values (nulls etc.).

c) Using static data inside production code.

Once in a while, you have to jump in and add some tests and logic to code that's written by someone else. Imagine this code is a wrapper around your product XML configuration file. You decide to write your unit tests after applying the changes ("well", you can say, "I'm all protected by the suite that's already in place, so I can make my change without risking regression, then just test my changes and it's all good..."). 

So, you start writing a test. The test suite class contains a member like this:
XmlConfiguration config = new XmlConfiguration(xmlFixtureString);
What it does is to set up a global object used by all the tests. BUT, what you can also see, it uses the same fixture object every time. What you need to write tests for is a little corner case that does not need all this crap that already got into the global fixture. So, you decide to start fresh and write your own fixture. Your test begins like this:
string customFixture = CreateMyOwnFixtureForThisTestOnly();
var configuration = new XmlConfiguration(customFixture);
...
And it passes. Ok, what's wrong with this? Nothing big, unless you read the source code of XmlConfiguration class carefully. Inside, you can see, where the xml string is stored:
private static string xmlText; //note the static keyword!
What the...? Well, well, here's what happened: the author of this class coded in a small little optimization. He thought: "The configuration is only changed by the field group and to do it, they have to shut down the system, so, there is no need to read the XML file every time an XmlConfiguration object is created. I can save some cycles and I/O operations by reading it only once when the first object is created. Another created object will just use the same XML!". Good for him, not so good for you. Why? Because (unless your test runs first), your custom fixture will never be used!

This was the last way I wanted to mention, now to the second point.

2) "Test After" ends up as "Test Never"

Once in a while, I come into argument with a coworker of mine. What those guys usually say is: "Ok, I know that unit testing is good, I just don't buy the Test First part". When I raise the argument mentioned in point 1, they say: "I can make it without Test First - after I write each unit test, I modify production code on purpose to make sure this test fails - then I get the same value as you do, without the Test First absurd". What I usually do then is to take them to my desk and show them a code coverage report for a single file. All of the methods are covered in green as covered, and one single method is in red as not covered. Then I say: "Guess which parts of this code was written Test First and which is written Test After". I like it when they discover (or I tell them) that this uncovered code is Test After that ended up as Test Never.
Let's be honest - we're all in a hurry, we're all under pressure and when this pressure is too high, it triggers heroic behaviors in us, especially when there's a risk of not making it with the iteration commitment. Such heroic behavior usually goes by the following rules: drop all the "baggage", stop learning and experimenting, revert to all of the old "safe" behaviors and "save what we can!". If tests are written last, they're considered "baggage", since the code is already written, "and it will be tested anyway" by real tests (box testing, smoke testing, sanity testing etc. comes into play). It is quite the contrary when using Test First, where failing test is a reason to write any code. To write the code, you need the reason and thus, unit tests become irremovable part of your development. By the way, I bet in big corporations no one sane ever thinks they can abandon checking in the code to source control, at the same time treating unit tests as "an optional addition", but that's a topic for another post.


3) Not doing Test First is a waste of time.

One day, I was listening to Robert C. Martin's keynote at Ruby Midwest 2011, called Architecture The Lost Years. At the end, Robert made some digressions, one of them being about TDD. He said that writing unit tests after the code is not TDD. It is a waste of time. 

The first time I thought about it, I thought it was only about missing all the benefits that Test First brings you: the ability for a test to fail, ability to do a clean-sheet analysis, ability to do Need Driven Design etc., however, now I think there is more to it. If you're reading the Sustainable Test Driven Development blog regularly, you know that Amir and Scott value testability as design quality, along with cohesion, encapsulation and others. Also, they state that in order to make TDD (and even plain unit testing for that matter) sustainable, the code must have this testability quality on a very high level. How can we use this valuable insight to identify the waste? Let's see how testability looks like in Test First workflow (let's assume that we're creating new code, not adding stuff to dirty, ugly legacy code):

- Write unit test that fails (The code has high testability
- Write code that satisfies the test

Now, how does it usually look like in Test After approach (from what I saw in various situations):

- Write some code (probably spans few classes until we're satisfied).
- Start writing unit tests
- Notice that unit testing the whole set of classes is cumbersome and unsustainable and contains high redundancy.
- Refactor the code to be able to isolate objects and inject some mocks (The code has high testability)
- Write proper unit tests.

As you may have noticed, I emphasized few steps that are additional in Test After approach. What's their equivalent in Test First? Nothing! Doing these things is a waste of time! And, this is a waste of time I'm seeing done over and over again!

Anyway, because of this, let me say it clearly: if you've got a guy in your team that's doing TDD and he's the only one, and you're just "coding and shipping" carelessly even without unit tests, be informed, that this guy will ALWAYS have to corect your code to make it testable and, for that matter, better designed. So, if you really wanna stay on the dark side and not do what's right and ethical (which is to do TDD), at least buy him a soft drink from time to time.

Ok, that's it. Enjoy your weekend!

Saturday, 5 May 2012

Mockowanie funkcji języka C bez użycia C++

Joł!

Jeśli ktoś kiedyś przeczyta mój poprzedni wpis, być może zastanowi się, czy nie można by przenieść mechanizmu nadpisywania funkcji z HippoMocks tak, żeby wykorzystywać go w zwykłym C.

Dobra wiadomość jest taka, że jest to możliwe i że mechanizm nadpisywania funkcji z hippo mocks bardzo łatwo jest wydzielić, natomiast zła wiadomość jest taka, że przeniesienie samego mechanizmu nadpisywania nie daje żadnej specjalnej zalety w porównaniu do np. słabych funkcji gcc.

W każdym razie, dla odważnych - jeśli zajrzycie sobie do źródeł ostatniego wydania HippoMocks (w moim poprzednim wpisie jest adres), na pewno zauważycie klasę Replace. To właśnie ta klasa służy do podmiany funkcji. Zademonstruję teraz na krótkim przykładzie jej działanie:

#include <stdio.h>
#include "hippomocks.h"

void fooA()
{
  printf("fooA\n");
}

void fooB()
{
  printf("fooB\n");
}

int main()
{
  HippoMocks::Replace replaceInThisScope(fooA, fooB);
  fooA(); // => "fooB"
  return 0;
}

Obiekty klasy Replace są dosyć sprytne, gdyż działają na zasadzie RAII (zdobywanie zasobów jest inicjalizacją), czyli w konstruktorze dokonują podmiany, natomiast w destruktorze przywracają stan domyślny. Oznacza to, że podmiana trwa tak długo, jak długo dany obiekt klasy Replace jest w zakresie.

Tak czy inaczej, nic nie stoi na przeszkodzie, żeby wyrwać tę klasę (i kilka jej zależności:  klasę Unprotect, typ e9ptrsize_t oraz funkcję horrible_cast (sic!)) i przerobić ten kodna kod C, korzystając z funkcji i makr (przynajmniej pod systemami linuksowymi powinno to zadziałać, nie próbowałem przerabiać wersji windowsowej, chociaż w nagłówku hippomocks.h również takowa jest.

Friday, 4 May 2012

Mockowanie funkcji języka C za pomocą HippoMocks

Hej!

Dzisiaj pokażę Wam, jak za pomocą ostatniej działającej wersji szkieletu HippoMocks zastąpić funkcję z języka C swoją własną (innymi słowy, to będzie tzw. mock funkcji). Jako że życie to nie film, do takiego przedsięwzięcia będziemy potrzebować kompilatora C++ (innymi słowy - nawet jeśli Twój produkt jest cały pisany w C, to na potrzeby testów jednostkowych musisz kompilować go kompilatorem C++).

Moje środowisko (na wszelki wypadek, gdyby coś komuś nie chodziło):
1. Kompilator g++ 4.6.3
2. System operacyjny Ubuntu Linux 12.04 z zainstalowanym pakietem build-essentials
3. Nagłówek hippomocks.h ściąnięty ze strony http://www.assembla.com/code/hippomocks/git/nodes/master/HippoMocks/hippomocks.h (należy kliknąć ikonkę Download).
4. Platformę X86 (bodajże na x86-64 też powinno działać) - Sparc, sorry...

Teraz jedna mała uwaga: biblioteka HippoMocks w całości zawarta jest w jednym pliku nagłówkowym hippomocks.h. Ten plik musimy sobie dorzucić do swojej kompilacji. Natomiast sama kompilacja tego pliku może nie przejść!! U mnie wysypywał się na instrukcji prinff(), do której nie miał nagłówka:

../src/hippomocks.h:3990:94: error: ‘printf’ was not declared in this scope
make: *** [src/Main.o] Błąd 1

Na szczęście można temu szybko zaradzić, znajdując w hippomocks.h pierwszy ciąg instrukcji #include i dodając tam: #include <stdio.h>

No i na koniec mały przykład, jak to działa:

#include <iostream>
#include "hippomocks.h"

int ReturnOne()
{
  return 1;
}

int main()
{
  HippoMocks::MockRepository mocks;
  mocks.OnCallFunc(::ReturnOne).Return(123);

  std::cout << ReturnOne() << std::endl; // 123
  return 0;

Powodzenia!

Saturday, 28 April 2012

Biurkowa tablica pseudo kanbanowa

Hej!

Ostatnio doszedłem do wniosku, że fajnie by było postęp niektórych moich przedsięwzięć śledzić na zasadzie tablicy scrumowej, tak jak dzieje się to u mnie w pracy. 

Stwierdziłem, że postawię na tablicę wirtualną zamiast fizycznej, z racji jej większej "mobilności". Nie potrzebowałem żadnych specjalnych możliwości - jedynie możliwość dodawania "backlog itemów", przypisywania do nich zadań i przeciągania ich po tablicy. Szukałem długo odpowiedniej aplikacji, w pewnym momencie nawet miałem ochotę napisać własną, kiedy zdałem sobie sprawę, że za bardzo kombinuję - jak to zwykle bywa, najlepsze są te najprostsze rozwiązania. Ściągnąłem zatem aplikację XPad i korzystając ze zwykłych żółtych karteczek skleciłem coś takiego:



Tuesday, 11 January 2011

Test Driven... Design?

Ostatnio miałem kilka dyskusji na temat testów jednostkowych oraz Test Driven Development.

Postaram się omówić główne punkty mojej argumentacji za stosowaniem testów jednostkowych oraz metody TDD, którą wolę tłumaczyć jako Test Driven Design.

1. "Nie ma dowodów, że w ten sposób znajduje się więcej błędów"

TDD nie służy do znajdowania błędów (przynajmniej nie taki jest jego główny cel). Jego przeznaczeniem jest projektowanie aplikacji, tzn. pisze się najpierw test, służący specyfikacji fragmentu funkcjonalności, który mamy zaimplementować. W tym sensie zestawy testowe są wykonywalnym zamiennikiem dla dokumentów tzw. Low Level Design, gdzie jest opis słowno - muzyczny i diagramy UMLowe.

2. "TDD cierpi na tzw. "syndrom ojcostwa" - jeśli ktoś pisze testy, to robi te same założenia, co pisząc kod. Normalniejsze podejście jest takie, że testy pisze jedna osoba, a kod druga".

TDD nie służy do znajdowania luk i błędów w naszym rozumieniu wymagań. Od tego są inne metody testowania. TDD służy jedynie do upewnienia się, że kod robi to, co chcemy, żeby robił (bez względu, czy jest to zgodne z wymaganiami, czy nie). Owszem, fakt że piszemy testy tylko w oparciu o wymagania pomaga nam nie czynić założeń oraz druga osoba może potem przejrzeć scenariusze testowe i zweryfikować ich poprawność - w tym sensie weryfikuje nasz projekt.

4. "TDD to strata czasu - można przecież od razu pisać kod".

a) Jak wspomniałem, można traktować zestawy testów jednostkowych jako wykonywalne dokumenty LLD. Warto zadać sobie pytanie, czy łatwiejszy i mniej czasochłonny w pisaniu i utrzymywaniu jest taki dokument (który czasami potrafi mieć nawet 60 stron A4), czy zestaw testów, które dodatkowo weryfikują kod.

b) Skłaniam się do traktowania również procesu puszczenia testów jednostkowych jako elementu kompilacji. Kompilacja kodu np. w C++ również zajmuje sporo czasu, natomiast wielu z nas nie chciałoby oddawać gwarancji uzyskanych przez sam fakt, że kompilujemy nasz kod (zazwyczaj jest to sprawdzanie typów, statyczna analiza kodu itp.). Bruce Eckel zauważył nawet, że w przypadku języków interpretowanych, takich jak Python, posiadając zestaw testów jednostkowych pokrywających znaczny obszar kodu, wcale nie tęskni za kompilacją kodu, bo jeśli wszystkie testy przechodzą, to znaczy, że nie ma w nim błędów, które mógłby wyłapać kompilator.

c) Testy jednostkowe pelnią rolę dokumentacji i listy kontrolnej. W tym pierwszym znaczeniu dobrze dokumentują kod i gwarancje, które muszą być przezeń spełnione. W tym drugim - dobrze pokazują w trakcie kodowania, co jest już zaimplementowane, a co oczekuje na implementację. Stosując się do zasady pisania testów najpierw, wiemy że nienapisany test = niezaimplementowana funkcjonalność. Dzięki temu łatwiej sprawdzić, które wymagania mamy pokryte, a które nie.

5. "Testy jednostkowe utrudniają zmiany w kodzie, bo w razie czego trzeba je przerabiać".

Z mojego doświadczenia wynika, że testy jednostkowe ułatwiają zmianę, bo duże pokrycie funkcjonalności testami daje nam dużą pewność, że nic nie popsujemy. I znowu, jeśli uznamy, że nienapisany test = niezaimplementowana funkcjonalność, to wiemy, że jeśli cały zestaw testów przejdzie po naszych zmianach, to jesteśmy bezpieczni.