Problems with directly calling the system code from the tests include
The system must be in the same language.
It requires exact knowledge of the system code.
The tests bypass code and can miss defects.
To address these problems:
Pick the right language for the tests, or use something like JNI.
Help programmers write the system code, have programmers write the application-test class, or both.
If the risk is high enough, make the tests run through an interface that covers more of the code.
The tests can be run through a different interface without requiring any changes:
The changes take place in the application test class and below.
The farther out toward the interface you get, the more difficult these become, and the more useful a test tool is.
To make the executable tests run through a higher-level interface using a test tool:
Create an application test-interface class.
Move the direct-call methods from the application test class into a direct-interface subclass of the application test-interface class.
Refactor the application test class to instantiate an object of the appropriate interface type and call the methods on that object instead of implementing them directly.
Create a tool-specific interface subclass of the application test-interface class and implement the methods that go through the tool.
It's better to design the system up front to make direct-call testing less risky, if you can do so.
Try the following exercise, and these ideas will become clearer.