Deterministic and nondeterministic tests have different use cases and different values to your suite. Generally nondeterministic can't provide the same precision as deterministic testing, which has slowly grown into "nondeterministic testing provides no value." This is false. They may be less precise, but they can also be much broader, which has its own benefits.
Let's take an example: you write a function that sorts a list of integers. What would be some of the deterministic unit tests you'd find useful?
- An empty list
- A list with just one element
- A list with all of the same element
- A list with multiple unique elements
- A list with multiple elements, some of which are duplicates
- A list with
- A list that's already partially sorted
- A list with 10,000,000 elements
And that's just a sorting function! Sure, you could argue that some of these are unnecessary, or that some of these can be ruled out with informal reasoning. But we're engineers and we've seen informal reasoning blow up in our face. We know we're not smart enough to completely understand the systems we've built or fully keep the complexity in our heads. That's why we write tests in the first place. Adding nondeterministic testing just says that we might not necessarily be smart enough to know all of the good tests a priori. By throwing semi-random data into your function, you're much more likely to find an edge case you missed.
Of course, that doesn't rule out deterministic testing either. Nondeterministic testing helps find bugs in huge swaths of the program. Once you've found the bugs, though, you need a reproducible way to show that you fixed it. So:
- Use nondeterministic tests to find bugs in your code.
- Use deterministic tests to verify fixes in your code.
Note that this means a lot of solid advice about unit tests don't necessarily apply to nondeterministic tests. For example, that they must be fast. Low-level property tests should be fast, but a nondeterministic test like "simulate a user randomly clicking buttons on your website and make sure you never get a 500 error" should favor comprehensiveness over speed. Just have a test like that run independently of your build process so that it's not slowing down development. For example, run it on its own private staging box.