Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Naming test classes with BDD-style "When" and its maintenance consequences

As per my understanding, true BDD is more about meeting business specs but I thought starting with the behavior-at-class-level could be worthwhile as I was struggling to adopt TDD and BDD naming made it easier.

Let us focus on BDD-style unit tests (at class level).

I started naming my test classes in a way that reflects expected behavior using "When" as suggested in this article. It's really effective. However very shortly, I realized that such test class names might have following maintenance consequence.

It's not easy to find test-class(es) for a class at hand.

I'm used to the traditional naming "ClassnameTest" in which case I could find the test-class in a snap.

I use Java and JUnit4 and unfortunately I don't have built-in language constructs like given, etc.

Let's consider the following example.

public class Lion{...}

Test classes

public class WhenLionIsHungy{
    @Test public void should_eat_meat(){...}
    @Test public void should_not_eat_grass(){...}
    @Test ...
}

public class WhenLionHunts{...}

public class WhenLionIsWithCubs{...}
...

Please forgive me for the example. Assume that they all are valid test classes. Let's not worry about it unless you are sure that we should never have multiple behavior-based test-classes for a target class.

BDD is an elegant solution to turn the tests into documentation and above example reflects that. Testing a method like "testMoneyTransfer()" is never going to provide any insights after all.

The above is a happy example but it'd be a nightmare to guess the test-class for a class named "LibraryServiceImpl".

Is it WhenBookIsRequested or WhenLibraryIsClosed or both or something else??

Unit tests should serve as the documentation to full or some extent. However in the LibraryServiceImpl-case, the documentation itself is not easily discoverable.

Even if you name a class correctly and design it with SRP, it's still harder to find out the test-class as searching for references would possibly return 100s of hits in test folder too.


My first attempt to solve the problem using @Suite.

@RunWith(Suite.class)
@Suite.SuiteClasses({WhenLionIsHungy.class,
                     WhenLionHunts.class,
                     WhenLionIsWithCubs.class
})
public class LionTest{}

What does it solve?

  1. Easy to find test-class(es) for a class at hand as I named it after target classname.
  2. All types of high-level behavior of a single class at one place. Personally, I find that more insightful.

What does it introduce?

  1. Empty classes with no purpose but to group "When" classes. I'm not completely sure whether that's a bigger problem but it certainly feels weird to have them.

My second attempt to solve the problem using inner classes. Reference

@RunWith(Enclosed.class)
public class LionTest{

     public static class WhenLionIsHungy{...}

     public static class WhenLionHunts{...}

     ...

}

What does it solve?

  1. Easy to find test-class(es) for a class at hand as I named it after target classname.
  2. No more empty classes.
  3. Somewhat easy to figure out the gist of the class. (IDEs can provide the outline)

What does it introduce?

  1. Ridiculously lengthy classes. However, there's no way to have all of them at one place yet be compact & equally readable.
  2. Using static inner test classes? Does it sound right?

I hope I don't have to completely go back to naming test classes after target classes but "Easy to find test-class(es) for a class at hand." seems like a major problem that can't be overlooked.

Please provide your comments on above solutions individually.

It'd be great if you could come up with something else.

like image 415
phanin Avatar asked Jan 28 '14 07:01

phanin


1 Answers

"I use Java and JUnit4 and unfortunately I don't have built-in language constructs like given"

If you are on a JVM you can use groovy/spock, its really easy to integrate spock in java projects and IMMO the difference worth the effort. Give it a try!.

I don't think that finding the test classes for a concrete production class is something that you really need. One of the things with BDD its that you organize the test with the features you are developing in mind, the class that implements this functionality its a implementation detail. Normally when you are looking for some test, your are really looking because you want to verify/check/fix some functionality in your application.

In fact the problem its not find the test for LibraryServiceImpl, the problem here is having a class with this bad name. "Library" its the only information in this name "ServiceImpl" is noise. At this point the question can change to "find the test for the library functionality", a little better, but if you have a lot of library related functionality in the application probably this class can be separated in classes with meaningful names like LibraryBooksReturnPolicy or LibraryNewBooksRegister (only samples, probably bad samples, but you can get the idea).

BDD/TDD its not only a testing technique, its a technique for driven your design, finding appropriate names for you classes if one of the most important things in a good design, you cannot make a lot of effort for finding semantic and great ways to name your test code (its a good effort anyway) and put names with words like "service", "manager" or "impl" on your production code!.

Anyway, if you not agree with all of this and still want to find the test for a given class, you can use your IDE and find the class usages in the test folders and you have a list of the test that exercises this class milliseconds and with navigable links.

like image 101
AlfredoCasado Avatar answered Oct 04 '22 01:10

AlfredoCasado