My other blog (in Hungarian) merhetetlen.blogspot.com

Saturday 18 May 2013

Abstracting abstract abstractions

Even on Dagobah there is no such a thing: enough rain.

Abstractions are good. We can simplify things based on the way how we want to use it. The last time when I was on a QA conference on our one and only Death Star in the Nether sector, there were a whole presentation about how can we create a good framework with creating layers of abstractions.

Lets take an example:
    You have to test a backend user management system, which has a REST API(or SOAP, or json over http or basically whatever). When you have to test it you will create an abstraction for sure which maps your tests to actual http calls somehow. and hopefully you won't make http calls from your test methods every time... This is layer 1.

    After a while, when you get bored to set up the same object over and over again when you want to log in with a test user, or register, or do something you will decide to create another abstraction with util methods for those commonly used actions or group of actions you have to perform over and over again. This is layer 2.

   After a few months or after adding much more tests or after covering many many new features you realize even with these util methods sometimes it is painful (in terms of lines of code, or in terms of readability / maintainability) to set up complex preconditions. So you decide you need another layer. But it is not obvious what.

    Maybe you already have a class file which represents your test users. So it can hold basic or not basic info like: first name, last name, dob, email, password, number of dogs, IQ etc... And maybe you will start to wonder: if I have a list (or something like a list) of attributes what a user can have, why do not I have a list of actions what a user can perform, or can take. You can have things like: login, register, send message to someone, get banned, fly to the moon, get fired, get attacked by Jedis etc...
    One of the best way to list things in java is to create an enum. Another benefit is, you can define an abstract method, what every enum must implement, something like: doAction(TestUser). So in you enum thing, you can list your actions, and implement the required api calls to achieve that action (use things from previous layer). And in your test user object you can do a method like: doActions(MyOwnActions... action) and you can list those things what you want to perform with your test user. This is layer 3. and it is really handy:
user.doActions(REGISTER, LOGIN, GO_TO_MOON, KILL_A_JEDI, EAT_BAKLAVA);

    So we have 3 layers for a simple api testing which abstracts the http calls to a level where you can just list what you want to do and your test user will be in that state. But what happens if someone testing the GUI for example needs your test user thingy. You can tell him to do what you did, so before the tests create a test user instance and set it up with the required actions and so on.
It means they have to change or have the same kind of things on multiple place (of course depends on, maybe you have to keep your test class hierarchy flat, and you do not need users in every test). So maybe in that framework it can be ugly to set up the test users everywhere, and you want to provide a nice way to just get what you want.
    You already have a static abstraction about the actions what your test users can perform, so you can easily create dependency injections.
    Well I know that this example is not that common, but in my case we cannot just inject the dependency with guice or something... here, in the tests the test user depends on runtime parameters, and you need a given and often various state of the user.
    What can you do? You can create an annotation like: @TUser(actions={Action1, Action3}) and if you use testng you can override the IMethodInterceptor so you can create a test user into the annotated field based on some parameters (from the test instance, or runtime things) you decide to use. So you have abstracted the test user initialization, and this is layer 4.

...well, when I implemented this I thought there are at least 8 level of abstractions .. it's only 4, but the message is clear, if things are getting complicated and you have lot of duplication do not afraid to take a step back, look it from a new point of view, and put those things what you need to another layer.

...sorry for the half broken language, my brain is half off....
 

Gungans are everywhere

Maybe it is just me, but I often get annoyed by the dozens of gungans who appeared around me.... they are everywhere...

I am on a user's and dev's list of a testing tool and each and every day I see a post from a gungan... it is a safe bet that the post has been sent from a gungan when:

  • he sends his question to both list, he did not bother to read the purpose of the mailing lists, of course when someone tells him, not to do so, he appologies and promise to not do it again (it does not really matter, because someone else will do the some next day)
  • his question is
    • pointless, meaningless, I mean I am not an expert of the high galactic language but at least I try to describe my problem as simple and detailed as possible (true, not often perfect), but here you have to call a protocol droid to understand the message.
    • extremely generic, I mean so generic at the first time you think it is a joke
    • completely unrelated to the tool, so you guess he was just using that tool in a different window when that problem occurred in a completely different application in another window/screen/battle ship
    • unrelated to the tool, so the problem is with something what this tool is using and it is clear from the error message, or whatever
  • his email is
    • without details... so basically you have no idea what he is talking about, but he experienced something with that tool
    • has a lot of details... so you get the description of the problem, the log files, the content of his HDD, his mail history, all together is bigger than 1 petabyte.
    • has enough details but copy pasted to the mail with broken format, so no one can actually understand it (and sometimes the last two are combined)
  • his problem is
    • easily solvable, so if he read the error message, he could see (hopefully) what is the problem and how to solve it, because it explicitly tells him where is the problem, what to do, it is even gives a nice recipe for his date tomorrow....
    • already answered 10 billion times
    • can be solved after 20 second of galactic network searching...
  • his emails
    • are coming sequentially
      • in every half a day about the same problem, in the same thread 
      • in every day about the same problem in different thread
      • in every minutes about different "problems" in different threads (ok I know, at least it is a proper problem/thread usage)
I have asked a lot of stupid questions as well (and I will), but I always tried to find my answers before, I even tried to debug the application, sacrifice a wookie just to not ask, before I did so....

... and only a gungan can call himself in public as something like: Emperor certified jedi slayer professional after 2 mission happened on a deserted planet hunting scorpions...