I just discovered GuiceBerry, and I think it's great. The only thing
that really bothered me right from the beginning was the class name
string in the GuiceBerryEnv annotation. I understand the reasoning in
the FAQ, but I'm still not very happy with the current solution:
- Refactoring will break my code
- While we don't have a compile-time dependency, the test class still
has to know about the class name. This basically only helps to
"silence the compiler", but leaves us with the logical dependency.
I see that this situation can be relieved by using a
GuiceBerryEnvRemapper, and this led me think up a more general
solution that would completely eliminate the runtime dependency, and
also give us back the refactoring friendliness. Now I must admit, that
I'm not familiar with GuiceBerry's implementation, and I have no idea,
if this would really work or what kind of problems it might create.
It's just a rough idea:
---------
The Test class could look like this:
@GuiceBerryEnv
public class MyTest extends GuiceBerryJunit3TestCase {
[...]
}
Notice that the annotation doesn't have any string value. Maybe we
should use a different annotation (or not annotation at all).
---------
Then we'd have something like a GuiceBerryEnvMapper (similar to
GuiceBerryEnvRemapper):
public interface GuiceBerryEnvMapper {
String GUICE_BERRY_ENV_MAPPER_PROPERTY_NAME =
"GuiceBerryEnvMapper";
Class<? extends Module> map(TestCase testCase);
}
... with an implementation like
Class<? extends Module> map(TestCase testCase) {
if (testCase instanceof Test1)
return MyEnv1.class;
else
return MyEnv2.class;
}
(I'm not a big fan of "instanceof", but this was the quickest solution
I could think of.) Notice that the method returns a Class, instead of
a String (compared to what the GuiceBerryEnvRemapper does).
The rest would be like using the GuiceBerryEnvRemapper: Set a property
at startup - e. g. as a command line argument, or in a TestSuite.
This would move the Env dependency away from the test class, and into
the GuiceBerryEnvMapper. Renaming (through refactoring) Test1 or
MyEnv1 would also rename the class references in the Mapper.
What do you think?
Chris
Z
On Fri, Jan 1, 2010 at 7:19 AM, Christian Lercher
<cl_for_...@gmx.net> wrote:
> Hi,
>
> your reply wasn't sent to the mailing list, only as a private mail to me. If you want to post it there, I'd prefer to reply on the list :-)
>
> Happy new year
> Chris
>
>
> On 31.12.09, at 21:15, zorz...@gmail.com wrote:
>
>> Sorry for the delay in replying. I blame the holidays...
>>
>> Let's go by parts. The functionality you ask for is already available,
>> with a slightly different syntax. But first, the caveat: the (main)
>> reason why a GBE takes a class (rather than be parameterless, as you
>> propose) is so that one can easily run a single test/test case (i.e.
>> without the need to set a remapper -D property).
>>
>> So, to accomplish what you say you want, try this:
>>
>> 1) Pass an empty string to GBE:
>>
>> @GuiceBerryEnv("")
>> public class MyTest extends GuiceBerryJunit3TestCase {
>> [...]
>> }
>>
>> 2) Write your GBE remapper that ignores the passed string:
>>
>> String map(TestCase testCase, String guiceBerryEnvName) {
>> if (testCase instanceof Test1)
>> return MyEnv1.class.getName();
>> else
>> return MyEnv2.class.getName();
>> }
>>
>> Note that, despite returning a String, your remapper will survive
>> refactorings, since it does not hardcode it.
>>
>> BTW, I must say that the remapper will more probably look like this
>> (see Example5Remapper from the tutorial dir):
>>
>> String map(TestCase testCase, String guiceBerryEnvName) {
>> return MyEnv1.class.getName();
>> }
>>
>> Now to address the fine print:
>>
>>> - Refactoring will break my code
>>
>> True. But the canonical way to do that, and all-but-eliminate problems
>> with refactorings is to declare a constant, as you can see throughout
>> the tutorial (say Example0HelloWorldTest).
>>
>>> - While we don't have a compile-time dependency, the test class still
>>> has to know about the class name.
>>
>> Indirectly, yes, if you want to be able to run a single test without
>> having to pass a -D property.
>>
>>> This basically only helps to
>>> "silence the compiler", but leaves us with the logical dependency.
>>
>> It's a runtime dependency, and the same thing (only better) you'd have
>> by, say, declaring a class name in an ant's build.xml file or
>> something like that.
>>
>> Peace,
>>
>> Z
you're right, it's possible to do this with the Remapper, if we use an
empty string in the annotation. Probably that solution is good enough
and there's not enough reason to implement something different.
It may give me a little bit of a hard time when trying to convince
someone to use guiceberry, and the first thing they will have to write
is an annotation with an empty string value that has no effect...
The constants used in the tutorials are clearly another solution, but
they can't be generated automatically from the class name (if you try
to use
String PET_STORE_ENV_0_SIMPLE = PACKAGE +
PetStoreEnv0Simple.class.getName();
you would get this error:
"The value for annotation attribute GuiceBerryEnv.value must be a
constant expression").
So, I'll go for your empty string solution - it feels a little bit
like a workaround, but I don't see any practical problems with it :-)
Thanks
Chris