Blog-Archiv

Freitag, 15. September 2023

JUnit 5 Before and After Annotations Execution Order

JUnit has been a Java testing framework now for 25 years. In continuation of my year 2020 article about JUnit 4 execution order I will describe here how junit-jupiter-api 5.4.2 behaves, together with junit-platform-commons 1.4.2. It is about @BeforeEach and @AfterEach annotations, no others.

Questions will be: How is the execution order when ....

Same Annotation on Multiple Methods


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class SameAnnotationOnMultipleMethods
{
    @BeforeEach
    public void bbb() {
        System.err.println("@BeforeEach bbb()");
    }
    @BeforeEach
    public void aaa() {
        System.err.println("@BeforeEach aaa()");
    }
    @BeforeEach
    public void ccc() {
        System.err.println("@BeforeEach ccc()");
    }
    
    @AfterEach
    public void zzz() {
        System.err.println("@AfterEach zzz()");
    }
    @AfterEach
    public void xxx() {
        System.err.println("@AfterEach xxx()");
    }
    @AfterEach
    public void yyy() {
        System.err.println("@AfterEach yyy()");
    }
    
    @Test
    public void test() {
        System.err.println("SameAnnotationOnMultipleMethods: @Test test()");
    }
}

Output is:

@BeforeEach aaa()
@BeforeEach bbb()
@BeforeEach ccc()
SameAnnotationOnMultipleMethods: @Test test()
@AfterEach xxx()
@AfterEach yyy()
@AfterEach zzz()

The methods seem to get executed in alphabetical order, not as written in source. This applies to both @BeforeEach and @AfterEach.

Multiple Same Annotations on Multiple Methods


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class SameMultipleAnnotationsOnMultipleMethods
{
    @BeforeEach
    @AfterEach
    public void bbb() {
        System.err.println("@BeforeEach @AfterEach bbb()");
    }
    @BeforeEach
    @AfterEach
    public void aaa() {
        System.err.println("@BeforeEach @AfterEach aaa()");
    }
    @BeforeEach
    @AfterEach
    public void ccc() {
        System.err.println("@BeforeEach @AfterEach ccc()");
    }

    @Test
    public void test() {
        System.err.println("SameMultipleAnnotationsOnMultipleMethods: @Test test()");
    }
}

Output is:

@BeforeEach @AfterEach aaa()
@BeforeEach @AfterEach bbb()
@BeforeEach @AfterEach ccc()
SameMultipleAnnotationsOnMultipleMethods: @Test test()
@BeforeEach @AfterEach aaa()
@BeforeEach @AfterEach bbb()
@BeforeEach @AfterEach ccc()

Again the methods seem to get executed in alphabetical order.

An interesting variant of this ambiguity is when @BeforeEach and @AfterEach collide with a single @BeforeEach or @AfterEach:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class SameMultipleAnnotationsOnMultipleMethods
{
    @BeforeEach
    public void bbb() {
        System.err.println("@BeforeEach bbb()");
    }
    @BeforeEach
    @AfterEach
    public void aaa() {
        System.err.println("@BeforeEach @AfterEach aaa()");
    }
    @AfterEach
    public void ccc() {
        System.err.println("@AfterEach ccc()");
    }

    @Test
    public void test() {
        System.err.println("SameMultipleAnnotationsOnMultipleMethods: @Test test()");
    }
}

Output is:

@BeforeEach @AfterEach aaa()
@BeforeEach bbb()
SameMultipleAnnotationsOnMultipleMethods: @Test test()
@BeforeEach @AfterEach aaa()
@AfterEach ccc()

Again the execution order is alphabetical by method-name. The order is not connected to the fact that aaa() carries multiple annotations, as I found out by changing the method names.

Annotations in Inheritance

Super-class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
abstract class AbstractBeforeAndAfter
{
    @BeforeEach
    public void setUp() {
        System.err.println("AbstractBeforeAndAfter: @BeforeEach setUp()");
    }

    @BeforeEach
    @AfterEach
    public void bothInSuperclass() {
        System.err.println("AbstractBeforeAndAfter: @BeforeEach @AfterEach bothInSuperclass()");
    }

