I don’t think you can’t unit test properly without a mock framework. Look, I don’t have a crap-ton of experience writing unit tests; I’ve probably written less than 2000 so far. But from that amount of experience I can definitely say that your code will not be tested as well as it needs to be unless you mock your code dependencies. Some people aren’t clear on why this is true. This is why I believe you need to mock to test.
The purpose of mocks is to replace dependencies in your code with fake objects that can be controlled during the test process. It allows you to fully isolate and test the unit of code in question. These mocks can be instructed on what calls they should expect, and what return values to pass back to the code under test, or even what exceptions to throw. These assertions can then be checked at the end of the test in order to determine if your code behaved as expected.
The most common question about this process is *why?*. Why take the effort to replace these dependencies when, IRL, they are part of the code under test? Wouldn’t it make more sense to test everything at once?
The reason is because a unit test should focus on a specific unit of code; a unit most commonly meaning a specific execution path through a single method. If your test calls other methods of other objects, your test isn’t about the specific unit in question; it is testing the interaction between two objects within your codebase. That’s not a unit test; its an integration test. While integration tests are valid and useful, they may result in false positives in your tests. An object may not behave as expected, due to its code being buggy, which results in a false positive in your test. Mocks also allow you to test code in ways that would normally be hard, or even impossible, to do using the real dependencies.
In order to remove the chances of this from happening, you replace this dependency with a mock object. You then instruct the mock to expect certain calls and return certain results, or even to throw exceptions which may be hard, if not impossible, to simulate with production code. You can then fully exercise the method under test, hitting all the code paths in a structured way that can be reviewed for correctness. And, unless you do this, your code isn’t tested.