Let's say I want to implement a simple key-value storage, which provides basic CRUD operations. The only requirement is that all the modifications should persist between application launches. I'm not concerned about particular persistence mechanism, as it may change during development, or even be different depending on the platform.
How should I approach testing such library / class / module (whichever it becomes in the end) ?
All the answers I found were focused on testing particular database solution, but that's what I want to avoid. I'm only concerned about the fact that changes are being persisted, not about how they're persisted.
The problem I see with this approach is large amount of code for very simple task, and the fact I'm still concerned about particular persistence mechanism in the end. This may complicate development, especially for such a simple thing as a key-value storage.
That would test exactly what I need, but such a test would probably be expensive, and not necessarily easy to write.
Well, the whole point of the library would be to test persistence. Say, some user settings, or game save. Testing if it works in memory doesn't test the real problem.
I've read through most of the topics related to persistence and TDD here, and on other sites, but all I could find focuses on specific persistence mechanism.
somewhat related, but doesn't touch on the testing subject: How many levels of abstraction do I need in the data persistence layer?
A persistence layer is a port of your application. As such, your approach should be to write an adapter to that port, and test that adapter using an integration test.
The test itself doesn't need to spin the adapter more than once - testing that things actually persist would be testing the persistence mechanism itself, and that's not your concern. At some point, you'll have to assume things work, and they have their own tests to make sure that's correct. A typical write and then read back test would do for most cases.
Once the adapter exists, you can use several techniques for writing other tests - mock the adapter when it is a collaborator for other units, or use an in-memory implementation of the adapter (using a contract test to see that the in-memory implementation is the same as the other one).