if ( document.comments_form.url ) { document.comments_form.url.value = getCookie("mtcmthome"); }
It's been a few weeks since I announced another killer app, hasn't it?
I have complained about JUnit many times, both in terms of design and implementation. I think my complaints are justified, but if there is something I don't like, it's people who complain but don't do anything to solve the problem they are complaining about.
Therefore, without further ado, I give you... TestNG, the JUnit killer.
For quite a while, I have seen very little reason to write a replacement to JUnit since I didn't think the added value would be enough to convince the vast number of JUnit users to switch. After reading up about NUnit, it occurred to me that using JSR 175 annotations for testing was an interesting evolution, but still not quite the quantum leap I expected (besides, in its effort to mimick JUnit, NUnit repeats a lot of its flaws).
So I went back to the drawing board, in search of one last killer argument to justify taking on this endeavor, and I came up with another ultimate complaint that, strangely, I hadn't voiced about JUnit: the test Suite.
I have always been confused by JUnit's Test Suites. Where should the method belong? In the test class? Separately? Should it be static? And if I want to change the tests run, why do I need to rebuild my code?
With these changes in mind, I came up with the design principles that are the foundation of TestNG. To quote the documentation:
TestNG is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use, such as:
- JSR 175 Annotations.
- Flexible test configuration.
- Default JDK functions for runtime and logging (no dependencies).
- Powerful execution model (no more TestSuite).
I think the combined use of JSR 175 annotations and Test Groups make TestNG a very flexi... (augh, sorry Hani)... a very supple testing framework that should compare decently with JUnit.
Check out the documentation, take it for a run and let me know what you think.
Somebody rightfully pointed out that the page seems to install some nasty spyware if you're not careful. I didn't notice, using Thunderbird, so if you are using Internet Explorer, watch out.
Sorry about that.

