The 
QuickCheck manual gives detailed information about using
QuickCheck effectively. You can also try
https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html,
a tutorial written by a user of QuickCheck.
To start using QuickCheck, write down your property as a function
returning 
Bool. For example, to check that reversing a list
twice gives back the same list you can write:
import Test.QuickCheck
prop_reverse :: [Int] -> Bool
prop_reverse xs = reverse (reverse xs) == xs
You can then use QuickCheck to test 
prop_reverse on 100
random lists:
>>> quickCheck prop_reverse
+++ OK, passed 100 tests.
To run more tests you can use the 
withMaxSuccess combinator:
>>> quickCheck (withMaxSuccess 10000 prop_reverse)
+++ OK, passed 10000 tests.
To use QuickCheck on your own data types you will need to write
Arbitrary instances for those types. See the 
QuickCheck
manual for details about how to do that.
When testing fails 
quickCheck will try to give you a minimal
counterexample to your property: @ import Test.QuickCheck
prop_reverse_bad :: [Int] -> Bool prop_reverse_bad xs = reverse xs
== xs
>>> quickCheck prop_reverse_bad
*** Failed! Falsified (after 3 tests and 3 shrinks):
[0,1]
@
However, beware because not all properties that ought to fail will
fail when you expect them to:
>>> quickCheck $  x y -> x == y
+++ Ok, passed 100 tests.
That's because GHCi will default any type variables in your property
to 
(), so in the example above 
quickCheck was really
testing that 
() is equal to itself. To avoid this behaviour
it is best practise to monomorphise your polymorphic properties when
testing:
>>> quickCheck $  x y -> (x :: Int) == y
*** Failed! Falsified (after 4 tests and 3 shrinks):
0
1