The solution is "stupidly simple". I have at my module level a public subroutine with the name "test_module" which performs the tests of all the private subroutines of the module I want to test (these are usually analytical derivative calculations and stuff like that).
Yes, I have test code in my module, but in practice, after 10+ years of using this pattern, nobody had issues with it.