Annotations and inheritence

5 views
Skip to first unread message

steve.ebersole

unread,
Aug 3, 2007, 5:20:38 PM8/3/07
to testng-dev
I tried a small migration of moving our tests over from JUnit3 to
TestNG. JUnit forced us to do some kooky things which we probably
just do not need to do with TestNG.

But this is a simple PoC, and so I tried to mitigate effort as much as
possible.

So I tried setting up a super class for the test classes themselves
where the super class would build the SessionFactory based on override-
able methods (usually the only thing that varies between tests is the
mapping files used). This super class would build the SessionFactory
in a method annotated with @BeforeClass and close it in a method
annotated with @AfterClass.

What I seemed to find, though, was that TestNG did not recognize when
annotations occurred on superclass. Is that by design? Or a bug? Or
am I just doing something wrong?

Alexandru Popescu ☀

unread,
Aug 3, 2007, 5:23:41 PM8/3/07
to testn...@googlegroups.com

If I'm reading it correctly this should definitely work. Can you
reduce it to some reproductible test case?

./alex
--
.w( the_mindstorm )p.
TestNG co-founder
EclipseTestNG Creator

>
> >
>

steve.ebersole

unread,
Aug 3, 2007, 5:28:49 PM8/3/07
to testng-dev
Sure. Let me rip out the Hibernate bootstrapping and just use print
statements. It'll be a good exercise for me as well just to step back
and build this up gradually.

steve.ebersole

unread,
Aug 3, 2007, 5:35:42 PM8/3/07
to testng-dev
Ah, it only checks accessible members...

So something like:

@BeforeClass
private void doSomeSetUp() { ... }

will not work.

Alexandru Popescu ☀

unread,
Aug 3, 2007, 5:46:00 PM8/3/07
to testn...@googlegroups.com
On 8/4/07, steve.ebersole <steven....@gmail.com> wrote:
>

That's it!

steve.ebersole

unread,
Aug 3, 2007, 5:53:07 PM8/3/07
to testng-dev
Any plans to work this on private members?

C?dric Beust ?

unread,
Aug 3, 2007, 5:56:34 PM8/3/07
to testn...@googlegroups.com
On 8/3/07, steve.ebersole <steven....@gmail.com> wrote:

Any plans to work this on private members?

We have to exclude them for class-level annotations.

When you are putting @Test on a class, all the public methods in the class automatically become tests, and excluding private methods lets developers still exclude some methods on this class that are not really tests.

--
Cédric

steve.ebersole

unread,
Aug 3, 2007, 5:59:32 PM8/3/07
to testng-dev
I am talking specifically about private methods annotated with:
1) @BeforeTest
2) @AfterTest
3) @BeforeClass
4) @AfterClass
5) @BeforeMethod
6) @AfterMethod

steve.ebersole

unread,
Aug 3, 2007, 6:02:15 PM8/3/07
to testng-dev
Also, I saw that the implementation of the IHookable#run method y'all
pointed me at b4 also gets treated as a test method if the class is
annotated with @Test. Would be nice to add that to your exclusions.

Alexandru Popescu ☀

unread,
Aug 3, 2007, 6:24:55 PM8/3/07
to testn...@googlegroups.com
On 8/4/07, steve.ebersole <steven....@gmail.com> wrote:
>

Do you see any valid reasons for doing this? I think this may lead to
interpretable behavior when used in inheritance contexts.

steve.ebersole

unread,
Aug 3, 2007, 6:35:39 PM8/3/07
to testng-dev
Define valid :) So to me this bootstrapping is an internal impl
detail and should not be accessible to the subclasses. But at the
same time I am much more practical in this respect; I just want it to
work, but was just curious if y'all planned support for this as it
seems desirable.

Sorry, I don't understand what you mean by "interpretable behavior".

Alexandru Popescu ☀

unread,
Aug 3, 2007, 6:49:00 PM8/3/07
to testn...@googlegroups.com
On 8/4/07, steve.ebersole <steven....@gmail.com> wrote:
>

[code]
class Parent {
protected SessionFactory m_sessionFactory;

@BeforeClass
private initSessionFactory() {
m_sessionFactory = ...
}
}

class Child {
@Test
public debatable() {
assert m_sessionFactory == null // my expectation as
initSessionFactory is not accessible from Child instance

assert m_sessionFactory != null // Steve's expecation
}
}
[/code]

bests,

steve.ebersole

unread,
Aug 3, 2007, 9:52:20 PM8/3/07
to testng-dev
In reality, the situation I had is much more like:

[code]
class Parent {
private SessionFactory sessionFactory;
@BeforeClass
private initSessionFactory() {
sessionFactory = ...;
}
protected SessionFactory getSessionFactory() {
return sessionFactory;
}
}
class Child extends Parent {
@Test
public void sorryButThisIsNotReallyDebatableToMe() {
// To me, it is *totally* reasonable to expect that
getSessionFactory() returns non-null here...
assert getSessionFactory() != null;
}
}
[/code]

steve.ebersole

unread,
Aug 3, 2007, 9:57:36 PM8/3/07
to testng-dev
And just to illustrate my point about IHookable:

[code]
@Test
class SomeTest implements IHookable {
/** A real test. Gets picked up correctly, no problem... */
public void testIt() { ... }

/**
* Impl of IHookable#run. As impl of interface, this needs to be
public scope
* and thus TestNG tries to run this as a test here, and of course
fails because
* of the parameters...
*/
public void run(IHookCallBack callback, ITestResult result) {
callback.runTestMethod( result );
}
}
[/code]

