This is an dddition to Lie Ryan's answer.
As with pretty much anything in programming, even here the approach will not be similar for different cases.
You are justifying the modification procedure by having well written unit tests, but even then, directly modifying a class or making it open for extension usually depends on what exactly you are currently programming.
Case I.
When it makes sense to modify an existing class
Imagine you have your domain layer containing the business logic of your application. In an ideal world, the business rules are well defined and are set. There is only one set of the business rules.
In a situation such as this, you will rarely have different implementations of a similar thing, you are limited by the business constraints and these have to be well defined.
You will probably have unit tests for this layer and when you modify this layer, you want the tests to fail, inidicating something (usually a business rule) really has changed.
Case 2.
When it makes sense to extending a class
Then there's the other situation, which can be applied to pretty much anything aside your domain. Services, modules, persistence layer, caching layer, repositories,...
Many of these are classes, which, even though may only exist once in your code, maybe replaced one day with a better alternative. This is when it is good to code to an abstraction, be it an interface or an abstract class acting as a supertype for concrete implementations.
By not depending on a concrete implementation but an abstraction, when a new alternative for said service (be it caching, persisting or processing data) comes, you can simply provide a new implementation, change your configuration to use the new implementation and you are pretty much set, without toouching the rest of your (usually pretty large) codebase.
OCP definitely does not make sense for everything. As with every pattern, even this one is meant to ease the development but is not a rule to be sctirctly obliged by.