    @AfterEach
    public void tearDown() {
        System.err.println("AbstractBeforeAndAfter: @AfterEach tearDown()");
    }
}

Derived class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class AnnotationsInInheritance extends AbstractBeforeAndAfter
{
    @BeforeEach
    @Override
    public void setUp() {
        System.err.println("AnnotationsInInheritance: @BeforeEach @Override setUp()");
    }

    @BeforeEach
    @AfterEach
    public void bothInDerivedClass() {
        System.err.println("AnnotationsInInheritance: @BeforeEach @AfterEach bothInDerivedClass()");
    }

    @AfterEach
    @Override
    public void tearDown() {
        System.err.println("AnnotationsInInheritance: @AfterEach @Override tearDown()");
    }

    
    @Test
    public void test() {
        System.err.println("AnnotationsInInheritance: @Test test()");
    }
}

Mind that bothInDerivedClass() is not an override of bothInSuperClass()!

Output is:

AbstractBeforeAndAfter: @BeforeEach @AfterEach bothInSuperclass()
AnnotationsInInheritance: @BeforeEach @AfterEach bothInDerivedClass()
AnnotationsInInheritance: @BeforeEach @Override setUp()
AnnotationsInInheritance: @Test test()
AnnotationsInInheritance: @AfterEach @Override tearDown()
AnnotationsInInheritance: @BeforeEach @AfterEach bothInDerivedClass()
AbstractBeforeAndAfter: @BeforeEach @AfterEach bothInSuperclass()

Super-class @BeforeEach annotations come before derived class. Super-class @AfterEach annotations come after derived class.

Mind that "@AfterEach @Override tearDown()" here comes before "@BeforeEach @AfterEach bothInDerivedClass()". This contradicts the previous assumption that methods are executed in alphabetical order. The tearDown() method is executed first because it is an @Override of a method in super-class.

Missing Annotation in Override

This builds on the same super-class as the example above.
Assumption is that @AfterEach tearDown() in super-class was overridden, but the annotation was forgotten:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class MissingAnnotationInOverride extends AbstractBeforeAndAfter
{
    @Override
    // Mind that @AfterEach is missing here!
    public void tearDown() {
        System.err.println("MissingAnnotationInOverride: @Override tearDown()");
    }

    
    @Test
    public void test() {
        System.err.println("MissingAnnotationInOverride: @Test test()");
    }
}

Output is:

AbstractBeforeAndAfter: @BeforeEach @AfterEach bothInSuperclass()
AbstractBeforeAndAfter: @BeforeEach setUp()
MissingAnnotationInOverride: @Test test()
AbstractBeforeAndAfter: @BeforeEach @AfterEach bothInSuperclass()

The tearDown() was not executed.
That means when you override an annotated method and do not call it explicitly, the annotation in super-class will be ignored and the method will not be executed at all. In other words, these JUnit 5 annotations are not inherited from a super-class, they have to be repeated.

Mind that such does not apply to any annotation-framework, it depends on the framework's implementation. Although the annotation definition syntax has become part of the Java programming language, the semantic of an annotation is up to its implementation, and that is NOT part of the Java runtime library.

Conclusion

Adding the annotations feature to the Java programming-language opened the door for complexity that has nothing to do with object-oriented thinking. We need to learn and try out the semantics of each and every annotation we use.

For the discussed JUnit5 annotations, we need to remember:

  • Super-class annotations come first on "BeforeEach", come last on "AfterEach"
  • Alphabetical order of execution is not something you should build upon, this would just lead to confusion
  • The discussed JUnit-5 annotations will not be inherited from an overridden method, they must be repeated in the override

Hope this was helpful for some test developers!




Donnerstag, 14. September 2023

The Most Destructive Word in Software Production

Words are the essential substance from which software is built. But when we read a piece of source written in some of the programming languages, do we see words? In my daily practice of maintaining software I see ambiguous fragments and abbreviations:

  • abs (absolute, abdomens, anti-lock brake system, ...)
  • bg (background, bag, bad guy, ...)
  • cb (callback, cable broadband, ...)
  • db (database, decibel, ...)
  • ext (external, extension, extra, ...)
  • ...

