I don't understand the point of integ tests for a lib. Whatever you can do in a library you can also do as a unit test, but unit tests can also test private functions. What's the benefit here?
The only thing I can think of is that restriction is a feature, as in it ensures that your public API has everything needed to function properly (i.e. things that need to be pub are pub).
I also am curious why cargo test doesn't seem to differentiate between unit or integ tests. I'd think that a lot of binary integration tests wouldn't want to automatically be run locally, while unit tests should be fine.
Testing a library’s public API only is a very important feature — otherwise, you could design a library that accidentally cannot be used for its purpose, or update it in a way that breaks that API.
It’s not just a matter of public vs. private items; there’s also considerations like whether the trait coherence rules allow implementing traits in the way needed to use the library, or whether a macro uses the correct item paths. Those can only be tested by a separate crate.
Also, test targets (I don't really like calling them “integration tests”, because integration is the role of a test, not a technical characteristic of how it’s run), being separate crates, can do things that benefit from being isolated to separate crates or processes, like select an alternate global memory allocator for instrumentation or testing allocation failures. They can be compiled in parallel or selectively, to reduce testing time during development.
The integration tests in the tests/ dir have two important differences:
They use the library built withoutcfg(test), so it can't have any special test-only behaviors that you'd miss outside of the tests.
They can only access the library's public API. This way you know the interface works and your tests don't accidentally depend on something inaccessible outside of the library.
To ensure long term maintainability and longevity of a software project, there are two important properties your test setup must have:
It must me hard to accidentally break functionality that matters,
It must be easy to refactor implementation details and internals.
Integration tests have both properties. Integration tests are good.
Unit tests that depend on implementation details have the first property but NOT the second property. In other words, you will regret that you wrote unit tests when the time has come for the first big refactor. Because then you need to rewrite your tests too.
If you only test what matters to consumers, like the public API, you can refactor internals without changing tests at all.
Of course, in practice, you will not get your public API right the first time, so you need to iterate on that as well.
And of course sometimes unit tests make sense even if they test an implementation detail.
But in general, stick to the "only test what matters" philosophy.