In the C world I sometimes do something similar: the C file with the test code simply #include's the actual source file and gets compiled into one executable (together with some mocks).
Not too dirty (ahem...) and allows you to test various internal parts.
And I must admit I got the idea after looking into Rust :)
In C I just write the tests in the same source files as the things they're testing. Some interface tests get their own source file.
All the tests end up in the "release" binary, along with the application code. Separate test_main entry point to run the tests instead of the application. If I don't want the tests available, marking test_main static and deadstripping rips them all out.
Not too dirty (ahem...) and allows you to test various internal parts. And I must admit I got the idea after looking into Rust :)