Lately I've been writing some 3D graphics code in Rust for fun. Almost twenty years ago, in highschool, I used to write similar code in C++, without relying on any sort of automated testing – and damn, with the amount of math involved, it's so nice to just write and run some rudimentary sanity-checking tests as you code! I used to rely on constantly building and running some interactive test app and from its behavior try to figure out whether your matrix calculations have a sign error somewhere or not.
A solid type system helps a lot in making refactoring a less fearsome endeavour, but a reasonable test coverage on top of static checking makes it even more so.
Also, automatic perf tests that keep track of regressions and improvements are just awesome.
Sure, this also makes sense when it is about math. But what I write mostly is about managing files on the disk, querying databases, processing text and XML and also some web frontend. Occasionally even communicating via a COM port. And when I need math I do it with NumPy. These tasks seem rather easy to implement and rather hard to test.
Sometimes mocking, even to check error paths, isn't a lot of work. It depends.
A lot of times designing your interfaces to support mock objects more easily improves them in other ways; if you pass in a file object instead of a filename, for example, not only does it become easier to pass in a mock object that raises an error, but it also becomes easier to pass in a GzipFile object or an HTTPResponse object, and presto, your function now handles compressed data and data stored on a web server.
with patch.object(requests, "get") as get_mock:
with nose.tools.assert_raises(socket.error):
get_mock.side_effect = socket.error
inject_exception.call_api("http://example.com")
If your functions have lots of hidden dependencies and side-effects, it’s hard to test.
If you split concerns properly and keep your glue/IO code separated from the decision-making/business-rules/logic, mocking is quite trivial, and there are advantages other than just testability.
A solid type system helps a lot in making refactoring a less fearsome endeavour, but a reasonable test coverage on top of static checking makes it even more so.
Also, automatic perf tests that keep track of regressions and improvements are just awesome.