If your code is mostly passing data around and managing side effects a unit test is more like a parasite. It will couple to your code and fail noisily when refactoring but not provide any actual useful feedback.
No, I’m still going to sweep the floor, just not with a toothpick.
You need to test that you're using the contracts you've made, and integration tests verify that everything works as expected when both sides are complying with the contract.
I'm saying that if I've abstracted out the filesystem into an interface the distinction of asserting against whether a method was called on that interface and asserting a side-effect is somewhat trivial. I've made it so that side-effects must be handled in integration tests.
But now you're enforcing a degree of coupling between your filesystem interface and your code. If you refactor the filesytem interface such that the overall logic hasn't changed, but the parameter order for one of the methods is updated, then you essentially double the amount of work you have to do (update the code and update the tests). If you just test the logic and not the interface, then you only have to update the tests that pertain to the logic itself.
You should read up on the concept of functional core/imperative shell.
While that's true, you don't want to end up in a situation where the test is enforcing coupling which makes it harder to refactor your code. So, for instance, if you have tests that assert whether the method tested calls another method with certain parameters in a certain order, then you're enforcing coupling between the two methods. If you refactor the code to have the first method return a result and then use that result to call the second method, then you also have to update the tests. But they shouldn't have to be updated since the overall logic hasn't changed. If, instead, you test the logic of each method individually, then you can refactor and not have to update the tests.
I'm confused. These opening words make it sound like you're getting ready to partially disagree, but then the rest of the comment is pure agreement. What did I miss?
When I modify this code, what tests will tell me that it still does what it's supposed to?
To this situation:
When I modify this code, will tests that enforce coupling between different components tell me that the code still does what it's supposed to?
You observed that no, such tests would need to be changed even though the overall logic was the same. I assume that next you would apply the same question to a test that is for the overall logic, and conclude that yes, it's a good test.