Alexandru Popescu ☀

unread,
Aug 4, 2007, 3:33:49 AM8/4/07
to testn...@googlegroups.com
On 8/4/07, steve.ebersole <steven....@gmail.com> wrote:
>

You see, we already have a debate :-). I don't see any reasons for
which a parent private method should be visible to its children (or at
least no valid reasons so far).

Alexandru Popescu ☀

unread,
Aug 4, 2007, 3:34:36 AM8/4/07
to testn...@googlegroups.com
On 8/4/07, steve.ebersole <steven....@gmail.com> wrote:
>

I think we can do better in this case and filter out that method.

steve.ebersole

unread,
Aug 4, 2007, 9:37:40 AM8/4/07
to testng-dev
> You see, we already have a debate :-). I don't see any reasons for
> which a parent private method should be visible to its children (or at
> least no valid reasons so far).

getSessionFactory() is in fact protected for the explicit purpose of
exposing to "children" (aka subclasses).

Therefore I have to assume you mean the initSessionFactory() method.
In which case, you and I have very different definitions of
"visible". The Child class cannot "see" it, cannot call it. I
suppose you might mean in terms of the execution? Um, how is
@BeforeClass any different than other initialization code (like say a
constructor) that could in fact run when the subclass in
instantiated? And besides, in this argument, how is making it
protected now "good"? All that does is expose internal workings of
the superclass which the subclass can now call and possibly screw
things up. From a theoretical PoV, your thinking here is completely
incorrect IMHO. Not picking up that initSessionFactory() call breaks
the superclass/subclass contract. Sorry, I still don't see a debate
here; I think my view is unequivocally correct ;)

Anyway, like I said I don't really care: its a test-suite. I can
largely control how people (read me and the rest of the hibernate dev
team) access these classes. This is now a theoretical discussion to
me. The practicality is that making it protected makes it "work".


steve.ebersole

unread,
Aug 4, 2007, 9:44:40 AM8/4/07
to testng-dev
In fact, as an example...

@BeforeClass really could be achieved via constructor (of course I'd
still have problems with the other "scaffolding" annotations like
@AfterClass or @BeforeMethod):

[code]
class Parent {
private SessionFactory sessionFactory;

protected Parent() {
initSessionFactory();


}
private initSessionFactory() {
sessionFactory = ...;
}
protected SessionFactory getSessionFactory() {
return sessionFactory;
}
}

class Child extends Parent {
@Test
public void sorryButThisIsNotReallyDebatableToMe() {
// To me, it is *totally* reasonable to expect that
getSessionFactory() returns non-null here...
assert getSessionFactory() != null;
}
}
[/code]

So obviously this works. According to your definitions (I think) here
we have a private method on the superclass "visible" to the subclass.
At least it behaves in exactly the same way I was expecting
@BeforeClass directly on the private initSessionFactory() to work.
So perhaps you could explain where the difference lies in your mind?

steve.ebersole

unread,
Aug 4, 2007, 9:47:24 AM8/4/07
to testng-dev
Or here is perhaps a better example since it even avoids the protected
ctor:

[code]
class Parent {
private SessionFactory sessionFactory = initSessionFactory();

Alexandru Popescu ☀

unread,
Aug 4, 2007, 10:21:33 AM8/4/07
to testn...@googlegroups.com
I think we can continue this discussion for a long time.

In the last examples you've sent you are following the Java visibility rules:

Case 1: you have a protected ctor which is visible to the subclass and
from there you invoke the private method => oke with Java

Case 2: you have the default public constructors and the
initialization block => oke with Java

while your initial example is just about making a parent private
method visible to a child class.

Now, considering that this discussion is just meant to reach a working
solution, I am still wondering why you really need that behavior.

bests,

./alex
--
.w( the_mindstorm )p.
TestNG co-founder
EclipseTestNG Creator

On 8/4/07, steve.ebersole <steven....@gmail.com> wrote:
>

steve.ebersole

unread,
Aug 4, 2007, 11:20:46 AM8/4/07
to testng-dev
To my mind both of those examples are *exactly* the same as what I
expected with TestNG annotations.

"Really need" what behavior? The Hibernate test suite has many, many,
many test classes. Almost all of them need to build a SessionFactory,
with really pretty minimal differences between the classes (usually
the mappings to use is the only variance). Again, these previous
examples are all based off of your original trivial example meant to
illustrate you PoV. The Hibernate "base test" is really more like:

class abstract FunctionalTest implements IHookable {
private Configuration configuration;
private SessionFactory sessionFactory;
/**
* This is one of a few methods test classes can use to
* control configuration of the SessionFactory.
*/
protected abstract String[] getMappings();
...
protected Configuration getConfiguration() {
return configuration;


}
protected SessionFactory getSessionFactory() {
return sessionFactory;
}

@BeforeClass
private void initializeEnvironment() {
configuration = new Configuration();
for ( int i = 0, x = getMappings().length; i < x; i++ ) {
configuration.addResource( getMapping()[i] );
}
sessionFactory.buildSessionFactory();
}
@AfterClass
private void releaseEnvironment() {
if ( sessionFactory != null ) {
sessionFactory.close();
}
}
...
}

Again, to me these methods are simply part of the implementation
details of the superclass contract. Exposing them to subclasses by
making them visible serves no purpose except to confuse subclass
writers (do I need to manually initialize the environment myself? why
else would this be exposed as protected? ...).

Reply all
Reply to author
Forward
0 new messages