Fuzz testing or Fuzzing is a software testing technique, which basically consists in finding implementation bugs using malformed/semi-malformed data injection: it's a type of Black Box Testing, which means testing a closed system on the comportemental point of view.
A trivial example:
Lets's consider an integer in a program, which stores the result of a user's choice between 3 questions. When the user picks one, the choice will be 0, 1 or 2. Which makes three practical cases. But what if we transmit 3, or 255 ? We can, because integers are stored a static size variable. If the default switch case hasn't been implemented securely, the program may crash and lead to "classical" security issues: (un)exploitable buffer overflows, DoS, ...
Fuzzing is the art of automatic bug finding, and it's role is to find software implementation faults, and identify them if possible.
A fuzzer is a program which will inject semi-random data into a program/stack and detect problems. The data-generation part is made of generators, and error detection relies on debugging tools.
Comparison with cryptanalysis
The number of possible tryable solutions is the explorable solutions space. The aim of Cryptanalysis is to reduce this space, which means finding a way of having less keys to try than pure bruteforce to decrypt something.
Most of the fuzzers are:
- protocol/file-format dependant
- data-type dependant
Because a program only understands structured-enough data. If you connect to a web server in a raw way, it will only respond to listed commands such as GET (or eventually crash). It will take less time to start the string with "GET ", and fuzz the rest, but the drawback is that you'll skip all the tests on the first verb.
In this regard, Fuzzers try to reduce the number of unuseful tests: the values we already know that here's little chance they'll work: you reduce impredictibility, in favor of speed.
A fuzzer would try combinations of attacks on:
- numbers (signed/unsigned integers/float...)
- chars (urls, command-line inputs)
- metadata : user-input text (id3 tag)
- pure binary sequences
A common approach to fuzzing is to define lists of "known-to-be-dangerous values" (fuzz vectors) for each type, and to inject them or recombinations, see  for real-life examples.
Protocols and file formats imply norms, which are sometimes blurry, very complicated or badly implemented : that's why developers sometimes mess up in the implementation process (because of time/cost constraints). That's why it can be interesting to take the opposite approach: take a norm, look at all mandatory features and constraints, and try all of them: forbidden/reserved values, linked parameters, field sizes (a lot of implementations are said not to verify field sizes). That would be hand-fuzzing.
Fuzzers usually tend to try one-level-imbrication-level attacks, which means changing only one parameter at a time. A highly intelligent fuzzer would detect/know linked parameters, and play on them.
Fuzzing tools can detect trivial errors quite easily, but are less gifted with complex ones (in terms of imbrication level).
Another problem is that when you do some black-box-testing, you usually attack a closed system, which increases difficulty to evaluate the dangerosity of the found vulnerability.
Technical resources on OWASP
Fuzzing vectors 
Wikipedia article 
Fuzzing-related papers 
The ultimate fuzzers list @ infosec 
Another list @ hacksafe 
Hachoir, a generic parser: could be quite a good starting point 
The fuzzing mailing list 
Codenomicon's product suite: