Pages

Thursday, 5 April 2012

DUnit and TCustomAttributes

In this article I am playing with Unit tests and TCustomAttributes as I am working on different ideas to build up a lightweight testing framework. Basically I liked the idea of TestCase attribute from NUnit and I wanted to do something similar using TCustomAttributes and accessing those attributes using the Delphi RTTI library. To understand better my purposes have a look at the following example extracted from NUnit webpage:

Notice that every test case contains a set of Custom attributes and they will be executed by using a delegate. I have included the Result parameter in the attribute so the test can know the result straight away and inform about it.

6 comments:

The problem with your approach is that it requires putting way to much stuff inside the test method itself like getting out the passed values itself.

Also it does not improve DUnit itself which is only able to execute unparameterized methods.

I did this while ago when I saw a question for exactly that on SO (http://stackoverflow.com/questions/8999945/can-i-write-parameterized-tests-in-dunit)

The only disadvantage of my solution is because attributes in delphi have very restricted kinds of parameters. Like you cannot have arrays as in .Net. So my solution uses a semicolon separated string to pass the arguments. But you can just extend the attribute class with more constructor overloads if you like.

That way you can easily write test methods that take parameters and specify the parameters in the attributes as in .Net as you showed at the beginning. And actually it makes the test code itself also a one liner and not a bunch of setup code that you have to write every time.

Thanks for your comment Stefan, it is always appreciated. What I can say?, I take my hat off to you. I have reviewed your solution for DSharp.Testing.DUnit and I think it is brilliant.I'm glad that at least we think alike as I wanted to achieve the same behaviour as C Sharp and you were able to mimic it better.

I agree with you on the only drawback of using your solution as it relies on the user correctly inputting the semicolon separated values. As per my solution I rely on the TCustomAttribute so there is no misunderstanding as to what values are acceptable within the attribute.

With my solution you can easily inherit your attribute classes from TestCaseAttribute and override the constructor. Fill FValues with the passed arguments and you are done. Everything else is handled by the framework.

That reduces the actual implementation to 4 lines for each attribute (1 SetLength and 3 Assignments) and zero lines inside the test method itself which then just gets some parameters.

P.S. Be careful with the types you use in the arguments. Turned out variants are kinda broken: http://qc.embarcadero.com/wc/qcmain.aspx?d=104778

I'm sure that in the end we can get something really interesting. I'll give a try to your solution and see how can I use it. I'm still thinking of building a lightweight test framework to run test cases using TCustomAttributes as from my point of view I think it is better using the attribute rather than coding a new test. I'm thinking about the login case; imagine we have the Login(user pass) method which it's a simple method but we want to test it against different users. So instead of writing a loop inside the test through all the users, I like the idea of leaving the responsibility to the attribute.Anyway, I'm still thinking on this as I'm trying to make my unit tests as more readable as possible.

About the Author

I am a Software Engineer and I consider myself a problem solver with the ability of getting things to work. Having a keen eye on quality, architecture and risks this lets me build good software. I am mainly interested in AI, compilers, grammars, graphics and more mathematical stuff. If you like this page you could also visit me on twitter @thunderjordi and on Facebook.