The majority of software development is actually rather straightforward and boring: decompose the task into steps, create things that should do them (often grouping closely related tasks together into one class or system or module or whatever), plug ’em together, and apply any known techniques to take care of any unusual conditions, such as needing extreme scalability, fault tolerance, low latency, etc. But then there are the people who go above and beyond, to invent those “known techniques” that later people apply, or at least tackle unusually difficult problems.
Testing requires more out-of-the-box thinking. A form takes an integer from 1 to 100? Just for starters, let’s see what happens if we give it zero. (We hope it doesn’t crash the ship’s vital systems!) Or 101. Or leave it blank. Or put in 3.5.
A very basic test (such as applied to a very simple new feature in an existing well-tested app), or a beginning tester, might leave it there, only testing common unintentional user errors. But a good tester will take it up another notch. There are several ways to do that.
One is to test against at least some of the unusual conditions that can be brought about either by the user or the network or machinery itself, whether deliberately or not. What happens if we get to this point in the form, and close the application? Forcibly kill it from the OS? Open the same web-app in a new browser tab? Open another instance of the same desktop app? Log out? Power down? Leave it all up, but disconnect from the network? Let the phone battery die?