Most tests are use-case based or written for checking error-handling.
Use-case tests are easy to write: you don't even need to be a programmer (in fact, it's good if use-cases are defined by a Product Owner or Tester), though of course some cases are only known to the programmer as they're the only ones who dive into the details. The programmer should come up with all use-cases the PO missed, of course, and judge whether or not they need to test those too... sometimes it's ok to not test as the cost-benefit is low. Anyway, once you have this use-case based test mentality, it's very easy to write the tests (using a proper language to do it is important! Don't use just JUnit if you're doing Java as it will be really tedious to write and you will stop midway - I know, I've been there... I highly recommend using Spock, though other frameworks to make writing test pleasurable exist).
This applies mostly for "integration tests". For unit tests, hopefully you don't find them difficult to write?! I find them quite easy to write since I know how to write testable code, which takes a while to learn but once you do, it's really easy.
If you have examples of difficult to write tests, I would be curious to see it! Perhaps we can discuss how to make them easy.
For example GUI testing is IMO still an unsolved problem. Maybe AI will help there but existing solutions are generally not worth the pain.
I work in silicon verification and the testing we do is way way way more thorough than software testing, for obvious reasons. Do you formally verify your software? Unlikely.
I can only assume you work in an easy-to-test domain on a project that doesn't have changing requirements, like... I dunno a C compiler or something.
You're dismissing my claim by basically saying I am naive. Which is not an honest argument as you know absolutely nothing about me (and I don't want to tell you more than I did here).
About changing requirements: what does that have to do with testing at all? If requirements change, you basically discard the tests for the old behaviour and start over...
I would say that the testing we do is very close to formal verification because it's close to being comprehensive - though no, we do not use methods normally classified as such. I tried to but the benefit we would get over our current approach would be negligible.
By the way, I do a lot of UI testing, and dare I say it: yes, it's easy too.
We use this sort of thing if you're curious: https://gebish.org/manual/current/#pages
Again, if you find a real example of something you find hard to test, let me know so I can evaluate it against my own situation.
To be fair most projects don't care beyond "rollback the database and maybe display a 5xx error or something". But some do. Anyway, use cases are fine. It's the failures / edge cases that cause pain.
interesting perspective - why do you think this is a bad thing?
to me, it's an opportunity to verify that the change is intended. without it, how do you know that the program does what it is supposed to do?
Thinking of it as a leaky abstraction helps me.
I try hard to separate domain logic tests from implementation specific tests.
Your code could be loosely coupled with high cohesion, but with lots of random tests like you get when code coverage is a performance metric, you have to add a lot of complexity that only relates to an implementation.
People then get used to just blindly updating the golden images. It becomes basically "the output changed, do you want to continue anyway" which is not the most useful thing. You really want it to say "the output is wrong".
I don’t want to write tests for everything. I just want to write the ones that matter.
TDD is _about_ writing tests that matter, but most people think it is about writing all unit tests first.
If you are following TDD anywhere close to the way it is described, you will only be writing tests that relate to domain functionality first.
Note how it is described here, although it is turse.
https://martinfowler.com/bliki/TestDrivenDevelopment.html
The coverage metric as a goal writing style doesn't work for TDD, sorry you were exposed to that.
You are correct that model doesn't work.
Coverage is not a goal of TDD, but in practice you will have 100% coverage by following TDD as you would never have reason to write code that isn't covered by test.
Ultimately, the purpose of coverage tools is to let you know what you might have forgotten to clean up during a refactor, to help you remove what you missed.
More importantly, why are you writing any code for things that don't matter?
Another way, if you know what the code is supposed to do, why write it down in two places?
This would be like criticizing double-entry accounting by asking "if you know what the amount is, why write it down in two places?"
We write the code down in two places because that gives us advantages that far outweigh the added effort:
- Once written, your test will catch regressions forever
- A test is often excellent documentation on what the code does
- It's now much easier to refactor the code, making it more likely that it will be refactored when needed.
We are all terrible at writing tests. We just find our own ways to do it.
But yea 9/10 times that a snapshot test fails, it's noise rather than signal.