In short, "functional core, imperative shell" can be summarized as:
functional core implements logic; you unit test it. Your tests call real functions just like in production real functions are called; ie compared to mocking / faking, you don't run a risk that mocks / fakes mildly mismatch behavior of the real dependencies
imperative shell basically just handles dependencies. It extracts values from them / returns results to them, but has no logic -- everything is delegated by calling functional core. You don't unit test it, because there is no logic to test
So translating this to java (for pseudo-code purposes only, my question isn't java specific), my classes have been looking something like
public class Foo {
...
public int doBar() {
return doBarImpl(fetchValueFromDB());
}
static int doBarImpl(int value) {
return value + 1;
}
}
The referentialy transparent / functional core methods are package private and inaccessibly from public interface, but I can still test them by declaring my unit test classes to have same package. This does run against the whole "don't test your private methods" idea. Declaring them public, either in same class or new one, would misrepresent it, because it is ultimately an implementation detail, and I don't want other classes to rely on its existence (naturally this is when functionality isn't generic enough; if it's generic and reusable, then there is no issues / questions to make it public). Keeping it private but testing through public methods throws away the whole benefit I initially mentioned, in that you're not testing method calls directly anymore but start using mocks and fakes and again step into the risk of them not matching the real thing in production. Is my understanding correct, that it is explicitly expected to test private methods in this "functional core, imperative shell" design and ignore the rule of thumb about not testing private methods