For example, consider a sort function. The types can tell me that I passed in a collection of a particular type and I got a collection of the same type back. However, what I really want to know is that the collection contains the same elements, and that they're in order. This is what you really care about at the end of the day, does the function do what I intended.
This is difficult to express using most type systems out there. You could use dependent to express that, but it certainly wouldn't be something that you get for free with Haskell.
So, you'll still have to write tests to ensure that the function is semantically correct for anything non-trivial.
It's also worth noting that Clojure Spec lets me express exactly what I care about using the same language semantics I'm already using to write regular Clojure code:
(s/def ::sortable (s/coll-of number?))
(s/def ::sorted #(or (empty? %) (apply <= %)))
(s/fdef mysort
:args (s/cat :s ::sortable)
:ret ::sorted
:fn (fn [{:keys [args ret]}]
(and (= (count ret)
(-> args :s count))
(empty?
(difference
(-> args :s set)
(set ret))))))
The specification will check that the arguments follow the expected pattern, and that the result is sorted, and I can do an arbitrary runtime check using the arguments and the result. In this case it can verify that the returned items match the input.