You know, I hear people use this excuse to justify bad behavior a lot. I once heard a bike messenger tell me that brakes gave him a false sense of security so that's why he didn't have a brake on his fixed gear bicycle.
Compiles, runs, fails if i is out of bounds. Which means that you need to test the code that you write, and that even if you have 100% line coverage (or branch coverage or MC/DC coverage or...), it doesn't mean i won't get the wrong value.
People who claim that "once my C++/Haskell/Agda/whatever program compiles, I know it probably has no bugs" thus tempt others to mention "a false sense of security." (Agda might be going further than anyone else with proving that i cannot be out of bounds, AFAIK... though a generally undecidable problem will remain generally undecidable.)
I've never seen anyone claim that types (even a very strong type system like Haskell or Rust) mean you don't have to write tests. They just mean you don't have to write the very silly tests you would otherwise have to to feel secure in a dynamically typed language.
I dunno. If you cover every source line at least once, that catches the dumb type errors as it does dumb bounds errors etc. If you don't have a test covering every source line at least once, then you will have dumb type errors as well as dumb bounds errors in those uncovered lines. So I don't think dynamic languages force you to write silly tests when you look at it that way.
If however you say, "hey, I seriously don't want to cover all the lines including say error messages", then in Python,
if error:
print obj.name
...might be a problem because obj doesn't have a name; in C++,
if(error)
cout << obj.name;
...might only blow up because obj is a null reference (which is what you get when you dereference the null pointer, in practice, even though null references aren't supposed to exist); and in a language with non-nullable pointers, the equivalent of the above can only blow up if printing the name (which is surely valid if obj is non-nullable and type-checks as having a name) somehow blows up, which for a string in a memory-safe language is very very unlikely.
So if you leave uncovered lines in your code which we all do then yes, the stricter the type system, the better your chances are, statistically, all else being equal (for instance, the number of lines being the same... which might not be the case.)
Overall the silly tests people sometimes write in dynamic languages are needless IMO and result from over-applying "TDD" or "unit testing" or some other buzzword and/or paranoia of someone coming from a statically typed language background. I personally think I have pretty much the same amount of tests regardless of the type system.
> They just mean you don't have to write the very silly tests
I have seen claims and have a hunch that more than just very silly tests can be eliminated by types, but I'm struggling to come up with or remember any examples.
I'm hoping someone who has one will reply with one of these claims or any examples.
Unfortunately, unsafeIndex is often spelled "!". There is a push (which I support!) away from partial functions, but there are still plenty of partial functions provided by standard libraries under names that sound reasonable.