Comment by Octabrain
3 years ago
I am not an software engineer, but I write code almost daily for personal side projects and automation at work. I write unit and integration tests even for the most toy thing I do (this doesn't come from some kind of fanatic view about testing, but simply because I enjoy writting code and learning/exposing myself to the reasoning and decisions of certain scenarios). I understand the benefits of it, however, I find unit testing specifically tedious and boring as hell. I always wondered if it could be done by some framework that relies on something like Github Copilot: Install the framework, run it pointing to your function/methods file(s), get a file with your tests that you can perhaps adjust here and there and then integrate it in your CI. I understand the challenge due to, perhaps in some cases, the implicit difficulty of coming up with relevant tests for certain parts of your business logic but, do you think it will be possible some day, Am I saying something stupid?
This may sound weird, but I've written code professionally for 25+ years and I have never written a unit test, I'm not even sure what a "unit test" is or would look like in the context of what I write.
My job is to first consider a dozen ways a new feature might be used; then consider how it could be misused; then write a backend portion and UI for it, testing it for usability and potential for error along the way, using everything I've already thought could go wrong; then monitor it once it's deployed to see if anything actually does go wrong. After actually testing it through everything I can think of by hand, function by function, line by line as I write it, it seems totally stupid to write code to test my own code.
Not to sound like a shit, but that's what end users are for ;)
> This may sound weird
The weird part is not that you don't write unit tests, plenty of developers don't. It's that in 25+ years, you never even had the stray thought "hm, maybe all this manual work that I keep repeating might be worth automating?" and then tested it out to see whether it was worth doing or not
The manual testing really isn't very hard... where it relies on the same logic I use existing logic. Where it's new logic, particularly new UI, testing really can't be automated. Where it's not UI but server logic or DB queries, it's pretty explicit and tested six ways to Sunday before committing. I think the only true unit tests I ever wrote, if you can call them that, were to test pseudorandom behavior across distributions of things like character spawning in games, where player testing wouldn't be sufficient to spot anomalies. For something like that where you're going to have a billion variants, it makes sense to test for oddities on some subset of a million or two. In other words, tests to make sure that output fit a certain curve. Not that logic itself worked or didn't work, because if I can think of a test to write where it might fail, I'd already have fixed it not to fail under that test.
2 replies →
That’s great if you’re the only person working on the code.
Not everyone is as diligent and not everyone will have thought through/remembered all those edge cases when they happen to end up maintaining/adding to your code.
Personally I find that tests are a great way to ensure all that hard work you did thinking about those edge cases isn’t wasted. That and manually testing stuff is annoying after the first time.
Things that either work or don't, don't really need tests. Things that have a ton of edge cases... you might spend ten times as long writing tests as writing the code. In a few cases it might be worth it. Like, I have a piece of hotel software that lets rates be set by the night plus peak extra rates over overlapping time periods, and has to delineate the nightly totals for a customer stay that crosses over lots of those. Lots of edge cases. I had that in production for 5 years before someone noticed that although the totals were right, if the very last night was at the default rate after coming off a peak rate, the line item wasn't printed on the bill. It was a simple case of greedily including one more segment in what was printed. Now... would a unit test have found that? You have to know exactly what you'd be looking for first. The test would have to be smarter than the person writing the code.
3 replies →
> Not everyone is as diligent and not everyone will have thought through/remembered all those edge cases when they happen to end up maintaining/adding to your code.
Edge cases could be due to accidental complexity. In this case, the solution is to remove accidental complexity, not to freeze it forever in tests.
Edge cases could be due to essential complexity. In this case, if people are making changes without remembering them, it means they are changing code they don't understand. Sure, tests are making it easier. I don't want it to be easier.
> tests are a great way to ensure all that hard work you did thinking about those edge cases isn’t wasted
It is also a great way to ensure that your best talent is wasted on building guard rails.
Think about it: your best engineers are spending time making worst engineers more productive. And they are not even doing it through mentoring, so your worst engineers will remain where they are.
Say you have to test 15 things to know it works.
Then you make a change, that change impacts 10 of those. You now need to go back and re-test, manually, those 10 thing and check they haven't broke.
That's mainly why I use unit tests, as regression testing.
Also as documentation, if I'm unsure around what a piece of code is actually doing, it's helpful to see tests that hit and find something like.
When_FooIsInThisState_ThenIExpectThisThingToHappen()
Then see the setup and expected output.
It's also good for incrementally building stuff. e.g. I have 3 scenarios I want to get working, I write a test for the first and get it passing. Then I move onto scenario 2, this requires changing the code slightly for scenario 1, but I don't have to re-test scenario 1 as I've got a unit test that is running each time I change the code that tells me it still passes, and so scenario 1 is still working. Then when I do scenario 3, I know 1 and 2 are still working without manually going back and testing them...
They can really save a lot of manual effort.
> Then you make a change, that change impacts 10 of those. You now need to go back and re-test, manually, those 10 thing and check they haven't broke.
Unit tests provide no guarantee that those 10 things haven't broke. They can break in a way that's not under test. Or they can be working individually, but breaking as a whole.
You still have to test those 10 things manually before merging.
Sure, sometimes unit tests will show you regression early on. Early feedback can be useful. But it will never give you confidence, it doesn't eliminate manual testing.
The important part is that you're making a change and you don't have confidence that it is safe. This is a signal of bad design. In most cases the right solution is to fix the design, not throw tests at the problem. There are exceptions of course, but even in those cases tests are not something to celebrate or be proud of.
It's like getting obese and then celebrating statin therapy. I mean it's great, but not as great as not being obese in the first place.
2 replies →
I think if you're working alone or on a small team on a small project it's use is negligible. The added tidium may not be worth it, and many major issues can be shaken out with integration tests, QA and users.
I think a lot of people miss the (in my opinion) best use case for UnitTests. It's in-code self-documentation. Leaving a little nugget of your understanding of the functionality behind for other developers in the future. Even if a method is horribly named or the business logic is so complex it takes an hour to understand exactly what is happening, a simple group of (input -> method -> expected output) unit test can get you working with the method fairly fast.
Actually, if I am extending a giant god function that is un-tested. I find it exceedingly helpful to add some tests for the specific use cases I am adding, just to be sure I have understood the code path relevant to my change correctly.
Yeah I feel like the best reason for unit tests is to have a beginner run them with a debugger and take a tour through the code.
If this is actually the case you are not writing complex systems.
Not writing complex systems is a skill (that most engineers lack).
That's awesome to hear! I myself do something similar with my own code, but then again, I also trust myself more than any computer ;)
About how long would you say that process takes you? Do you repeat it every time a minor change needs to happen to your code? If not, how do you verify the impact of your changes?
Rough answer, I spend about 50% of my time just thinking about pitfalls before I actually write the code, 10% of the time writing it, and the other 40% testing it to my satisfaction (doing every stupid thing I can think of someone doing to break it) before I deploy.
Usually though, in practice, this looks like tracing/logging several times in the middle of each function to make sure I'm getting it right. Where unit tests are supposedly most handy is when unexpected types are passed to functions, leading to unexpected errors. The majority of proofing against that can be done by strict typing and good code documentation.
Ultimately the best test is when you ask yourself "wait, what if someone sent this" and you try it to see if you can break your own code. That's just a spidey sense in the back of your head. If you didn't have the specific doubt to begin with, I don't know how you could write a unit test to disprove it anyway.
[edit] just a note to any new coders reading this: Spend the time to always read, read, read everything about how to harden the code you write for security purposes. Unit testing will not save you from SQL injection or XSS attacks, so study those and bulletproof your work against them first before you worry about mathematical proof that your database call never results in an error under some odd condition.
6 replies →
Por eso "noduermes" LOL
If you have written a debug println you have written a unit test.
Not at all stupid. I'm sure people are already using ChatGPT to generate unit tests. There are limits, of course, given that (for now) it doesn't have the full context of your code, but it's definitely capable of generating tests for pure functions that are named well and don't require too much outside context. Some projects have tons of these.
Yes, I use it in that way. And if ChatGPT didn't generated the code with pure functions (usually it didn't), you can explicitly ask to generate the code with pure functions. Then ask to generate the tests.
Usually I get good tests from ChatGPT when I approach it as an iterative process, requesting multiple improvements to the generated test based on what it gives to me. Note that it doesn't replace the skillsets you need to know to write good test coverage.
For example, you can ask to generate integration tests instead of unit tests in case it need context. Providing details on how the testing code should be generated really helps. Also, asking to refactoring the code in preparation to make it testeable, for example, or requesting to convert some functions to actual pure functions, or requesting to refactor a piece of code it generated to a separate function. Then you ask to generate tests for normal and also for boundary conditions. The more specific you get, the chance of getting a good and extensive tests from it is much higher.
Having the tests and code generated by ChatGPT really helps to catch the subtle bugs it usually generates on the generated code (fixing it manually), usually I get test coverage that proof the robustness I needed for production code.
This approach still needs manual fine tuning of the generated code, I think ChatGPT still struggle to get the context right but in general, when it makes sense to use it, I'm more productive writing tests in this way than manually.
I used my Copilot trial to explore writing tests more than writing code. I found that it actually worked well, especially for data and boilerplate heavy tests, like table-driven tests in Go. It was saving me quite literally dozens, if not a hundred or more, of keystrokes. I wrote Go for side projects these days, not for work, so I don't pay for the license, but if I was writing Go again professionally I'd pay for the license for this alone.
In my day job, I write Ruby, and it didn't impress me much when I used it with Rspec. I'd says it was saving me maybe a dozen or so keystrokes in total to write a new spec.
What about chatGPT’s unit tests? Does it even make sense to unit test code that generates a language model?
code with output that can only be evaluated artistically can't be called right or wrong anyway. As long as it throws no errors, it's a valid language model. I can only imagine how many nulls and undefineds and Infinitys they just throw out and shove under the rug. That's actually the kind of code where not knowing how it works is okay. Encouraged, even.
chatGPT works surprisingly well for python (and am sure other popular languages too). I can dump a code bit and tell it to write the tests + fixtures for me. The tests it wrote actually showed it "understood" (or recognized the pattern of) the code too. For example part of the code renamed some columns for a data frame that was loaded from a csv. The fixture it created for the csv had the correct column names the code was renaming.
Unless you're doing TDD or chatGPT is too busy, there's almost no excuse now to not write some unit tests ;)
I wonder which is good at for GPT finally: generate impl from test or vice versa. I think former, as a noob.
Github copilot is pretty good at writing tests