The purpose of a test is to restrict the possible productive implementations. Make sure that you only put restrictions on the implementation that you actually need. Typically this is what your program should do, and not how it does it.
So if for example your service adds something to the repository, you should test that the new entry is contained in the repository afterwards, and not that the add action is triggered.
For this to work, you need to be able to use the repository implementation (tested elsewhere) in the test of the service. I found that using the real implementation of a collaborator is generally a good approach – because it is really the best implementation around.
"So but what if using the real implementations in the test is expensive (e.g. because they require resources which are complicated to set up)? I need to use mocks in this case, right?"
In any case you'd probably want one integration test which tests that the real implementations work together. Make sure that this one integration test is all that is needed to test your service. Or in other words: If a service plugs together a lot of collaborators (and is hence potentially hard to test), make sure that it doesn't contain any logic. If it does, and you'd need multiple (integration) tests, you need to change the structure of your code, e.g. by isolating the logic and hence making it more testable.
Using mocks in this case eases the pain of testing a piece of badly isolated logic, and hence hides an architectual problem. So avoid Mocks wherever possibledon't use mocks to test badly structured code, but fix the structure instead.