5 ms·
>You literally had a bug in your code that would have been caught with testing, and it did not have to do with the logic but with input sanitization, which has
by formulathree 3y ago
>You literally had a bug in your code that would have been caught with testing, and it did not have to do with the logic but with input sanitization, which has to do with your integration with what the framework provided you.
No this bug wouldn't have been caught. I'd have to write the test for it.
I did do integration tests. I just did them manually. If I wrote all the manual tests and made them automated this "error" would still be there. It's a matter of luck whether I manually test or write the integration test that finds this error.
It's also technically not huge bug. The server returns a 500 error instead of a 400 error.
>I am not advocating for unit tests, I was advocating for any tests at all, in the code sample. I already said that the highest value tests in my mind are E2E tests. The ones that make sure your code does what it says it does, in as black-box a way as possible.
Ok you got this. You're right on this. I should put in some testing here. even if it's minimal.
>Also, I want to note that the logic you're applying works best with languages with strong type systems, not Python.
This is where you are wrong. Python has what I call an opt-in type system meaning it's optional and it can be as strong as you want it to be. You choose the external type checker like mypy and those type checkers can be as powerful as you want.
The python type system can even do Haskell/Rust level exhaustive matching on sum types: https://tech.preferred.jp/en/blog/python-exhaustive-union-match/ https://tech.preferred.jp/en/blog/python-exhaustive-union-ma...
As you've seen with my JSON type, python allows for recursive types and sum types. Which makes it more versatile than golang which can't do either even though it has a "strong" type system. All the bells and whistles for a modern type system are there and more.
In fact I believe python may even exceed typescript in power. I believe type script won't do exhaustive checks on sum types.
>I am only comfortable skipping tests completely in languages with strong, expressive type-systems -- Typescript, Rust and Haskell. Even then, I still make sure to write regression tests and think of what can go wrong.
So modern python matches these language in type level power, it's just optional so it's not as pervasive as typescript but the option is there and when utilized it's equal if not better in power. But proof based coding isn't solely just on using types to prove out your code. I just used idris as an example of an alternative path. The reality is Idris is a bit too extreme because coding those types takes too much effort.
Basically i change my coding style to both minimize errors and allow me to build a minimal proof in my head. I use coding style, proofs in my head, and a strong/versatile type system all in conjunction to minimize error rate to low enough levels that tests are less important.
>Again, YOU think that the difficulty to writing integration tests is huge, and that's because you don't do it a lot. To me that's a liability, not something I want to adopt -- the faster I can write tests and prevent regressions, the better code I am able to write, the easier it is to integrate my code and have confidence that changes don't break existing functionality.
You can't do integration tests without using infrastructure and spinning up processes. Let me put it to you plainly. It's not ME who thinks it's difficult. It's the majority of the developer population knows it's much more difficult then unit tests. That's why such a segregation even exists. The fact that we categorize the tests into integration tests and unit tests speaks to huge differences between the two in difficulty.
You may think integration tests are easy, but that's just your own opinion. This is different from the overall general opinion which is more inline with what I'm saying.
>Absolutely not -- no matter how strong your type system is, it will bend and break during contact with the real world. E2E tests, regression tests and the like are crucial for good engineering.
Sure I can agree with your philosophy in spirit. Practically speaking though there's the effort involved in this that makes it so that many companies just don't do it.
Maybe it's changed recently, I've moved from backend web to embedded systems to arm devices and GPU programming so I've been away from web stuff for about 3 years. Generally no docker or as much infra in this universe. If docker-compose and mocking infra locally is universal on everyones local machine now, then yeah you could be right but from what I'm seeing... I don't think so.
Unless we have metrics I'm still going to go with the fact that in general it's not easy and most companies don't do it.
>You seem to be battling one heck of a strawman here. I have not said that you should write excessive unit tests. I said that you should have at least any tests, and that I find the most value in E2E tests.
Alright. I get what you're saying. Write tests, integration tests are the most important thing here. Unit tests are basically more or less practically useless for this takehome. And specific for this takehome I could've written integration tests here by writing some hack python script to call docker-compose. I agree.
Where we don't agree is the difficulty level of writing these tests and the general pervasiveness of integration or E2E tests in the industry overall. We can just leave it at that, without metrics the argument goes in circles. We've taken it as far as we could.
>Also, Idris is basically Haskell w/ dependent types, but what you're saying about it is full of half-truths. You cannot use the type system to prove a program is correct, you use type systems to prove a program is sound. Correctness is problem dependent, input dependent, and many other things that are situation-dependent.
No this is truth. You're just misinformed.
Dependent types are enough to prove a program is correct given a set of specifications. Of course not everything can be proven. But most things can. You can't prove the halting problem for example, but you can prove specs like determining if a list is sorted: https://dafoster.net/articles/2015/02/27/proof-terms-in-idris/ https://dafoster.net/articles/2015/02/27/proof-terms-in-idri...
Also from the documentation of idris, (see the title): http://docs.idris-lang.org/en/latest/proofs/index.html http://docs.idris-lang.org/en/latest/proofs/index.html
Theorem proving is the title. or aka proving anything mathematical or aka proving mathematical programs meet mathematical specs. Dependent typing is one way to go about this. There are other ways. The intended design of idris is similar to TLA+ the intention is to allow the user to prove the program correct to a spec and avoid unit tests via proof.
>With a strong type system, you can avoid unit tests, but only if you use that type system properly (ex. using a Natural instead of an Integer where it is appropriate).
How is using a nat more safe than an int? A nat just makes it more general.
I'm not 100% in agreement here. Let me add some nuance to my opinion. Unless the type system is more powerful then what you have in say haskell, it can only reduce the need of unit tests. You can't completely avoid it, but it can be reduced to the point where practically speaking you don't always need it. Certainly not for this takehome.
With something like idris you can, technically speaking, eliminate the need for unit tests all together. But this is a technical thing. In practice it's really hard to achieve this due to how hard it is to write these proofs. But the technical level of safety is operating at the highest level here.
>I think it's a matter of culture. Are you the person that makes time to write these/makes it part of your plan, or not?
Depends on the situation, the timelines, the brittleness of of the project and the technology. Highly, highly dependent on all sorts of critical variables. I'll tell you this: I don't enshrine the concept of testing. It's always a case by case basis.
If i had unlimited time though, I think your philosophy would be correct. Always write tests. But with limited time I artfully choose between a myriad of techniques (including choosing what to test) to meet certain timelines in the best way possible. This sometimes means not writing tests.
Also sometimes laziness is a factor, I'll be honest. If I'm confident my toy project or take home is correct I just won't write unit tests even if I have unlimited time. I think what I learned from this overall thread and from you is that I should always write a bunch of tests for the take home.
Hey I know this thread got a little heated. I want you know that I appreciate your viewpoints even though I disagree with a lot of points. I think heated but respectful debates are healthy and I definitely learned something here. Conversations where people are overly polite I feel like I learn nothing because people rather be polite then honest. So appreciate all the time you took to reply. I may be clashing hard against your points but there's nothing but respect here. You are clearly experienced.
- hardwaresofton 3y ago> No this bug wouldn't have been caught. I'd have to write the test for it. ... Ah OK, so this is where I think that we're talking past each other. When I say "testing", and the reason I focus on making them automated is that when I sit down to write tests, if I'm really up to it I get myself in a mood where I'm thinking of what can go wrong. Doing that is what I'm suggesting might have made the difference. > This is where you are wrong. Python has what I call an opt-in type system meaning it's optional and it can be as strong as you want it to be. You choose the external type checker like mypy and those type checkers can be as powerful as you want. ... > > The python type system can even do Haskell/Rust level exhaustive matching on sum types: https://tech.preferred.jp/en/blog/python-exhaustive-union-ma https://tech.preferred.jp/en/blog/python-exhaustive-union-ma... > So this comes back to me not writing much python -- but I have used typing + mypy briefly a while ago, but I'd need to see some claims it is nowhere near as powerful as Typescript/Rust/Haskell. I'm not a python + mypy regular user though, so I don't know where it's limits are, but particularly with the haskell comparison, it does not support GADTs (https://github.com/python/mypy/issues/8252 https://github.com/python/mypy/issues/8252). Also, I know it doesn't support Rust's affine type system (ownership/borrows). It's true that mypy could in theory do this, but it does not, and I do not expect it to anytime soon (backing for the other projects is more sound anyway). I personally believe NodeJS is the best of the "scripting" languages, but that's a whole 'nother discussion. > So modern python matches these language in type level power, it's just optional so it's not as pervasive as typescript but the option is there and when utilized it's equal if not better in power. But proof based coding isn't solely just on using types to prove out your code. I just used idris as an example of an alternative path. The reality is Idris is a bit too extreme because coding those types takes too much effort. > > Basically i change my coding style to both minimize errors and allow me to build a minimal proof in my head. I use coding style, proofs in my head, and a strong/versatile type system all in conjunction to minimize error rate to low enough levels that tests are less important. I agree on being flexible, I like using Haskell/Rust because of this as well. I certainly disagree that python matches any of those others in type level power though. > You can't do integration tests without using infrastructure and spinning up processes. Let me put it to you plainly. It's not ME who thinks it's difficult. It's the majority of the developer population knows it's much more difficult then unit tests. That's why such a segregation even exists. The fact that we categorize the tests into integration tests and unit tests speaks to huge differences between the two in difficulty. > > You may think integration tests are easy, but that's just your own opinion. This is different from the overall general opinion which is more inline with what I'm saying. Regardless of what other devs thing of it, it's worth doing IMO. Here's the project I linked before https://testcontainers-python.readthedocs.io/en/latest/README.html https://testcontainers-python.readthedocs.io/en/latest/READM... > Dependent types are enough to prove a program is correct given a set of specifications. Of course not everything can be proven. But most things can. Disagree with this. For example, memory leaks. If I define "correct" to be "doesn't leak memory", Haskell (for example -- don't know what the memory is like in Idris) is going to have a hard time. There are things that are hard/impossible to encode into dependent types. > Theorem proving is the title. or aka proving anything mathematical or aka proving mathematical programs meet mathematical specs. Dependent typing is one way to go about this. There are other ways. The intended design of idris is similar to TLA+ the intention is to allow the user to prove the program correct to a spec and avoid unit tests via proof. Theorem proving != engineering, and this is why Haskell/Idris/ML languages at large never hit mainstream properly. If you try to write the kind of types that could even attempt to get correctness absolutely right, less than 0.1% of programmers would be able to use or work on your codebase. I don't think TLA+ is relevant here because it's mostly used for testing distributed/concurrent systems in practice, and is not a PL so-to-speak. > How is using a nat more safe than an int? A nat just makes it more general. https://hackage.haskell.org/package/natural-numbers-0.1.2.0/docs/Data-Natural.html#t:Natural https://hackage.haskell.org/package/natural-numbers-0.1.2.0/... Naturals (& peano numbers) are the categorical example of dependent types that are useful -- in Haskell, natural numbers are the set of positive integers including zero. It makes the -1 case impossible (i.e. fail to parse @ the program boundary), which is the point. > With something like idris you can, technically speaking, eliminate the need for unit tests all together. But this is a technical thing. In practice it's really hard to achieve this due to how hard it is to write these proofs. But the technical level of safety is operating at the highest level here. I think we're agreeing, but you just said two very different things -- most things (especially important ones) cannot be easily solved at the type level. How do you type-check a redis connection at compile time? or a database like postgres at runtime? You can't, really -- because they are inherently dynamic things. Now in the case of unit tests, I'd argue that a good type system is one of the only things that can prevent you from having to write lots of unit tests. I'm pretty sure we agree on this! > I'll tell you this: I don't enshrine the concept of testing. It's always a case by case basis. Yeah this is where we disagree -- I think this case called for it, you disagree. We agree that sometimes tests are not necessary or warranted. > Hey I know this thread got a little heated. I want you know that I appreciate your viewpoints even though I disagree with a lot of points. I think heated but respectful debates are healthy and I definitely learned something here. Conversations where people are overly polite I feel like I learn nothing because people rather be polite then honest. So appreciate all the time you took to reply. I may be clashing hard against your points but there's nothing but respect here. You are clearly experienced. No problem, always glad to have a debate, and being able to have them on HN is why I still come to this site! We can disagree, and I benefit from learning where your disagreements are (I don't there are actually many, I think our stances on testing and types are very similar outside of me just not liking in python in any meaningful way).