I'd like to add something else that's hinted at by other answers, but I don't think has been mentioned explicitly:
@puck says "There still is no guarantee the first mentioned argument in the function name really is the first parameter."
@cbojar says "Use types instead of ambiguous arguments"
The issue is that programming languages don't understand names: they're just treated as opaque, atomic symbols. Hence, like with code comments, there is not necessarily any correlation between what a function is named and how it actually operates.
Compare assertExpectedEqualsActual(foo, bar)
with some alternatives (from this page and elsewhere), like:
# Putting the arguments in a labelled structure
assertEquals({expected: foo, actual: bar})
# Using a keyword arguments language feature
assertEquals(expected=foo, actual=bar)
# Giving the arguments different types, forcing us to wrap them
assertEquals(Expected(foo), Actual(bar))
# Breaking the symmetry and attaching the code to one of the arguments
bar.Should().Be(foo)
These all have more structure than the verbose name, which gives the language something non-opaque to look at. The definition and usage of the function also depends on this structure, so it can't get out-of-sync with what the implementation is doing (like a name or comment can).
When I encounter or forsee a problem like this, before I shout at my computer in frustration I first take a moment to ask whether it's 'fair' to blame the machine at all. In other words, was the machine given enough information to distinguish what I wanted from what I asked for?
A call like assertEqual(expected, actual)
makes as much sense as assertEqual(actual, expected)
, so it's easy for us to get them mixed up and for the machine to plough ahead and do the wrong thing. If we used assertExpectedEqualsActual
instead, it might make us less likely to make a mistake, but it gives no more information to the machine (it can't understand English, and choice of name should not affect semantics).
What makes the "structured" approaches more preferable, like keyword arguments, labelled fields, distinct types, etc. is that the extra information is also machine readable, so we can have the machine spot incorrect usages and help us do things right. The assertEqual
case isn't too bad, since the only problem would be inaccurate messages. A more sinister example might be String replace(String old, String new, String content)
, which is easy to confuse with String replace(String content, String old, String new)
which has a very different meaning. A simple remedy would be to take a pair [old, new]
, which would make mistakes trigger an error immediately (even without types).
Note that even with types, we may find ourselves not 'telling the machine what we want'. For example the anti-pattern called "stringly typed programming" treats all data as strings, which makes it easy to get arguments mixed up (like this case), to forget to perform some step (e.g. escaping), to accidentally break invariants (e.g. making unparseable JSON), etc.
This is also related to "boolean blindness", where we calculate a bunch of booleans (or numbers, etc.) in one part of the code, but when trying to use them in another it's not clear what they're actually representing, whether we've got them mixed up, etc. Compare this to e.g. distinct enums which have descriptive names (e.g. LOGGING_DISABLED
rather than false
) and which cause an error message if we get them mixed up.
assertEquals()
, that method is used hundreds of time in a code base so it can be expected that readers familiarize themselves with the convention once. Different frameworks have different conventions (e.g.(actual, expected) or an agnostic
(left, right)`), but in my experience that's at most a minor source of confusion. – amon Jun 30 '18 at 13:03assert(a).toEqual(b)
(even if IMO it's still needlessly verbose) where you may chain few related assertions. – Adriano Repetti Jun 30 '18 at 13:43assertExpectedValueEqualsActualValue
? But wait, how do we remember whether it uses==
or.equals
orObject.equals
? Should it beassertExpectedValueEqualsMethodReturnsTrueWithActualValueParameter
? – user253751 Jul 1 '18 at 10:28