You are a
GRAMMAR GOD!
If your mission in life is not already to
preserve the English tongue, it should be.
Congratulations and thank you!
How grammatically sound are you?
brought to you by Quizilla
After using both tools for quite a while, I decided to write down my impressions on their respective strengths and weaknesses. Since the post ended up being longer than I thought, I posted it directly on my Web site.
After making such a comment, and being called on it, I had to step up to the plate.
I have been working with JSR 175 quite a lot these past months and I have identified the following points of interest:
Let me go through this list in order.
Shortcut notation
If the annotation type you are defining only needs one value (or none at all), the syntax can be simplified by using a shortcut notation. You define that your annotation type supports this notation by providing only one annotation called "value()":
Then you can use any of the following constructions:
or more simply:
Note that the following is incorrect:
because value() doesn't have a default value (i.e. it is required). In order to make this syntax valid, you would need to define your annotation type as follows:
Which brings me to...
Defaults
JSR 175 distinguishes a mandatory annotation from an optional one with the "default" keyword. For example:
public @interface Person {
// name is required
String name();
// but the date of birth is optional
String dateOfBirth() default "";
}
With this type, I can now annotate my code like this:
@Person(name = "Cedric")
public class Employee {
}
or
@Person(name = "Cedric", dateOfBirth = "January 2nd, 1969")but not like this:
public class Employee {
}
// error, missing required annotation "name"
@Person(dateOfBirth = "January 2nd, 1969")
public class Employee {
}
It sounds all good and well until you realize that "null" is not a valid defaut value. The problem with that is that tools using metadata often need to know whether the value they are receiving is the default value (meaning: the user didn't specify anything and the tool needs to provide its own) or not (in which case, they need to retain that value).
As you probably all know, none of the java.lang types have an official default value, so you will have to come up with one. It can be pretty easy for strings (how about "Unspecified"?), a bit more dangerous for numerics ("new Integer(57381234123)") but where it gets ugly is for booleans.
The problem with booleans is that they only have two values, true and false, so using one as a default is not enough for the tool to be able to tell whether the value it received is a default or if it was specified by the user.
The trick I have been using for now is creating my own enum "Bool":
enum Bool { UNSPECIFIED, TRUE, FALSE }
There are a few things ugly about this solution:
Unfortunately, it's the only way I have found to circumvent this problem.
Now, all this is not so bad because what is being surfaced to the user is fairly minimal. Basically, except for the fact that they need to know about the "Bool" type and TRUE and FALSE, all the rest is fairly transparent. All this dirty business with singular values representing the defaults is strictly between the annotation author and the code that will consume these annotations.
For example, let's add a boolean field to our Person annotation above:
import Bool;
public @interface Person {
// name is required
String name();
// but the date of birth is optional
String dateOfBirth() default "";
// optional boolean
Bool isRegistered() default Bool.UNSPECIFIED;
}
Granted, it doesn't look that good, but your users only need to write the following:
import Bool;
import Person;
@Person(name = "Cedric", isRegistered = Bool.TRUE)
public class Employee {
}
Note that static imports can make things a little bit more legible:
import static Bool.TRUE;
import Person;
@Person(name = "Cedric", isRegistered = TRUE)
public class Employee {
}
at the risk of possible name conflicts.
Multiple annotations
A common misconception from people who read the JSR 175 specification a little too fast is the idea that it is impossible to have an annotation appear several times.
Strictly speaking, this is true, but it's pretty easy to circumvent: use arrays.
For example, consider the following annotation:
public @interface EnvEntry {
String name();
String value();
}
And we want this annotation to be allowed to appear several times in a class file. A simple way to achieve this is by introducing a holder type:
public @interface EnvEntries {
EnvEntry[] value();
}
Note that the holder uses the shortcut notation, which makes the final result quite readable:
@EnvEntries({
@EnvEntry(name = "height", value = "1m80"),
@EnvEntry(name = "weight", value = "75kg")
})
Conclusion
After describing all these little annoying sides of JSR 175, I don't want to leave readers with the impression that the specification is flawed or unusable. Quite the contrary.
I believe JSR 175 is going to change radically the way we program and I am betting that in a couple of years from now, all the Java source code we read and write on a daily basis will contain annotations.
The syntax will undoubtedly look foreign to you at first sight, but I am confident that it will grow on you as you use it. It sure had this effect on me. Also, wait until we get some decent support for annotations in IDE's (code folding, coloring, etc...) and you won't even notice it's there any more.
Robert posted an interesting write-up comparing XDoclet and SGen. I'd like to correct two errors he made (Robert, I tried to find an email address for you but couldn't...).
Designed to replace XDoclet use within BEAWe don't use XDoclet at all inside BEA. We have our own engine and parser (based on a Java compiler called Javelin), so SGen was certainly not created to replace XDoclet internally.
Finally, I haven't studied XDoclet 2 at all so I can't comment on the similarities that Robert sees between SGen and XDoclet 2. SGen is simply what EJBGen has been using for a long time internally and recent requests from users to make EJBGen extensible prompted me to factor out the annotation part from EJBGen. Thus SGen was born.
Robert, feel free to email me if you have specific questions or suggestions for SGen, I'll gladly help.
Robert Cringely posted yet another baffling article in his pulpit column. While some of his points make sense, most of the article is littered with very questionable observations, such as:
It was only when those companies decided to pay attention to Microsoft and decided to compete with Microsoft that they got in trouble.
Robert is observing the right phenomenon but interpreting it wrongly. What got these companies in trouble is not that they started caring about Microsoft, it's that Microsoft entered their space. And when this happens, ignore Microsoft at your own peril.
Another company that is competing with Microsoft by ignoring them is Google.
Oh Google is certainly not ignoring Microsoft. Microsoft has a search engine of its own, so they are definitely competing. Why does Google have the upper hand right now? Because their product is superior to Microsoft's. It's as simple as that.
The key to beat Microsoft is not by ignoring them but simply by offering better products before they enter your space. And when they do finally enter your space, you need to keep offering better products, because at this point, simple technical excellence will not last in front of billions of dollars and a company that has such a stellar track record at executing.
Ted is campaigning for the "final" keyword:
I'm sorry, but your comment "Run, don't walk, from any code developed by people who think this way [Cedric: people who want everything to be overridable by default]..." is really appropriate for Smalltalk code, but wildly out of place for code developed for the JVM, the CLI, or the C++ programming environments.
I disagree with Ted's firm stance on this issue. While "final" has some very good niche uses, I contend that most of the code should be written as being inheritable by default. As a developer, you just can't guess all the possible ways that future programmers will use your code. Sure, they could misuse it and break it, but that's not your concern. If your API is well documented (contract, side effects, parameters, etc...) and that a good developer then tries to extend it, they will come up with things that will probably amaze you.
That being said, I see "final" as being useful in core classes of the libraries, such as String. For various reasons (security being one of them), such fundamental classes need to be free of tampering and extension. That's perfectly reasonable.
But for any other type of "user code", please write your code assuming that one day, someone will want to override the method you are writing. It will make you see your work in a very different light.
The author of the "Open
letter to McNealy" has some severe misconceptions about the software field:
But software technology -- operating systems, development tools, application servers, systems management tools, and the like -- all come with a major handicap. They become commodities so fast that pure technological excellence has little differentiating power.None of these industries are anywhere near commoditization. If such were the case
Even free IDE's such as Eclipse can't commoditize the IDE space, as JBuilder and IDEA have been showing for several years now.
Software doesn't exist in a vacuum. What drives commoditization is user needs, not producers. And so far, user needs have constantly brought in new requirements on all these software stacks, precisely preventing commoditization from happening.
The author needs to read up on some basic business principles before trying to patronize a CEO...
The Sun-Microsoft settlement seems to mean a lot of different things to a lot of different people, but I realize that my needs are very simple: I want to be able to write Java code for the .Net platform.
I don't mean writing Java on a Windows platform (I do that every day and it's working very well), but being able to access the native Win32/.Net API's from Java. Right now, if I want to write an add-in for Outlook, I need to switch to C#. Not a huge deal in itself, and it's actually a fairly pleasant experience, but I was promised "Java everywhere" and I'm still waiting.
Hopefully, the Sun-Microsoft settlement will make this possible.
On a related note, it's interesting to see how things change. A few years ago, writing native code for Windows meant: switching back to C++ (bad) and Visual Studio (good). Now, it means switching to C# (good) and Visual Studio (bad).
Don't get me wrong: Visual Studio remains an outstanding IDE and if anything, it is more powerful now than it was five years ago. It's just that today, modern Java IDE's blow it out of the water...
Hopefully, in a couple of years from now, deciding to write native code for Windows (or for any platform for that matter) will mean switching to both languages and tools that are equally pleasant to use as those I am using today.
Carlos and I have been having way too much fun with this chick. Give it a shot, it's addictive.
In some way, it reminds me of Little Computer People, one of the first implementations of a Tamagotchi. That was back in 1985...
Pretty much all the spams that I receive contain a link to a Web site where you are supposed to go for more information. I noticed that very often, these Web sites have been registered recently (say, in the past two months to be conservative, but it's more like a couple of weeks). The reason for this fast turnover is that spam domain names get taken down typically in the weeks that follow the spam itself.
I was wondering if this couldn't be used by filters (either Bayesian or simple rule filters such as SpamAssassin) to increase the spam probability of a suspicious email.
Whenever a domain name included in an email has been registered in the past two months, mark it as spam.
Could it be so simple?
stupid newbie bloggers cant protect their stuff worth shit hehehe. it took me 15mn to carck this looser's weblog and i did it just becoz im bored stiff in school. at least i get to check out cynthia whos sitting right across me, shes the preetiest girl in our class. shes paying attention to the teach, i cant believe shes not bored either
cant wait for this class to end i need to practice some new stuff on my skate i know im gonna nail the kickturn this time. who needs these stupid classes anyway? when i graduate, ill become a skateboard pro or ill just make money hacking computers like i did this luser. oh cynthia just turned around and smiled to me. shes so cute i cant believe it. i have to ask her out but what if she doesnt like me!!??
alright this looser is lucky im a white hat hacker and im not gonna delete any of his files but i hope he ll learn his lesson nex time,,,
i am so good heehee