There are actually many poor claims you made in your posts about "good tests".
> I'm aware that addition is a toy example, but suppose we want to test our implementation:
> Except for very simple verification, to exclude obviously broken implementations, I'd rule out testing specific values such as 3+4=7. And, like you said, performing an exhaustive exploration of all values is out of the question.
> So I'd try property testing instead. Relevant properties in this case are associativity, commutativity, etc.
> As an example, I'd try writing properties such as:
> for all X, Y: add(X, Y) = add(Y, X)
These properties can also be satisfied by implementations of add() that:
- return a constant value
- return the smallest number of (x, y)
- return the largest number of (x, y)
The cases you threw out as an "obviously broken implementation" are required to actually validate that functionality of the method. The functionality of the method is also one of the properties of the method.
You can write it in a more generic way than simply: assert(7, add(3, 4)). However, those tests are _also_ required. Without them, you never actually test that the `add()` function does what it's supposed to: add two numbers together.
Regardless of type system, you also have to worry about underflows and overflows - another property of the functionality of the method.
> You are right, without additional information property testing would be less useful. Which is yet another reason to favor static typing in my opinion.
Static typing doesn't help you constrain sets of inputs; it may not be valid that your method accepts all ranges of integers. You could have a method `addbase2(int x, int y)` that is to be used only when x and y are powers of two because of an optimization you perform in that method. Static typing doesn't help you generate the correct input set for x and y.
The only thing that static typing provides, in regards to test cases, is this:
def add(x, y)
assert x is int
assert y in int
return x + y
// test cases
assertIsThrown(add("foo", "bar"))
That was the test case you had.
Regardless, the point of the article was not about static typing being bad. There is value it. However, there is also value in not being so rigid in your type system that things don't work well.
add((short)0, (long)1) // compiler error if you have an extremely rigid type system
Generic systems typically swing the pendulum far to the right requiring an extremely rigid type system. That always causes pain. The question you have to ask, is the ROI worth it. For some, it is. For others, it's not.