There's two popular ways to verify your programs. Types and tests. Types are EASIER then tests and types COVER more then tests.
The ideal thing is to use both, but why spend so much time on unit tests over types is beyond me.
Note that integration tests are a different beast. Why? Because you need to test things that aren't covered by either unit tests or types. In unit tests you mock out things that are external like IO, databases, and the like. In integration tests you need to test all of those things.
Integration tests are THE most important thing to use to verify your program because it's an actual test to see if your code works. But unit tests vs. types? Types win.
When I code. I never write a single unit test. Just types, and integration or end 2 end tests. It doesn't hurt to write a unit test and it's actually better. But the fact that I'm confident my code works without it shows the level of importance.
What do you think "type of values" means and encodes, and why do you think it's different from your handwritten tests?
I assume you do know that type checking is more than simply validating you're passing a string to a function that expects a string argument?
> Types also only catch certain classes of errors
Which classes of errors? Why do you think manually written unit tests are more complete?
But you now why I asked the question: people who make claims like "types make only guarantees about the type of values" usually don't understand what modern type-checking can do or what can be encoded in types. People making these claims often believe type-checking is limited to validating that you only pass strings to a function expecting a string argument, or some minor variation of that.
Types are just tests. By removing all the mindless, repetitive tests you write, typechecking can free you up to write the actual meaningful tests that you truly need to write.
It's not "pick one", it's "use type checking to help you only write the unit tests that matter".
Thanks for your reply, this is why I asked: this kind of typechecking you describe only scratches the basics of what a robust type-checking system can do.
In practice, it can cover much more than "you returned a number from a function marked as boolean", and so it saves you from writing lots of unit tests, leaving you free to write only the few tests that make sense for your business logic.
Once you have a proper type system in place, you can do much more than:
> [...] a complicated set of business-logic functions that all return booleans [...]
You can encode more useful things in the type of the functions than just "it returns a boolean". Of course if every function you write only returns a boolean you won't get much from the type system! But that's circular reasoning...
Yes, but this is working with legacy code that wasn't written with a mature type-checking system in mind. The boolean type encodes very little meaning with it, and so there's not much you can reason about it, as you've noticed.
If your argument is "a modern type system cannot help me much with legacy code that didn't leverage it to begin with" I can understand you and even agree somewhat. But that's a different argument.
That's what I mean by circular reasoning: if you don't use the types, then yes, a type system won't be of much use. You'll spend time writing unit tests, which for legacy code seems like an adequate approach.
This is a modeling problem at this point. Types can make your model better by encoding more things. It's often not easy or feasible to refactor the mess, so sometimes you're stuck with it.
Before mypy came along I was pretty disciplined at A) testing and B) ruthlessly clamping down on primitive obsession.
I use types a lot now but I didnt see enormous quality or productivity gains when I started using them. I suspect people who let compilers catch their bugs were always A) pretty lax testers and B) probably often let primitive obsession get the better of them.
There are only a few types of tests where I think that types actually serve as an appropriate substitute.
Imo this might be true if you're writing a lot of glue code. But I think unit tests are extremely valuable for testing pure functions that are pure logic/computation. For glue code/ensuring everything is hooked up correctly, types easily beat tests, but for pure logic tests (ideally property based testing if you can swing it) works great.
The weird thing is Game developers don't unit test stuff. Why? Because it's mostly not that effective. I know a lot of people say stuff like game devs are cowboys that don't follow best practices but that's not true. A lot of game devs know their shit inside and out.
Here's the reason why Game devs don't unit test. There's no point. They run the program themselves a couple of times on their own computer and their good. The problem with web dev is that all their code is so Tied with IO they can't even run the code on their computer. So in order to test their logic they have to write mocks and then unit test their code with mocks in place. But why even do this when you can cover it all with an integration test?
Hence I don't unit test. My integration tests and type checking covers it all.
Usually the primary mistake is either using cucumber (which is awful) or surrounding the wrong interfaces with tests (usually at too low a level).
Done well they are like a mini superpower.
Once you have that and somebody has taken you around the block 3 or 4 times pairing with TDD on your app it usually starts to make more sense.
On my team after I inculcated the TDD habit properly, BDD came naturally. I didnt even encourage them to have conversations with the PO while screensharing the tests. They dont even know that's called BDD.
If your work is deeply exploratory and you don't even know what the final interface/API will look like, you shouldn't start with TDD. You should probably test as you go though, once you find some behavior you'd like to keep and pin down variations and edge cases you met during dev.
If/Once you know what the expected behavior is, you can encapsulate that knowledge in tests, that's TDD to me. Then, I can write more code that meets the need of the expected API.