You aren't required to test at the finest possible grain.
Many current discussions of unit-testing are rooted in a tradition (XP) where features are added incrementally. In other words, almost everything starts out as a "simple" function, and many of those simple functions grow in complexity over time.
So it makes sense, from that perspective, that we would want to have tests even for simple functions.
But... we are ideally not testing functions, but testing behaviors. We don't necessarily care how some particular behavior is achieved. As we reduce coupling to the implementation details, those details become easier to change.
Which means that when we take some well tested module, and extract a simple method from it, we don't necessarily need to run off and create a bunch of new tests for it -- the behavior that we care about is already covered by the tests we have for the code that invokes the new function.
In the case you describe, you are actually going the other direction (which is fine); having extracted simple functions everywhere, you are left with code that is trivial. From a design perspective, this is great - trivial code is really easy to reason about.
Hoare wrote code that is "so simple that there are obviously no deficiencies". In a tdd approach, we tend to work toward designs where code that is expensive to test (ex: I/O) is isolated into modules that are too simple to fail, and ensure that the complicated logic exists in components that are easy to test.
That said, choosing between two alternatives based on a boolean condition isn't actually expensive to test, so the resulting design would normally arrive at a point where the logic is being measured by a "unit test".
But the subject of that test would probably have a larger grain than a single if condition.
i can only test if i test the current IMPLEMENTION
Let me try a slightly different spelling: yes, we are absolutely testing an implementation. The execution of the test is going to observe the behavior of the current implementation, and then check if that behavior satisfies the documented constraint.
We're measuring the implementation against a constraint on the behavior.
If I ran the zoo, we might have a test that looks something like:
opening_a_menu_also_selects_that_menu() {
// Arrange?
// Act
OpenMenu(id)
selected = isSelected(id)
// Assert
assert selected
}
The test doesn't care whether OpenMenu is a single monolithic method, or if it delegates the work to smaller parts. It is utterly hands off on "how", so long as how provides the required API and produces a satisfactory result.
My questions is still "whats the point"? If i change one little thing about my implementaiton, like not keeping a list of globalTabs anymore, the test will break.
The point is largely about being able to introduce new behaviors without breaking old ones. Or to improve the design without breaking the behaviors.
In cases where you have write once code -- because once it is done you leave it alone, or because you change it by throwing it away and starting over -- unit tests don't accrue as much value as they would in a growing code base.