10 ms·
Aside from everything else, writing tests is just as much about writing testable code. Not everything that makes code easier to test makes code better, but ofte
by jchw 6y ago
Aside from everything else, writing tests is just as much about writing testable code. Not everything that makes code easier to test makes code better, but often times many things do; after all, nothing is easier to test than a simple, small, pure function with inputs and outputs.
I prefer trying to focus on table driven testing where I can write a single test that exercises the code various ways. This is not applicable to all types of software, but it is wonderful for things like parsers, emitters, algorithms, data structures... things that test well.
Unit tests like this are cheap but make it easy to assert that the code works. If you can assert that your individual functions do what you expect, it makes debugging and understanding software easier.
I like to think of testing as executable debugging. It’s like a debugging session that is executed over and over again. If your tests are difficult to maintain it may say something about what they are asserting or what they are testing.
- postalrat 6y agoWrite a test for a simple identity function that simply returns its only argument. An identity function should be as testable as they come.
- nelsondev 6y agoString value = “postalrat”; Assert.assertEquals(value, identifyFunction(value));
- postalrat 6y agofunction identifyFunction(a) { if (a == 'nelsondev') return ''; return a; }
- nmfisher 6y agoI'm not sure what your point is here - your implementation will fail the parent's test?
- deadso 6y agoTheir point is that the test case provided would lead the developer to believe that the implementation is a correct identity function, when in fact it isn't. Just clarifying, I do not take this as a good reason to not write tests.
- nmfisher 6y agoAh, right. I didn't realize some people believe tests should "prove" the correctness of a function (or that if they can't, they're worthless). Testing has its flaws, but that's not one of them. There's often very good reason to ask the question "does this function operate the way I expect it to when I provide this input?"
- trampi 6y agoAnd that's where the developer writing the test would check the test branch coverage and see that an obvious branch has not been tested.
- rexarex 6y agodef identify_function(a:str) -> str: if a is ‘postalrat’: return a
- dfabulich 6y agoWhy did you type this? (I'm pretty sure it doesn't make the point you think it makes.)
- ptx 6y agoTestning actively malicious code seems to me like a different problem than testing regular business logic. Yes, the code you're testing could also look for the running test code in memory and rewrite it to always return success, but why would you write code like that?
- CJefferson 6y agoAmerican Fuzzy Lop ( https://lcamtuf.coredump.cx/afl/ https://lcamtuf.coredump.cx/afl/ ) will detect that (at least, it will in C, where I tested it).
- chriswarbo 6y agoI see you are an Enterprise Developer From Hell https://fsharpforfunandprofit.com/posts/property-based-testing/ https://fsharpforfunandprofit.com/posts/property-based-testi...
- Jtsummers 6y agoIf I really needed that I’d use property-based testing. On my phone or I’d code an example. But basically it generates test inputs and gives you confidence by running some number of random tests that verify the property holds or finds a counter example. More realistic but basic example is to show that a function is its own inverse (reverse reverse list == list) or that one function is the other’s inverse (decode encode plain == plain).
- chriswarbo 6y agoIn ScalaCheck: forAll() { (x: String) => identity(x) == x }
- exdsq 6y agoUnless you’re implementing a semi-group which can be a pretty common design in functional programming
- deleted 6y ago[deleted]
- pydry 6y agoWhen people say "testable code" I always wince. Unit tests fucking suck at testing most code. Really suck. They're ok (ok, still not great) for things like parsers and algorithms... which isn't most code. But, rather than improving test tools to handle more types of code, developers say that they want to completely restructure the code to make it more amenable to the fact that unit tests suck. Unit tests can't handle database? You need "Testable code"! No... Developers code to a spec, but unit tests suck at encoding a spec except in the few cases you alluded to simply because most specs are not easily, clearly encoded in the form of your turing complete programming language with its mediocre tooling. The practical upshot of this theoretical brain damage is that when people write unit tests on code where it isn't a suitable tool it tends to be an expensive waste of time. The tests cost time to build, time to maintain and when they fail it means... "oh, you changed some code". Thanks, test. And then religious unit testing people always argue that it wasn't the tool that was at fault it was you. Working on projects with excellent integration test tooling really opened my eyes to the possibilities of another world - one without unit tests.
- jchw 6y agoI don’t have much to say beyond what I already did re “testable” code; forcing code to be “testable” can be bad, but in reality making code more testable probably mostly amounts to breaking it down into simpler, functionally pure bits. Not everything unit tests terribly well. I haven’t seen it work very well for things like React or Angular components. It does work well for small bits of apps. Like for example, lets say you have a component where a good deal of what it does is extracts information out of a URL. You might have tests that exercise the entire UI, entering URLs and checking the HTML output. The “more testable” version of this, imo, would be separating the URL parsing and extraction bit to a single free-standing routine that outputs some data given an input URL. You can then table test that bit. Then testing that this ties into the UI correctly could be done in the integration or end to end testing. In case of table based unit testing, I think it often works great, as it can act as a running log of regressions and newly discovered edge cases that can even serve as a sort of document of expectations for other developers, and while it cannot be used to test all sorts of code, it has wide applicability and you can see it in webapps, Go servers, the Wine and libinput sources, etc. It’s easy to get stuck on a single strategy to rule them all, but I think that often is a bit presumptuous and maybe dogmatic. Tests are a toolbox. Not every problem is a nail.
- jordanpg 6y agoI like to think of tests and the team's emotional relationship to them as a barometer of the extent to which the repo is and has consistently followed best practices in the language. IOW, enforcing unit and functional testing forces developers to follow best practices. If I find myself dreading writing and maintaining tests, it's a great indicator that the code diverged from best practices at some point in the past, and that the technical debt has just been accumulating ever since. Difficult to test code is code smell telling me that the technical debt is getting out of control or that the code is getting unmaintainable, and this can be difficult to walk back. Another great indicator is the extent to which the team "detests" mocking. Mocking is great -- when it's trivial to do. When it's not trivial, and elaborate mocking behavior and reflection and testing of private methods, etc. is needed, it's another indicator that best practices have been sacrificed for expediency.