I just figured out why callback style APIs are harder to test (and harder to deal with in general). It’s because they break one of the essential ingredients of a hexagonal architecture: the middle hexagon should be independent of the adapters. Here’s why…
In order to use a callback API, one or more of our application classes must implement the callback method(s), and must therefore conform to some abstraction defined by the API’s provider. So our classes must depend on the API. Which means that the API can’t be easily mocked or stubbed. We have to treat our callback objects as being part of the adapter for that API, and test the rest of the application by mocking or stubbing them.
In fact, now I’ve written it down it’s trivially obvious, and hardly worth saying.
As you were.