18

We're actually naming our test packages just like their to-test counterparts. So we end up with this structure:

src/main/java
    com.hello.world
        helloWorld.java
src/test/java
    com.hello.world
        helloWorldTest.java

I always felt like this is not pretty smart since you can't distinguish between "test" and "to-test" if only provided with the package name. On the other hand I haven't really found a case where this matters somehow. Is it a good practice to have the same naming conventions for both of the packages (for the test cases and the source classes)? If not, what would be a better approach?

OddDev
  • 409
  • 1
  • 4
  • 12
  • 1
    No idea if it's good practice, but it's a popular one. The other option (to put "Test" into the package name, method names etc) smacks a bit too much of "Smurf naming". As you say, it's hard to think of case where being unable to "distinguish between "test" and "to-test" if only provided with the package name" would be a problem. – David Arno Aug 19 '16 at 10:15
  • @DavidArno Thanks for your input :) How to avoid smurf-naming then? I mean we would end up with com.hello.world.test.helloWorld.java, wouldn't we? – OddDev Aug 19 '16 at 11:24
  • The "smurf" problem is more when you have a method `XXXTest()` in `com.hello.world.test.helloWorldTest.java`. General advise would be to only have "Test" appear once in the path, so either (a) use test in the package name (and name the test file the same as the file under test) or (b) make the package name the same and add "test" to the file/class name. – David Arno Aug 19 '16 at 11:27
  • @DavidArno Ah, thanks for clarification! I got your first comment wrong. I got it now. – OddDev Aug 19 '16 at 11:28
  • Well I'd argue that, if it was unclear, *I* got my first comment wrong :) – David Arno Aug 19 '16 at 11:31
  • Related: https://stackoverflow.com/q/2388253/435605 - Separation of JUnit classes into special test package? – AlikElzin-kilaka Nov 05 '19 at 09:11

1 Answers1

17

That is a good convention.

Sometimes you want to write unit tests for package-private classes and methods also. You won't be able to call them from a unit test class placed in another package.

There shouldn't be any confusion about having unit test classes in the same namespace as they shouldn't be in the class path when compiling or running the production code.

Here's an example of a small module with a public interface, a public factory class and two package-private implementation classes:

src/main/java:
    com.hello.transmogrifier
        public interface Transmogrifier
        public class TransmogrifierFactory
        class MapTransmogrifier implements Transmogrifier
        class ListTransmogrifier implements Transmogrifier

scr/test/java:
    com.hello.transmogrifier
        public class TransmogrifierFactoryTest
        public class MapTransmogrifierTest
        public class ListTransmogrifierTest

Hiding the implementations of the Transmogrifier interface could be a valid design choice. Perhaps it's the responsibility of the factory class to choose the implementation.

Since the implementations are package-private, you need to place the unit test classes in the same package if you wish to test them directly. If you have your unit test classes in some other package, you only have direct access to the public interface and the factory class from your tests.

COME FROM
  • 2,666
  • 15
  • 15
  • 2
    "Usually you want to write unit tests for package-private classes and methods also". No! This is really bad practice. Package private types are implementation details and should never be tested directly. Only test public APIs. – David Arno Aug 19 '16 at 13:18
  • 1
    @DavidArno I disagree. However, I replaced the word "usually" with the word "sometimes" to avoid that particular discussion. – COME FROM Aug 19 '16 at 13:20
  • 2
    You may disagree all you want, but testing the inner workings of a piece of code leads both to tight coupling between those inner workings and the tests and to brittle tests that readily break during even simple refactoring. It is very bad practice. – David Arno Aug 19 '16 at 13:35
  • If I want to make sure all my Transmogrifier implementations work, no matter what the factory does, then I'm going to write unit tests that test each implementation. Note that the implementations have a shared public API even though the classes are package-private. These test should not break unless I change the public API. Actually, I'd probably write a generic test for a Transmogrifier and then run it against each implementation. While it's possible to get each implementation using the factory, it's better to not have that dependency when testing Transmogrifiers. – COME FROM Aug 19 '16 at 13:45
  • Then one day, you look at `MapTransmogrifier` and `ListTransmogrifier` and decide they could be made into one class, so you create `ListMapTransmogrifier`, modify the factory to use that and delete the two classes. The code now doesn't compile, so you have to modify every test in both `MapTransmogrifierTest` and `ListTransmogrifierTest` to get it compiling. A test fails. Was that due to changing the tests or creating `ListMapTransmogrifier`? Out comes the debugger to find out ... alternatively when the tests use the factory, you do that refactor and all still compiles... – David Arno Aug 19 '16 at 14:11
  • Unit tests should always just use the public API's, which in this case is the factory. Doing it otherwise is a typical antipattern as you are making more work for yourself whilst believing you are making life easier. – David Arno Aug 19 '16 at 14:13
  • @DavidArno The Transmogrifier interface is also public. I want to make sure all implementations of that interface work correctly, just like the contract of that interfaces dictates. Having those tests depend on the factory class would actually add brittleness. Look, I'm familiar with that particular rule of thumb you're advocating. While I generally accept the reasons behind it, I don't think it applies to all cases. That's the problem with most rule of thumbs. – COME FROM Aug 19 '16 at 14:24
  • "I want to make sure all implementations of that interface work correctly". No! You should only be testing those implementations that can be exposed via the public API (the factory). Any that aren't should be deleted, not tested. Testing them directly makes life harder for tools that look for unused code, as it is used, by the tests! – David Arno Aug 19 '16 at 14:28
  • @DavidArno You seem to have a lot of ideas what I should or shouldn't do. That's nice. I suggest writing another answer. – COME FROM Aug 19 '16 at 14:58
  • @DavidArno It seems you think I'm suggesting writing tests against some package-private interfaces. I'm not suggesting that. You could write the test code for all Transmogrifiers only once, against the public interface, and then just re-use the same test code by feeding it different implementations without using the factory. There are many ways to do that. Also, it's nice to be able to write and test a Transmogrifier before adding it to the factory. – COME FROM Aug 19 '16 at 15:09
  • "Also, it's nice to be able to write and test a Transmogrifier before adding it to the factory" Have a read up on [TDD as if you meant it](http://coderetreat.org/facilitating/activities/tdd-as-if-you-meant-it). It's perfect for such a scenario. You write the Transmogrifier within the test code, slowly creating an inner class as you add tests. Then, when finished, you move it to `com.hello.transmogrifier`, modify the factory to use it and update the tests to use the factory. – David Arno Aug 19 '16 at 15:42