It looks like software developers do not have the ability to write whole words. That is because the most destructive word in software production takes its toll, even after 4 decades. So, what is it? It is not "innovation", not "disruption", not "magic, it is

"Code"

I refer to "source code". Why was it named "code"? Code is meant to be something secret, a key to some safe, a military password. (Also "source" does not sound very descriptive to me, but at least it is not that destructive.)

So why do I call the word "code" destructive in relation with software production?

Any individual that somehow comes into contact with computer applications immediately is confronted with the word "code". Its meaning of secrecy becomes aware and leads to a wrong believe: that the source of software needs to be something secret. You need to decode when reading it. You need to encode when writing it. "Of course this is unreadable, I am a coder". Somehow this is similar to "official secrecy" (still used in many inauthentic democracies).

"Coded" code is much harder to read and understand than "normal language" code (using long names). It costs time, and the risk of mistakes is high. Remember that software maintenance makes up 70% of production efforts. That is why I call it destructive. Because commercial software projects consist of millions of lines of code.

"Why do you demand readability? I am a coder! I am not a journalist whose articles should become popular. I am expected to code knowledge into computers, this is a highly specialized ability, and experts like me don't have the time to think about being understandable. If you need changes, you will have to invest lots of time anyway."

You may have heard things like these. Unfortunately we must take it seriously.

We know that memory and hard disk space were quite restricted 30 years ago. People had to keep their source short to save space. So they used abbreviations in their programs wherever possible. That was the moment when others could not understand it any more, and it was "code". (I am sure there exist lots of explanations.) It looks like "coding" is a tradition that has become meaningless.

Conclusion

These times are over, and we need to step out of our technocratic attitude and think about the value software can generate for a society. Forget the autistic programmer. Become a social source publisher. Dare to be a techno-journalist, because that is what we software developers are. The word "code" leads to the assumption that software is for experts only. Technocracy can not be our future. We all need to write source, and it should be readable for everyone.




Sonntag, 3. September 2023

Some Remarks about LINUX Development Strategies

I just have been reading an article about "Linux Meritocaste" that somehow touched me. I've been a LINUX user for 25 years, but never was involved in LINUX development, although I am software developer. I always wondered how these people work together, and how they are are organized. Nowadays they are a meritocracy, that means, ability and capacity count more than wealth or social class, which I consider to be a good thing. But even the meaning of words seems to change in the course of time ....

Many good contributions to free software projects are rejected simply because they did not come from the right class or because they do not serve to flatter the vanity of their highnesses the programmers.

I think this is generally and everywhere true, not only in software development. This is a human condition. What can we do to fix this? Maybe live meritocracy more consequently and focus on the individual, also in big teams?

About "the failure of Linux":

That is the fault of the misunderstood meritocracy that privileged programmers, despising graphic designers, marketing specialists, professional writers, but above all the common user.

Absolutely true. I can confirm this from 30 years of programming experience in teams. The role of developers is overestimated, and they behave like gold diggers. Really user-friendly systems are rare.

Thanks to that, today the development of Linux is in the hands of corporations that are the ones that determine which projects continue or not.

My conclusion is now: authoritarian decisions and carelessness of developers led to the failure of LINUX as free software. Again a human condition?

Carelessness? Isn't it maybe overzeal? I mean, they are idealists, working for an open-source and zero-cost operating system. Maybe we need more people that channel eagerness into the right direction?

The principles of free software are illusory if there is no funding to support a project.

I am not convinced of that. I think software development should be more common and ordinary, like contributing to an internet forum, like writing a newspaper article. You should not always expect money for publishing something. It will build up your image. The more you publish, the more you will be involved into businesses that can make use of what you produce, and finally you will earn more than when demanding money for every little bit. Depending on a smart contract you still could be member of the open source movement.

When we say somebody must pay for the production of free software, it is clear that corporations willing to invest will take over and promote their interests. They make money by being closed, not open.

Conclusion

Currently, the global trend towards authoritarian strategies is very strong. It would lead us back to old medieval failures, which already happened with the dropping of object-oriented paradigms in favor of copy & paste languages, or dropping the XML-compatibility of HTML. Technology must move forward, not backwards. If meritocracy was not sufficient, well, lets invent something new!