"Tests make it much less likely that you're going to change the code underneath them" I'd say it's the exact opposite. Maybe on a project where you're one of the original devs and know the ins and outs of the code base. Speaking from my own experience, joining a company that has no tests or only a few tests makes it way more scary to make changes than with a code base that is fully tested. Especially on larger/older code bases. I have had that situation 2 years ago where I joined a company with almost no tests and 100k+ SLOC spread across 4 services. I'm talking about the backend side of things (UI was a separate repo). Even after 4 months I was still afraid to even think of impactful changes to the codebase as anything could break. In contrast, joining a company that has a "No PR without tests" attitude, I could affect changes that would impact the whole codebase after just a few weeks on the project. Being able to click a button and know whether my change affected or has broken something in a distant part of code that I might not even know exists, is priceless. I freelance, so I worked with quite a few companies over the last decade and from what I've seen the ones that have a well tested codebase are the ones that change/improve the fastest. The ones with little regard for testing, the team attitude is usually something along the lines of "if it ain't broken, don't fix it". All because most people are afraid, that if they try to improve it, they might brake it and upset the stakeholders.
Yeah, I agree, If you write the test first, you will end up with code that is easy to change because it started out easy to test and the tests allow you to move fast because you verify that you didn't break anything by just running the tests. However, if you write tests after the fact, it's likely that your code is not written in a way it's easy to test. Then writing the test may make your code harder to change.
@@Tekay37 I'm not so sure about the write test first vs after. I'd say it's true for a beginner. Once you write a few thousands of tests, you will already know how to avoid pitfalls that make the code hard to test. You can incorporate all of that experience into the code you write without having to have a test keep you in check. Whether you start with the test or code depends on the issue at hand, I don't think one or the other is a silver bullet for everything or even a good general default. For me personally, I think it's about 50/50 whether I write code or test first.
unit testing everything is like getting stitches for a cut and then putting a plaster cast to make sure the stitches don't move. if you end up with an infection, you'll have this big fucking cast preventing you from treating that infection.
This is one of the few things I think Theo is just dead wrong about, and I kind of wish he'd make it more clear that this is his opinion rather than talking about it like it's objective truth.
Unit tests should be on higher abstraction level than implementation. Tests should not know anything about implementation. TDD is the approach to avoid any knowledge about implementation because its not written yet.
"should", but not "must". Sometimes it is useful to understand some of the implementation details. This is just Chicago vs London style testing and there are benefits to both approaches.
@@havokgames8297not sure i would agree. the purpose of the higher level tests is to make a stable public api, after which you can change anything underneath even to the extent of keeping the header file and throwing everything else away. you can also test your private code, but it should already be covered by the high level tests, or be dead code which is never used.
I don't really get why anyone would consider unit tests a waste of time. Also other kinds of tests are incredibly useful especially in library code that is evolving so that you do _not_ break your dependencies by accident. I've had unit tests save me multiple times in my career where a simple change yielded a different result in what might be considered edge cases, which were nevertheless relevant.
In general yes but I often see developers mocking some sort of service and then testing that their mocked values passed in were indeed returned correctly etc. Way too many developers waste time on pointless tests just to get coverage up.
@@attila2246 yeah or testing specific functions take in x arguments and return y. If you're a library that's probably important, but we focus on API tests. If I hit this endpoint with this data/when the app is in this state then I expect to see y response. It gives you far far more freedom to refactor as you are ultimately just testing the backend contract itself works as expected. Having unit tests that are too focused on implementation specifics is what Theo is arguing against. In that scenario you have to change a lot of tests when you refactor or move stuff around, which impacts developer velocity massively and makes it harder to tell if you've broken anything important or not. Not much of value is gained by checking if sum(a,b) is passed 1 and 2 it returns 3. And if you move sum into a different class it breaks all the tests for no gain. Having thorough API tests means you can do a big refactor with gained experience in the requirements and when all the tests pass you know that everything is working as it should.
welcome to library dev typescript is a hindrance, you will program the same program 2x, once for typescript type flows, once for the ackshual logic (yes your function affecting your type, inference!). It makes refactoring such a headache as you have to refactor two sets of logic as your library gets more complex in behavior unit tests become more and more inherent and it has nothing to do with types. but that is a different talk for a different day.
This ^^ goddamn typescript and it’s shiny facade. Unit testing (behavior of inputs and outputs of a unit), is critical for any code base. No one can convince me otherwise. The stress you feel when you make that “simple” change in a legacy code base without tests is never worth it. It’s not that hard, it saves you headache, it saves your company money, and other devs will thank you for years in the future. We don’t need to teach new devs these bad habits man.
I use unit test on raw sql queries since they aren't typed, I run them and check with Zod three things : - Query actually runs - That every column is correctly named and none are missing - Type of each column of the first row Very very cheap to write, I get the types of query with zod inference
which is why continuous integration uses regression testing of the public api, admittedly implemented with unit tests, rather than a unit test everywhere approach. it is also why it ends up with near 100% coverage, as the public api should be stable between major versions.
this is why continuous integration uses automated regression testing of the public api, rather than a simplistic cover everything with unit tests approach. as your public api should be stable between major versions, the tests don't break, or if they do, you find out before your users. because your public api is stable and does not expose internal details, you do not need to write tests for your private code, as it already gets called with yhe existing tests or it is dead code and can just be deleted.
Well, fixing bugs is much much harder when you have corrupted data in the database as a result of a bug. Then it is not just a simple code fix, it is also "God help me with migration scripts to retroactively fix the data if possible". If not then business cries in pain.
Anyone who thinks a team that skips unit tests will be faster than one that doesn't has simply never measured it. The problem is that it does take the whole team doing it to get the benefit. One person not writing code with unit tests in mind drastically increases the time it takes for everyone else to write them.
I have different opinion on testing. I started my career hating testing. One day a backend dev in my old company told me hey look I have developed this feature "in the dark" What do you mean in the dark? I asked He told me he had develop an endpoint without opening the app. He had been writing the tests necessary to cover all requirements of that endpoint. That was an aha moment for me. Developing specially backend code allows you to discover all the cases iterating on it and knowing instantly if you broke something. TDD for endpoints makes a lot of sense imo. So I would say tests are super good not only for libs but also for apps. 1- Gives you confident for iterations while building 2- Gives confident the next dev touching that code when making changes
To be fair, that's not describing unit testing. In the same way I work on a text editor and I do integration testing of the text editor against a mock API. Thinking unit testing has bad ROI is very compatible with thinking that automated integration testing (both end-to-end, but also of different 'parts') can make perfect sense.
"Unit tests are the foundation of reliable software; they give you the freedom to evolve and improve without fear.". Unit tests for regression is VERY useful.
Regression and integration tests (end-to-end) are a lot more useful than unit tests in my opinion. We caught so many bugs with those that were not caught by unit tests before we deployed to production. So yeah, I get where you are coming from.
end to end tests are not usually integration tests, but are instead acceptance tests, which serve a different purpose. unit tests and integration tests are used by continuous integration to regression test your public api. they tell you if you built what you intended to build. acceptance tests are done after continuous integration is done, and tell you if what you intended to build is what the customer wanted. as most typical projects have 60% of the requirements being unknowable until you start building it, knowing this difference matters. i tend not to see many tests which have high utility which are end to end or ui tests. they are needed for regression testing, but tend to be both slow and fragile.
Tests are there to describe what the system is doing. It's super useful to prevent regression errors but also serve as a documentation for new devs. I would not feel safe pushing code without tests because if it led to a bug my company would be sued and patients could potentially die
I'd say documentation describes what the system is doing or it should and tests audit the documentation. Some test are test of a built in test to make sure the test works. Those are needless.
In my experience tests are great for backend code, but seldomly worth it for frontend code. In the backend you don't have to manually test it if you have done good automated tests, while in the frontend how things looks and feel is to complex to put into automated tests. Furthermore unit tests have as you say a tendency to put the structure in concrete, which is why I strongly behavior driven tests that are tests from the abstraction level of the user and thereby being implementation independent. There is nothing better that being able to restructure a big part of your code not torching the tests.
I find FE tests actually very useful. Maybe this applies to bigger projects, but having good tests to make sure you don’t break core functionality of your pages helps with regressions. Also having snapshots help when you have internationalization to make sure strings render properly. I work with government clients, so introducing a regression could impact thousands of citizens getting benefits or applying to them, etc.
idk man, i'd really like to use the excuse of "this library & service just randomly stopped working" to get the rest of the week off, but you're ACTIVELY preventing that from happening
I don’t agree with this. Unit testing has mega value as a consultant in a corporate setting. People come and go all the time and business rules change. Unit tests help naive developers not break business rules and helps people understand the intention of code; this coupled with documentation. I’m happy that you don’t feel the need to unit test your code, but I don’t think you should encourage others to not write them. I think of unit tests as an insurance policy that prove the I/O is accurate.
thanks you for this answer! A unit test is part of the documentation, explains what and how the unit should work, and what is important. If somebody does not write good unit tests then will end up like the youtuber itself. He/She wont understand why testing is important
Around a year ago I learned about and wrote unit (function) tests for the first time, and went totally ham on covering all C stdlib functions like strlen() and memcmp() that I had to recreate as my library for university. These unit tests were invaluable, since I had to use this library for a ton of later C projects, and it would've sucked if I failed one of those later projects due to a bug in my stdlib. A later project was to recreate Bash, and it would've been absolute hell to unit test every function in there, since the functions in there serve as intermediate steps, rather than direct input -> output, like my stdlib library. Instead, I wrote 260 integration (program) tests to make sure that for example the Bash stdin input `echo foo > foo.txt cat foo.txt` gave me `foo` as stdout output, and those 260 tests were essential in finding numerous critical bugs that would've gotten me an F on the project otherwise (very draconian, I know)
Can't agree to any of the points you mentioned. Here are my takes: 1. Test doesn't prevent people from changing code. Instead, it allows them to change code, and verify the change at the same time. 2. Test does affect speed, but being unsure about how your change might impact a complicated codebase affects speed even more. A well tested codebase gives confidence that your change didn't break anything, and your test case gives confidence that the code you wrote does what is is supposed to do. Improved confidence = improved speed. 3. Test doesn't cover a shitty codebase. It supports a strong codebase, and prevents others from accidentally making it shitty. Overall, If you are the solo developer for a non-complex codebase, you can go away without tests, as you know every aspect of it. If you are just starting to build a system, and the requirements are unclear (and can change rapidly), you can avoid adding tests until the requirements are clear or you are ready to launch the product. But if you are working on a complex system with many developers, tests is a MUST.
Yeah…. But tests are there for other reasons…. Im currently refactoring a rather old codebase. Quite a lot of deeply nested code, many conditionals, lots of coupling, the multiple responsibility principle was applied to everything. I can’t just inject dependencies or mocks, because the system wasn’t built in a way to do that or for that to be useful. There’s also no unit or integrations tests. There are some limited regression tests. Im trying to refactor that code so I can modernise it and upgrade it. But it’s really really easy to accidentally break functionality… every change has subtle effects. I have to maintain all functionality, even the bugs, because who knows what relies on that bug. I have to build tests to verify the existing functionality first, so that I can then make the changes I need to make, and then use the tests to verify my changes didn’t break anything. Had the tests been there in the first place, the app probably wouldn’t have been written the way it was, and I wouldn’t have to do double the work because some previous develop didn’t take time to write tests. Though back when this was written, unit testing was less common… so that dev get a pass. Write tests! Future devs (possible even future you) will thank you!
Agree with how you said many people use tests as concrete to fix an inherently flawed codebase. However, tests really start to shine for complex codebases.
4:06 "we usually have them fixed pretty quick, on average under 7 minutes" factoid actually just statistical error. average production bug takes 20 seconds to fix. Buggers Georg, who lives in a cave, programs in PHP, & created a bug that took 3 years to fix, is an outlier and should not have been counted
Not this again... Many (most?) people work with mountains of shitty code and plagues of shitty devs. I assume this is why so many people, including myself, find this take so strange. Also sounds like Theo has only worked like 1 job alongside a small number of good devs. Or he has little experience working on BE applications.
Yeah, I agree with this. I’ve just come off a code base with 100% unit test coverage - the code did almost nothing yet somehow broke all the time in the real world. I think I see this concrete-over-bad-code approach when there is a lot of mutation that isnt isolated to where it’s needed. If you have your mutations isolated testing the io on that part can cover almost all cases usually in a few tests.
I sometimes like to take a TDD approach with specific features if I think running the test will be faster than manually testing the feature as I'm building it (and only manually testing it at the end). Like I had some new forms for a site I needed to setup and I found just writing a test to ensure the POST request was being handled properly and running that was much faster than manually filling out the form. But I do agree that the test itself was not necessarily that valuable to keep after the feature was complete.
I think it depends a lot of what you are building. If you are building a frontend application or a backend application or a library are totally different ways to build. In libraries and backend, with framework agnostic code, TDD works great. In the frontend, it is a total different world. In the frontend, E2E tests are more interesting than unit tests. Unit tests are useful for small parts in the frontend, with specific functions that isn't frontend specific. If you do something with the DOM, what I think are side effects, it is difficult to unit test.
Without unit tests, you absolutely should run manual tests, which should include a written test plan. Having a person do this work repeatedly is expensive. If in the future you want to change out a component for a different implementation, existing automated tests ensure the same behavior. This tightly couples the dev experience rather than waiting for QA to test manually for bugs and then fixing and testing again. You're also encouraged to change your automated tests as you address new behavior.
At my job we have lots of unit tests because our code is in production and we WANT to force devs to work slow and prove that they're not breaking out LIVE and PROFITABLE code before they commit to main. I spend almost all of my time improving legacy code so that it will be easier for the next person to work with and add features to. But when they do add features, I want them to be confident that their additions will not break existing features. And I want them to be able to be confident without having to deeply understand the entire code base. The metaphor of "covering everything in cement" is pretty good. Even if the code is "bad", if it is working, and being used by thousands of people every hour 24 hours a day, then maybe covering "bad" code in cement is a good idea. You can still improve it. You just need to be more slow and careful about it.
I never understood your stance on unit testing, but I think you actually made some really good points. Based on other comments, it seems the problem is most of us write garbage code, and if someone is the exception, they're basically guaranteed to work with other people who do write garbage code. Most unit tests are also complete garbage. In fact, unit tests are basically a giant dumpster fire, but give off a warm cozy feeling. Big respect for standing your ground and trying to increase understanding.
recently i started writing unit tests on an ad-hoc basis for things that are just annoying to test by hand. All development requires SOME form of testing, it's just a question of whether you automate it or not. So happens I work on a project where performance in local dev environment is MUCH worse than performance in production (due to increased latency to remote db and 10+ years of poorly structured DB queries). And the majority of my work is bugs or enhancements deep in the system. So the TDD way is just way faster and easier in a lot of scenarios. Even if I spend two hours writing the test, if I have to conduct 20 tests to eventually get my code right, and one way takes one minute per test run and the other takes 20 minutes per test run (yes I had a task like that recently, re-running a job over and over), then i'm still way ahead (2h20m vs 4h). Not to mention, the time I spent writing the test also helped me understand the problem better, which likely means less time spent on the solution itself.
the thing about test is that you should test properly. mocks are what make a codebase rigid. if you test only usecases having the system under test as a blackbox you will actually benefit from it
It depends how serious a bug would be. For critical sections/applications, test as much as possible. Otherwise, yeah, broader, implementation-agnostic tests that ensure functionality isn't breaking are enough.
I currently work on a project that has many critical (and complex) business logic to determine who can access things. If we didn't have tests for that, oh dear, I'd be PARANOID to touch any of that code.
Ive had changes pushed to production where i work that, if we had proper tests (which ive been pushing to get people to focus on...) would have been caught easily, but werent because so many things ran through the same internals (especially around auth and events) that changing anything there always risked breaking something because so much relied on those few sections of the code. When someone makes an optimisation for one case and it breaks another pattern they didnt even know existed, you're going to have a bad time if you aren't running unit tests. honestly it is wild that a popular tech youtuber is actively recommending against testing for most projects. If every path in your code has basically zero overlap with other parts testing may be less useful, but the more important certain parts of the codebase become, the more important it becomes to make sure that those parts don't start breaking when you change something
Год назад+1
I think unit testing is great to assure important results if the path to it is not very clear.the rest needs observation tools and e2e testing
I haven't looked into the code base, but these tests actually feel more like integration tests for the library rather than unit tests on a class or module basis
Unit tests are not strictly locked to classes or modules. I would classify an integration test as anything that reaches out from the code. Something that make request to a server or interact with a db. Unit tests would be anything that stays within your code base
This perspective on unit testing is fine for a relatively small, specific code base like you have, but it gets problematic when you start building large-scale software or working on big, cross-functional teams. Unit tests don't simply insulate the software you're writing (maybe they do if you have a tiny repo) they: 1. Ensure the contracts and functionality you've written are documented 2. Those contracts look the same even when some other developer makes a change 3. Guarantee compatibility across multiple scopes (use in different classes, different configurations of an object) Similar to your argument for TypeScript for library devs, I can see different use cases here. Saying "We only had a few problems tests caught" to prescribe this ideology to others is uh... not great. Probably best to add some nuance to your argument so any fledgling devs don't simply opt out of unit testing entirely or even general automation because they think it's such a niche use case. I do agree, however, with your point about writing for code coverage being one where you lose the plot. That type of testing absolutely stinks. Most teams need maybe... I'm spit-balling, but about 15-20% of their code covered in unit tests. You want all that business logic (calculations, form manipulation, etc) or those guaranteed contracts (api object properties) to be the exact same every time you ship. That's about it.
I agree, you may not need unit testing if you are confident about your skill and trust your team's skills. But you can't do that in a medium/large scale company. There are many developers at different levels, especially there are irresponsible devs that will do bad code that you can't ask them to make it right easily or you just have to accept and move on with it because it just works and the deadline is coming. The only thing that keeps you confident in this case is the good unit test implementation.
Never seen someone say so much crap with so much confidence. All I can say is this is pure sensationalism with the goal of getting engagement at the expense of noobs.
A unit test or a test in general is part of a contract that you will set up before coding. That contract describes the inputs and outputs of that code block and their boundaries. It doesnt really matter if you write the test before or after writing the logic. What matters is that the next guy or your future you after along time after you wrote the first code, trying to make changes to your logic that now have a guard rail that will save them from braking connected code or even the whole executable from crashing. This goes against the philosophy of writing everything as a test before writing the logic as I think that it is over-engineering and it wastes a lot if time. It also presses you into a thinking cage where you are not free enough to make changes in your mental code model anymore which totally limits the creativity that is needed while writing or designing code.
I think one day I'll have the same view, but I'm still new to a lot of things and tests help me catch mistakes and build experiences I'll rely on later when I run into random issues later.
Good stuff. I'm with you. For super complex functions that can't or shouldn't rely on just plain typescript for functionality, unit tests are quite good. That being said, I wrote one test in total, and that's just to make sure that when I'm gone, people don't go breaking my error parser. majestic piece of code, but typescript wasn't happy with my recursive type definition, so I had to "lie" and just make an overload function that provides the correct types (object keys inferred from one type, passed into another), and write the actual logic with fake Record because I was done with having 10 "as X" in a 30 line function. I wouldn't call it shit, but it's definitely one of those "pour concrete on it to make sure it stays in place" situations. I wish the codebase was set up for other types of tests. but before this, the only tests were for an email validator (didn't have zod before me lol) and that sort of stuff. And I'm too busy getting rid of useEffects and replacing them with react query to write tests :P
Great Video and good takes Theo! My testing philosophy in general is: test the interface that you are providing and focus on the non-happy paths. When you build a webapp the interface is the UI for the user in the browser, so maybe Test that with playwright or cypress, unit tests are often causing the exact problem you're describing. When you bild a library, your interface is the API so test that with jest or vitest. The UI/API your seeing daily while developing does not need to be tested, but when there are critical edge cases that need to function but are uncommon to stumble over during development: maybe worth testing these. Also when building a library, working towards a ~100% coverage badge is kind of a marketing investment 🤔
I hold similar views, test as far as possible. e2e tests only, I want my "website" to work, this gives me some guarantee or path to it. Unit-tests cripple you psychologically, you will be second guessing where it should be relatively clear to get idea of whats going on while "inside" your app.
But if the refactoring changes the interface, you have to modify all your tests accordingly. So unit tests (especially if you write tests for every single class, no matter how small or how trivial its functionality) can also hinder refactoring.
@@ReneHartmann Not really? You keep both implementations and get the result from both. Ensure they get equivalent results, then you can replace the old implementation and use the new. I get what you mean, but if that's a problem then either the interface is/was awful or you're thinking about tests incorrectly. Although tests will obviously need to be rewritten. That's the case for most changes. Add or remove something and that's always the case.
I have found that Typescript makes some broad assumptions. My guess is when you add the Request type to the parameter, TS is inferring that all functions in someKey must be of the same type and take the Request parameter, and this is the inferred type for func and otherFunc.
I personally don’t like unit tests because the scale is too small. Generally the only time my unit tests fail is when I first write the code, and when I change the expected behaviour I love acceptance tests. I’m fully capable of finding where shit went wrong
I generally unit test when my algorithm has regression or multifunction calls, api fetch async function and enum variated path to follow, generally error management. I don't know safety net but I like static types and good error management. Guardrails just work for me. Whenever I have a function output something that maybe of multiple types, yeah I run down to unit test to check for panic cases. You can probably tell I'm the rust nerd.😂😂😂
I'd happily write tests on someone else's time, on my time though I have no need or time for them. I've been coding for about 7 years and not once have I ever said, "I wish I had a test for this". The only reason something will break is if something unforeseen happens, and you can't test for what you can't see. Finding the source of errors takes almost no time at all, doubling your workload to save a couple of minutes once in a blue moon just isn't worth it. I get why it makes sense for a large company who have people of different levels coming and going, and they can sell it to the non-tech people as "fully tested". They can also justify doubling the amount of work, which is desirable in many corporate circumstances, so it makes sense that it's industry standard practice, it just doesn't provide me with value for money as a solo dev.
Totally agree with the right balance in unit testing, however I don't agree that tests inherently lock in current implementation. If one writes tests in such a way that they assume nothing about the inner implementation of your code, ie. one is supplying only inputs and expecting outputs, it's usually pretty easy to make changes. Which can be very useful when you are refactoring shitty code, since the tests give you something to lean on while refactoring. But yeah, it takes a lot of experience to get the tests right and if you don't just double the amount of your code problems. Other than that, we rarely use unit tests too in well maintained projects. I personally hate mocking and artificially setting up contexts and I avoid it wherever I can. That's the reason why only few % of our tests fall into the unit category, and why we keep the overall test amount at a minimum (screw 100% test coverage).
I like the C style of "testing" where you just throw asserts everywhere to say what your expectations are on how the code should be used. Unit tests make you want to implement solutions to hypothetical edge cases that should never actually happen in well designed system and it's all a complete waste of time
Is this "philosophy" of adding asserts in the codebase coming from C? I started using asserts and honestly it's pretty nice. It made me think about the edge cases right in the code and rule out the cases that can never happen and deal with the rest.
Spoiler, I hate tests and writing them. But like with everything in this world, everything has it's place and time. I got another example: You're hacking away at some feature F. This feature takes some input and produces some output. It can be whatever; a cli command in your application that reports some information, for instance. You hack at feature F - and you've come to the point where it produces output (from say, your string input, being a command on the CLI). It's V1; you know you're not done, but you're producing correct output for normal (sane) input. _This_ is one time when you write the test. Because you've been hacking on the feature so far. It produces output that seems correct. Now you want to actually refine what you've worked on. You know the input parameters and what the expected output parameters should be, because you've hacked/programmed/developed the feature. When you start a reiteration of this feature, for instance because you want to improve performance characteristics, or just general design things, having this test makes it much easier to see if you're still on track with the expected output - you compile, you run the test, and you see if any of your outputs are different after the re-factor. And after every small re-factor, you rinse repeat. This will save you huge amounts of time.
I understand the whole "solve the bug quickly" rather than spending a lot of time unit testing. But how about mobile development? Like.. in mobile development if there is a bug, it may take up to a week for the store to approve your update, and then not all of your users may update to the latest version either (unless you force them to)
Okay, after reading comments on other videos on this channel, I came to this one in hope of a topic where I have a differing opinion. Not actually. I too believe unit tests should be contextual. The amount of required unit test code should vastly vary depending on what your code does. You should not start with a unit test before writing your first class or module. Instead, give your application some time running in the real world, understand the nature of issues that impact it, and finally conclude on what portion of your code and what kind of testing will give you the best value in terms of effort required to write those tests vs the effort that will be spent otherwise to maintain the code without tests.
I will point out saying unit testing would’ve only fixed 3 bugs for you guys, that is almost 50% of your bugs eliminated by unit testing. Just food for thought
Imo. most people who are hating on unit testing are not doing it right (ie. Making it unnecessarily difficult for themselves using poor tooling or architecture) or they’re forcefully unit testing something that doesn’t need to be unit tested. Unit testing when done right should always be valuable, that’s the whole point of testing. There’s tons of bad unit tests out there. But there’s also tons of great unit tests. Unit test specs for ie. parsers, compilers, bundlers, etc is absolutely crucial. So when you say most tests you see are like cement trying to keep a crumbling structure together, all I’m hearing is you haven’t worked in the right type of projects. In general I unit test my core libraries, integration test my ui component libraries and acceptance/e2e test my applications. Use the right tool for the right problem.
Good tests should make it easier to change a codebase. Ideally, they should be able to document the expected behavior. On a legacy codebase, I have a much easier time if it comes with decent tests. It's as helpful as good typing and can be just the missing piece in the puzzle to get the whole picture. But I agree with this: tests usually make more sense at a bigger scope than the unit, so e. g. component tests covering user interaction, or integration tests towards a quirky API that can't be changed for compatibility reasons. Not code coverage counts, but test quality. Stepping a bit out of the topic: I think devs should constantly refactor their codebase as requirements change. Don't prematurely abstract to cover possible future use-cases (yagni), don't try to prevent the need for future code changes. Instead keep code as simple as possible and constantly adapt to what's needed right now. Good tests can help with that, also good typing. Improve both along with the code changes. Bad tests, as well as bad typing, can prevent healthy code changes. Evolving code, typing, AND tests keep your team's mind fresh and confident. It's neither the fact of unit testing or typing that's bad for your code quality, it's any part of your codebase you're afraid to change, for whatever reason.
Unit tests assert the behavior of your code. It verifies that it works initially and gives developers confidence that it still works after changes. Typescript is not a substitute for unit tests.....
You know I've never agreed with your stance on unit testing... watching this I realize it's because I work on a platform team, and almost all the code I write falls into the category of what you're talking about here in this video. It also made me realize a lot of my application work is colored by all the time I spend writing library APIs and is probably over-engineered for what it is. TIL. Ok, you got me, I get it now :D
I respectfully disagree. I learned about the cost of not testing very early on. Intern working on a Ruby on Rails codebase. Read an entry that has a JSON object and send one of those fields in the API. Simple. Most of the site product pages became unavailable because most products (outside of my team's scope) had a null value instead of an empty object. I just had to safe navigate. Oops. Or when I was writing a robust generic wrapper for a crappy API. Write once use everywhere. Not only did tests help me catch my own mistakes, but they later serve as documentation as to how the module was used. Reading a test file often makes understanding the original source easier. That said, I agree on the point of coverage and "useless" tests. Dry "assert-like" unit tests that just check the internal state of a module are terrible. BDD, even as simple as jest's describe/it, on the other hand, are the way to go.
When I heard you were developing libraries my first thought was whether they would be tested haha. Glad to see you are though. Out of curiosity, do you do e2e tests like Cypress?
Typescript provides only limited safety, as it doesn't exist in runtime and only works within a single repo. TS doesn't help with external APIs that may send anything, or even with own frontend/ backend operations unless you're using something like trpc. It's great to have, but actual guards that exist in runtime seem more important.
Good written tests are a boilerplate for new engineers/developers to understand the exact context/usage of a particular use of code. "tests make it much less likely, that you're going to change the code underneath them" - wrong, tests with good coverage make you much more confident that changing code won't break the application..... "If tests are preventing fast movement, they are also hurting your ability to fix things when things go wrong"" - again, wrong - something goes wrong in production without tests, you'll be wasting time finding/debugging/fixing the bug a test would have caught originally....
this is something i say a lot. you have a collection of tools. unit tests are one of those tools. every tool is for a specific job. you cannot use them for everything. you _shouldnt_ use a hammer to try and drive in a screw. you shouldnt use a fork with soup. there are a lot of tools at your disposal as a programmer and i think we are still kind of in a wild west period where a lot of people, including senior developers with decades of experience, dont actually fully understand the right and wrong places to use all of these tools that are available. there's so, so very many, and so, so very many conflicting opinions on all of them that there's just no way it's possible for anyone to have a full understanding of everything and all of the best use cases for all of it. and what anyone needs to do as a programmer is question what and why other programmers are telling you are the right and wrong things to do or use and decide for yourself if their arguments work for you (or your team) or not. for a lot of people, unit tests DO work for them, and very well, but for others, not so much.
First the moustache, then unit tests. Theo is slowly turning into primeagen
what's next? vim? ru$t? ts hate?
@@Dev-Siri humor
@@Dev-Siri TOKIOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
Next: spend a few months configuration nvim so it will have similar features to VScode.
all he needs is 4 kids now
"Tests make it much less likely that you're going to change the code underneath them"
I'd say it's the exact opposite. Maybe on a project where you're one of the original devs and know the ins and outs of the code base. Speaking from my own experience, joining a company that has no tests or only a few tests makes it way more scary to make changes than with a code base that is fully tested. Especially on larger/older code bases.
I have had that situation 2 years ago where I joined a company with almost no tests and 100k+ SLOC spread across 4 services. I'm talking about the backend side of things (UI was a separate repo). Even after 4 months I was still afraid to even think of impactful changes to the codebase as anything could break. In contrast, joining a company that has a "No PR without tests" attitude, I could affect changes that would impact the whole codebase after just a few weeks on the project. Being able to click a button and know whether my change affected or has broken something in a distant part of code that I might not even know exists, is priceless.
I freelance, so I worked with quite a few companies over the last decade and from what I've seen the ones that have a well tested codebase are the ones that change/improve the fastest. The ones with little regard for testing, the team attitude is usually something along the lines of "if it ain't broken, don't fix it". All because most people are afraid, that if they try to improve it, they might brake it and upset the stakeholders.
Yeah, I agree, If you write the test first, you will end up with code that is easy to change because it started out easy to test and the tests allow you to move fast because you verify that you didn't break anything by just running the tests.
However, if you write tests after the fact, it's likely that your code is not written in a way it's easy to test. Then writing the test may make your code harder to change.
@@Tekay37 I'm not so sure about the write test first vs after. I'd say it's true for a beginner. Once you write a few thousands of tests, you will already know how to avoid pitfalls that make the code hard to test. You can incorporate all of that experience into the code you write without having to have a test keep you in check. Whether you start with the test or code depends on the issue at hand, I don't think one or the other is a silver bullet for everything or even a good general default. For me personally, I think it's about 50/50 whether I write code or test first.
@@novopl Fair point. My code style did change over 5 years of doing TDD, applying certain patterns earlier than I would have done otherwise.
unit testing everything is like getting stitches for a cut and then putting a plaster cast to make sure the stitches don't move. if you end up with an infection, you'll have this big fucking cast preventing you from treating that infection.
This is one of the few things I think Theo is just dead wrong about, and I kind of wish he'd make it more clear that this is his opinion rather than talking about it like it's objective truth.
"Why waste time testing your code, when you can just let your users test it for free?" 🙄
6:54 revealed this was clickbait, Julius wrote all the unit tests, can't believe theo lied to us
Unit tests should be on higher abstraction level than implementation. Tests should not know anything about implementation. TDD is the approach to avoid any knowledge about implementation because its not written yet.
"should", but not "must". Sometimes it is useful to understand some of the implementation details. This is just Chicago vs London style testing and there are benefits to both approaches.
@@havokgames8297not sure i would agree. the purpose of the higher level tests is to make a stable public api, after which you can change anything underneath even to the extent of keeping the header file and throwing everything else away.
you can also test your private code, but it should already be covered by the high level tests, or be dead code which is never used.
I don't really get why anyone would consider unit tests a waste of time. Also other kinds of tests are incredibly useful especially in library code that is evolving so that you do _not_ break your dependencies by accident. I've had unit tests save me multiple times in my career where a simple change yielded a different result in what might be considered edge cases, which were nevertheless relevant.
In general yes but I often see developers mocking some sort of service and then testing that their mocked values passed in were indeed returned correctly etc. Way too many developers waste time on pointless tests just to get coverage up.
@@attila2246 that's called coverage requirement bullshit
@@attila2246 yeah or testing specific functions take in x arguments and return y. If you're a library that's probably important, but we focus on API tests. If I hit this endpoint with this data/when the app is in this state then I expect to see y response. It gives you far far more freedom to refactor as you are ultimately just testing the backend contract itself works as expected.
Having unit tests that are too focused on implementation specifics is what Theo is arguing against. In that scenario you have to change a lot of tests when you refactor or move stuff around, which impacts developer velocity massively and makes it harder to tell if you've broken anything important or not. Not much of value is gained by checking if sum(a,b) is passed 1 and 2 it returns 3. And if you move sum into a different class it breaks all the tests for no gain.
Having thorough API tests means you can do a big refactor with gained experience in the requirements and when all the tests pass you know that everything is working as it should.
welcome to library dev
typescript is a hindrance,
you will program the same program 2x, once for typescript type flows, once for the ackshual logic (yes your function affecting your type, inference!). It makes refactoring such a headache as you have to refactor two sets of logic
as your library gets more complex in behavior unit tests become more and more inherent and it has nothing to do with types. but that is a different talk for a different day.
As a library developer I can say library development sucks
This ^^ goddamn typescript and it’s shiny facade.
Unit testing (behavior of inputs and outputs of a unit), is critical for any code base. No one can convince me otherwise. The stress you feel when you make that “simple” change in a legacy code base without tests is never worth it.
It’s not that hard, it saves you headache, it saves your company money, and other devs will thank you for years in the future. We don’t need to teach new devs these bad habits man.
I use unit test on raw sql queries since they aren't typed, I run them and check with Zod three things :
- Query actually runs
- That every column is correctly named and none are missing
- Type of each column of the first row
Very very cheap to write, I get the types of query with zod inference
Regression tests on a (very) large front-end code base has been a life saver.
100%
which is why continuous integration uses regression testing of the public api, admittedly implemented with unit tests, rather than a unit test everywhere approach.
it is also why it ends up with near 100% coverage, as the public api should be stable between major versions.
this is why continuous integration uses automated regression testing of the public api, rather than a simplistic cover everything with unit tests approach. as your public api should be stable between major versions, the tests don't break, or if they do, you find out before your users.
because your public api is stable and does not expose internal details, you do not need to write tests for your private code, as it already gets called with yhe existing tests or it is dead code and can just be deleted.
Frontend devs are fine with the user finding the bugs. just imagine that for systems programming or financial stuff
No they are not fine. UX design errors are front-facing and can cause the same or even worse damage to the product.
Well, fixing bugs is much much harder when you have corrupted data in the database as a result of a bug. Then it is not just a simple code fix, it is also "God help me with migration scripts to retroactively fix the data if possible". If not then business cries in pain.
Anyone who thinks a team that skips unit tests will be faster than one that doesn't has simply never measured it. The problem is that it does take the whole team doing it to get the benefit. One person not writing code with unit tests in mind drastically increases the time it takes for everyone else to write them.
I'm working on projects where unit tests and integration tests are the bulk of the repo. Honestly, they're a pretty good safety net for new hires.
I have different opinion on testing. I started my career hating testing. One day a backend dev in my old company told me hey look I have developed this feature "in the dark"
What do you mean in the dark? I asked
He told me he had develop an endpoint without opening the app. He had been writing the tests necessary to cover all requirements of that endpoint.
That was an aha moment for me. Developing specially backend code allows you to discover all the cases iterating on it and knowing instantly if you broke something. TDD for endpoints makes a lot of sense imo.
So I would say tests are super good not only for libs but also for apps.
1- Gives you confident for iterations while building
2- Gives confident the next dev touching that code when making changes
To be fair, that's not describing unit testing. In the same way I work on a text editor and I do integration testing of the text editor against a mock API. Thinking unit testing has bad ROI is very compatible with thinking that automated integration testing (both end-to-end, but also of different 'parts') can make perfect sense.
"Unit tests are the foundation of reliable software; they give you the freedom to evolve and improve without fear.". Unit tests for regression is VERY useful.
Regression and integration tests (end-to-end) are a lot more useful than unit tests in my opinion. We caught so many bugs with those that were not caught by unit tests before we deployed to production. So yeah, I get where you are coming from.
end to end tests are not usually integration tests, but are instead acceptance tests, which serve a different purpose.
unit tests and integration tests are used by continuous integration to regression test your public api. they tell you if you built what you intended to build.
acceptance tests are done after continuous integration is done, and tell you if what you intended to build is what the customer wanted.
as most typical projects have 60% of the requirements being unknowable until you start building it, knowing this difference matters.
i tend not to see many tests which have high utility which are end to end or ui tests. they are needed for regression testing, but tend to be both slow and fragile.
Tests are there to describe what the system is doing. It's super useful to prevent regression errors but also serve as a documentation for new devs. I would not feel safe pushing code without tests because if it led to a bug my company would be sued and patients could potentially die
I'd say documentation describes what the system is doing or it should and tests audit the documentation. Some test are test of a built in test to make sure the test works. Those are needless.
Patients dying is very different to some start up iterating quickly
@@trappedcat3615 yes there is also documentation but tests are where you go when you need to look through the weeds.
In other words, you have to write tests otherwise you will be fired for not doing your job properly.
Uh... no. Sounds like what you're saying is "unit tests done badly are bad" because you just haven't seen them implemented well.
In my experience tests are great for backend code, but seldomly worth it for frontend code. In the backend you don't have to manually test it if you have done good automated tests, while in the frontend how things looks and feel is to complex to put into automated tests. Furthermore unit tests have as you say a tendency to put the structure in concrete, which is why I strongly behavior driven tests that are tests from the abstraction level of the user and thereby being implementation independent. There is nothing better that being able to restructure a big part of your code not torching the tests.
Nah man you gotta make sure those React components "render without crashing" /s
I find FE tests actually very useful. Maybe this applies to bigger projects, but having good tests to make sure you don’t break core functionality of your pages helps with regressions.
Also having snapshots help when you have internationalization to make sure strings render properly.
I work with government clients, so introducing a regression could impact thousands of citizens getting benefits or applying to them, etc.
Visual testing can help in FE
Man, don't degrade your work. They are both equally important.
@@theoDSP brother I hydrate XML with Typescript for a living. My life does not matter.
idk man, i'd really like to use the excuse of "this library & service just randomly stopped working" to get the rest of the week off, but you're ACTIVELY preventing that from happening
I don’t agree with this. Unit testing has mega value as a consultant in a corporate setting. People come and go all the time and business rules change. Unit tests help naive developers not break business rules and helps people understand the intention of code; this coupled with documentation.
I’m happy that you don’t feel the need to unit test your code, but I don’t think you should encourage others to not write them.
I think of unit tests as an insurance policy that prove the I/O is accurate.
thanks you for this answer! A unit test is part of the documentation, explains what and how the unit should work, and what is important. If somebody does not write good unit tests then will end up like the youtuber itself. He/She wont understand why testing is important
TLDR: you can’t unit test
Around a year ago I learned about and wrote unit (function) tests for the first time, and went totally ham on covering all C stdlib functions like strlen() and memcmp() that I had to recreate as my library for university. These unit tests were invaluable, since I had to use this library for a ton of later C projects, and it would've sucked if I failed one of those later projects due to a bug in my stdlib. A later project was to recreate Bash, and it would've been absolute hell to unit test every function in there, since the functions in there serve as intermediate steps, rather than direct input -> output, like my stdlib library. Instead, I wrote 260 integration (program) tests to make sure that for example the Bash stdin input `echo foo > foo.txt
cat foo.txt` gave me `foo` as stdout output, and those 260 tests were essential in finding numerous critical bugs that would've gotten me an F on the project otherwise (very draconian, I know)
Can't agree to any of the points you mentioned.
Here are my takes:
1. Test doesn't prevent people from changing code. Instead, it allows them to change code, and verify the change at the same time.
2. Test does affect speed, but being unsure about how your change might impact a complicated codebase affects speed even more. A well tested codebase gives confidence that your change didn't break anything, and your test case gives confidence that the code you wrote does what is is supposed to do. Improved confidence = improved speed.
3. Test doesn't cover a shitty codebase. It supports a strong codebase, and prevents others from accidentally making it shitty.
Overall,
If you are the solo developer for a non-complex codebase, you can go away without tests, as you know every aspect of it.
If you are just starting to build a system, and the requirements are unclear (and can change rapidly), you can avoid adding tests until the requirements are clear or you are ready to launch the product.
But if you are working on a complex system with many developers, tests is a MUST.
I write unit tests because I don`t want to waste time testing the app myself when jest can do it in seconds
Yeah…. But tests are there for other reasons…. Im currently refactoring a rather old codebase. Quite a lot of deeply nested code, many conditionals, lots of coupling, the multiple responsibility principle was applied to everything. I can’t just inject dependencies or mocks, because the system wasn’t built in a way to do that or for that to be useful. There’s also no unit or integrations tests. There are some limited regression tests. Im trying to refactor that code so I can modernise it and upgrade it. But it’s really really easy to accidentally break functionality… every change has subtle effects. I have to maintain all functionality, even the bugs, because who knows what relies on that bug. I have to build tests to verify the existing functionality first, so that I can then make the changes I need to make, and then use the tests to verify my changes didn’t break anything. Had the tests been there in the first place, the app probably wouldn’t have been written the way it was, and I wouldn’t have to do double the work because some previous develop didn’t take time to write tests. Though back when this was written, unit testing was less common… so that dev get a pass. Write tests! Future devs (possible even future you) will thank you!
Agree with how you said many people use tests as concrete to fix an inherently flawed codebase. However, tests really start to shine for complex codebases.
4:06 "we usually have them fixed pretty quick, on average under 7 minutes" factoid actually just statistical error. average production bug takes 20 seconds to fix. Buggers Georg, who lives in a cave, programs in PHP, & created a bug that took 3 years to fix, is an outlier and should not have been counted
Not this again... Many (most?) people work with mountains of shitty code and plagues of shitty devs. I assume this is why so many people, including myself, find this take so strange. Also sounds like Theo has only worked like 1 job alongside a small number of good devs. Or he has little experience working on BE applications.
"Add tests when they solve the problems that only tests can resolve."
Yeah, I agree with this. I’ve just come off a code base with 100% unit test coverage - the code did almost nothing yet somehow broke all the time in the real world.
I think I see this concrete-over-bad-code approach when there is a lot of mutation that isnt isolated to where it’s needed.
If you have your mutations isolated testing the io on that part can cover almost all cases usually in a few tests.
I sometimes like to take a TDD approach with specific features if I think running the test will be faster than manually testing the feature as I'm building it (and only manually testing it at the end). Like I had some new forms for a site I needed to setup and I found just writing a test to ensure the POST request was being handled properly and running that was much faster than manually filling out the form. But I do agree that the test itself was not necessarily that valuable to keep after the feature was complete.
I think it depends a lot of what you are building.
If you are building a frontend application or a backend application or a library are totally different ways to build.
In libraries and backend, with framework agnostic code, TDD works great.
In the frontend, it is a total different world.
In the frontend, E2E tests are more interesting than unit tests.
Unit tests are useful for small parts in the frontend, with specific functions that isn't frontend specific.
If you do something with the DOM, what I think are side effects, it is difficult to unit test.
Without unit tests, you absolutely should run manual tests, which should include a written test plan. Having a person do this work repeatedly is expensive. If in the future you want to change out a component for a different implementation, existing automated tests ensure the same behavior. This tightly couples the dev experience rather than waiting for QA to test manually for bugs and then fixing and testing again. You're also encouraged to change your automated tests as you address new behavior.
if you are willing to write manual then you should be automatically be willing to write Unit test 😂
At my job we have lots of unit tests because our code is in production and we WANT to force devs to work slow and prove that they're not breaking out LIVE and PROFITABLE code before they commit to main. I spend almost all of my time improving legacy code so that it will be easier for the next person to work with and add features to. But when they do add features, I want them to be confident that their additions will not break existing features. And I want them to be able to be confident without having to deeply understand the entire code base.
The metaphor of "covering everything in cement" is pretty good. Even if the code is "bad", if it is working, and being used by thousands of people every hour 24 hours a day, then maybe covering "bad" code in cement is a good idea. You can still improve it. You just need to be more slow and careful about it.
I never understood your stance on unit testing, but I think you actually made some really good points. Based on other comments, it seems the problem is most of us write garbage code, and if someone is the exception, they're basically guaranteed to work with other people who do write garbage code. Most unit tests are also complete garbage. In fact, unit tests are basically a giant dumpster fire, but give off a warm cozy feeling.
Big respect for standing your ground and trying to increase understanding.
recently i started writing unit tests on an ad-hoc basis for things that are just annoying to test by hand. All development requires SOME form of testing, it's just a question of whether you automate it or not. So happens I work on a project where performance in local dev environment is MUCH worse than performance in production (due to increased latency to remote db and 10+ years of poorly structured DB queries). And the majority of my work is bugs or enhancements deep in the system.
So the TDD way is just way faster and easier in a lot of scenarios. Even if I spend two hours writing the test, if I have to conduct 20 tests to eventually get my code right, and one way takes one minute per test run and the other takes 20 minutes per test run (yes I had a task like that recently, re-running a job over and over), then i'm still way ahead (2h20m vs 4h). Not to mention, the time I spent writing the test also helped me understand the problem better, which likely means less time spent on the solution itself.
the thing about test is that you should test properly. mocks are what make a codebase rigid. if you test only usecases having the system under test as a blackbox you will actually benefit from it
It depends how serious a bug would be. For critical sections/applications, test as much as possible. Otherwise, yeah, broader, implementation-agnostic tests that ensure functionality isn't breaking are enough.
I currently work on a project that has many critical (and complex) business logic to determine who can access things. If we didn't have tests for that, oh dear, I'd be PARANOID to touch any of that code.
but why not E2E tests then?
we do just that.
Ive had changes pushed to production where i work that, if we had proper tests (which ive been pushing to get people to focus on...) would have been caught easily, but werent because so many things ran through the same internals (especially around auth and events) that changing anything there always risked breaking something because so much relied on those few sections of the code. When someone makes an optimisation for one case and it breaks another pattern they didnt even know existed, you're going to have a bad time if you aren't running unit tests.
honestly it is wild that a popular tech youtuber is actively recommending against testing for most projects. If every path in your code has basically zero overlap with other parts testing may be less useful, but the more important certain parts of the codebase become, the more important it becomes to make sure that those parts don't start breaking when you change something
I think unit testing is great to assure important results if the path to it is not very clear.the rest needs observation tools and e2e testing
Types defines a theory, unit tests define the reality, simulate the runtime.
“Production” for a library is literally Development for users
I haven't looked into the code base, but these tests actually feel more like integration tests for the library rather than unit tests on a class or module basis
Unit tests are not strictly locked to classes or modules.
I would classify an integration test as anything that reaches out from the code. Something that make request to a server or interact with a db.
Unit tests would be anything that stays within your code base
This perspective on unit testing is fine for a relatively small, specific code base like you have, but it gets problematic when you start building large-scale software or working on big, cross-functional teams. Unit tests don't simply insulate the software you're writing (maybe they do if you have a tiny repo) they:
1. Ensure the contracts and functionality you've written are documented
2. Those contracts look the same even when some other developer makes a change
3. Guarantee compatibility across multiple scopes (use in different classes, different configurations of an object)
Similar to your argument for TypeScript for library devs, I can see different use cases here. Saying "We only had a few problems tests caught" to prescribe this ideology to others is uh... not great. Probably best to add some nuance to your argument so any fledgling devs don't simply opt out of unit testing entirely or even general automation because they think it's such a niche use case.
I do agree, however, with your point about writing for code coverage being one where you lose the plot. That type of testing absolutely stinks. Most teams need maybe... I'm spit-balling, but about 15-20% of their code covered in unit tests. You want all that business logic (calculations, form manipulation, etc) or those guaranteed contracts (api object properties) to be the exact same every time you ship. That's about it.
I agree, you may not need unit testing if you are confident about your skill and trust your team's skills. But you can't do that in a medium/large scale company.
There are many developers at different levels, especially there are irresponsible devs that will do bad code that you can't ask them to make it right easily or you just have to accept and move on with it because it just works and the deadline is coming. The only thing that keeps you confident in this case is the good unit test implementation.
@@VuTuanIT The truth of software engeenering xd
Never seen someone say so much crap with so much confidence. All I can say is this is pure sensationalism with the goal of getting engagement at the expense of noobs.
Thank you for all the content
A unit test or a test in general is part of a contract that you will set up before coding. That contract describes the inputs and outputs of that code block and their boundaries. It doesnt really matter if you write the test before or after writing the logic. What matters is that the next guy or your future you after along time after you wrote the first code, trying to make changes to your logic that now have a guard rail that will save them from braking connected code or even the whole executable from crashing.
This goes against the philosophy of writing everything as a test before writing the logic as I think that it is over-engineering and it wastes a lot if time. It also presses you into a thinking cage where you are not free enough to make changes in your mental code model anymore which totally limits the creativity that is needed while writing or designing code.
this framing on having `roles` that need to be filled by a tool makes this conversation so much more acessible; nice
I think one day I'll have the same view, but I'm still new to a lot of things and tests help me catch mistakes and build experiences I'll rely on later when I run into random issues later.
Good stuff. I'm with you. For super complex functions that can't or shouldn't rely on just plain typescript for functionality, unit tests are quite good.
That being said, I wrote one test in total, and that's just to make sure that when I'm gone, people don't go breaking my error parser. majestic piece of code, but typescript wasn't happy with my recursive type definition, so I had to "lie" and just make an overload function that provides the correct types (object keys inferred from one type, passed into another), and write the actual logic with fake Record because I was done with having 10 "as X" in a 30 line function.
I wouldn't call it shit, but it's definitely one of those "pour concrete on it to make sure it stays in place" situations.
I wish the codebase was set up for other types of tests. but before this, the only tests were for an email validator (didn't have zod before me lol) and that sort of stuff. And I'm too busy getting rid of useEffects and replacing them with react query to write tests :P
Great Video and good takes Theo!
My testing philosophy in general is: test the interface that you are providing and focus on the non-happy paths.
When you build a webapp the interface is the UI for the user in the browser, so maybe Test that with playwright or cypress, unit tests are often causing the exact problem you're describing.
When you bild a library, your interface is the API so test that with jest or vitest.
The UI/API your seeing daily while developing does not need to be tested, but when there are critical edge cases that need to function but are uncommon to stumble over during development: maybe worth testing these.
Also when building a library, working towards a ~100% coverage badge is kind of a marketing investment 🤔
I hold similar views, test as far as possible. e2e tests only, I want my "website" to work, this gives me some guarantee or path to it. Unit-tests cripple you psychologically, you will be second guessing where it should be relatively clear to get idea of whats going on while "inside" your app.
work in a company with a product which has more than 2 years life, and had at least a team or two of turnover. then realize why unit test exists.
Tests are good especially during refactoring. Those are the times that I truly appreciate tests.
But if the refactoring changes the interface, you have to modify all your tests accordingly. So unit tests (especially if you write tests for every single class, no matter how small or how trivial its functionality) can also hinder refactoring.
@@ReneHartmann Not really? You keep both implementations and get the result from both. Ensure they get equivalent results, then you can replace the old implementation and use the new.
I get what you mean, but if that's a problem then either the interface is/was awful or you're thinking about tests incorrectly.
Although tests will obviously need to be rewritten. That's the case for most changes. Add or remove something and that's always the case.
I have found that Typescript makes some broad assumptions.
My guess is when you add the Request type to the parameter,
TS is inferring that all functions in someKey must be of the
same type and take the Request parameter, and this is the
inferred type for func and otherFunc.
I personally don’t like unit tests because the scale is too small. Generally the only time my unit tests fail is when I first write the code, and when I change the expected behaviour I love acceptance tests. I’m fully capable of finding where shit went wrong
"Amazing experience uploading files", now that is what keeps me up at night and motivates me everyday 🤣🤣🤣🤣🤣
I generally unit test when my algorithm has regression or multifunction calls, api fetch async function and enum variated path to follow, generally error management. I don't know safety net but I like static types and good error management. Guardrails just work for me. Whenever I have a function output something that maybe of multiple types, yeah I run down to unit test to check for panic cases. You can probably tell I'm the rust nerd.😂😂😂
I'd happily write tests on someone else's time, on my time though I have no need or time for them. I've been coding for about 7 years and not once have I ever said, "I wish I had a test for this". The only reason something will break is if something unforeseen happens, and you can't test for what you can't see. Finding the source of errors takes almost no time at all, doubling your workload to save a couple of minutes once in a blue moon just isn't worth it.
I get why it makes sense for a large company who have people of different levels coming and going, and they can sell it to the non-tech people as "fully tested". They can also justify doubling the amount of work, which is desirable in many corporate circumstances, so it makes sense that it's industry standard practice, it just doesn't provide me with value for money as a solo dev.
Totally agree with the right balance in unit testing, however I don't agree that tests inherently lock in current implementation. If one writes tests in such a way that they assume nothing about the inner implementation of your code, ie. one is supplying only inputs and expecting outputs, it's usually pretty easy to make changes. Which can be very useful when you are refactoring shitty code, since the tests give you something to lean on while refactoring. But yeah, it takes a lot of experience to get the tests right and if you don't just double the amount of your code problems.
Other than that, we rarely use unit tests too in well maintained projects. I personally hate mocking and artificially setting up contexts and I avoid it wherever I can. That's the reason why only few % of our tests fall into the unit category, and why we keep the overall test amount at a minimum (screw 100% test coverage).
I like the C style of "testing" where you just throw asserts everywhere to say what your expectations are on how the code should be used. Unit tests make you want to implement solutions to hypothetical edge cases that should never actually happen in well designed system and it's all a complete waste of time
Is this "philosophy" of adding asserts in the codebase coming from C? I started using asserts and honestly it's pretty nice. It made me think about the edge cases right in the code and rule out the cases that can never happen and deal with the rest.
When the client demands it to be there, you just do it. The hilarious paet is that the client doesn't understand one bit as long it's green 🤦
i love u man. i wish more seniors+ were like u
Spoiler, I hate tests and writing them. But like with everything in this world, everything has it's place and time. I got another example:
You're hacking away at some feature F. This feature takes some input and produces some output. It can be whatever; a cli command in your application that reports some information, for instance. You hack at feature F - and you've come to the point where it produces output (from say, your string input, being a command on the CLI). It's V1; you know you're not done, but you're producing correct output for normal (sane) input.
_This_ is one time when you write the test. Because you've been hacking on the feature so far. It produces output that seems correct. Now you want to actually refine what you've worked on. You know the input parameters and what the expected output parameters should be, because you've hacked/programmed/developed the feature. When you start a reiteration of this feature, for instance because you want to improve performance characteristics, or just general design things, having this test makes it much easier to see if you're still on track with the expected output - you compile, you run the test, and you see if any of your outputs are different after the re-factor. And after every small re-factor, you rinse repeat. This will save you huge amounts of time.
I understand the whole "solve the bug quickly" rather than spending a lot of time unit testing.
But how about mobile development?
Like.. in mobile development if there is a bug, it may take up to a week for the store to approve your update, and then not all of your users may update to the latest version either (unless you force them to)
This is one of the reasons I push React Native so hard - OTA for bug fixes is OP as hell
fair enough.
There's definitely advantages on native development (and viceversa), but this is a good point
Okay, after reading comments on other videos on this channel, I came to this one in hope of a topic where I have a differing opinion.
Not actually. I too believe unit tests should be contextual. The amount of required unit test code should vastly vary depending on what your code does. You should not start with a unit test before writing your first class or module. Instead, give your application some time running in the real world, understand the nature of issues that impact it, and finally conclude on what portion of your code and what kind of testing will give you the best value in terms of effort required to write those tests vs the effort that will be spent otherwise to maintain the code without tests.
I couldn't agree more. I've seen many projects where the quality is measured by the amount of concrete used to cover the shitty house. 95% coverage 🤣
These are end-to-end tests. They are using the same entry points the client would
I will point out saying unit testing would’ve only fixed 3 bugs for you guys, that is almost 50% of your bugs eliminated by unit testing. Just food for thought
On my past work ( when I was still a Java dev 😱). I was "forced" to write test just to satisfy code coverage and I hated it!
Unit tests are not used to catch bugs. They are used to be able to refactor and improve the code confidently
Imo. most people who are hating on unit testing are not doing it right (ie. Making it unnecessarily difficult for themselves using poor tooling or architecture) or they’re forcefully unit testing something that doesn’t need to be unit tested. Unit testing when done right should always be valuable, that’s the whole point of testing.
There’s tons of bad unit tests out there. But there’s also tons of great unit tests. Unit test specs for ie. parsers, compilers, bundlers, etc is absolutely crucial. So when you say most tests you see are like cement trying to keep a crumbling structure together, all I’m hearing is you haven’t worked in the right type of projects.
In general I unit test my core libraries, integration test my ui component libraries and acceptance/e2e test my applications. Use the right tool for the right problem.
"We choose to _write unit tests_ in this decade and do the other things, not because they are easy, but because they are _mandatory..."_
Good tests should make it easier to change a codebase. Ideally, they should be able to document the expected behavior. On a legacy codebase, I have a much easier time if it comes with decent tests. It's as helpful as good typing and can be just the missing piece in the puzzle to get the whole picture. But I agree with this: tests usually make more sense at a bigger scope than the unit, so e. g. component tests covering user interaction, or integration tests towards a quirky API that can't be changed for compatibility reasons. Not code coverage counts, but test quality.
Stepping a bit out of the topic: I think devs should constantly refactor their codebase as requirements change. Don't prematurely abstract to cover possible future use-cases (yagni), don't try to prevent the need for future code changes. Instead keep code as simple as possible and constantly adapt to what's needed right now. Good tests can help with that, also good typing. Improve both along with the code changes. Bad tests, as well as bad typing, can prevent healthy code changes. Evolving code, typing, AND tests keep your team's mind fresh and confident. It's neither the fact of unit testing or typing that's bad for your code quality, it's any part of your codebase you're afraid to change, for whatever reason.
some people think unit tests as gutter rails rather than guard rails. I'm of the former.
Unit tests assert the behavior of your code. It verifies that it works initially and gives developers confidence that it still works after changes. Typescript is not a substitute for unit tests.....
'm new to coding and my current project is in js with no typescript. Should I have all my javascript projects have typescript?
Yeah but this doesn't apply to things nuclear reactors and the like
if (problem) {break}
No tests needed
You know I've never agreed with your stance on unit testing... watching this I realize it's because I work on a platform team, and almost all the code I write falls into the category of what you're talking about here in this video. It also made me realize a lot of my application work is colored by all the time I spend writing library APIs and is probably over-engineered for what it is. TIL. Ok, you got me, I get it now :D
I respectfully disagree. I learned about the cost of not testing very early on.
Intern working on a Ruby on Rails codebase. Read an entry that has a JSON object and send one of those fields in the API. Simple. Most of the site product pages became unavailable because most products (outside of my team's scope) had a null value instead of an empty object. I just had to safe navigate. Oops.
Or when I was writing a robust generic wrapper for a crappy API. Write once use everywhere. Not only did tests help me catch my own mistakes, but they later serve as documentation as to how the module was used. Reading a test file often makes understanding the original source easier.
That said, I agree on the point of coverage and "useless" tests. Dry "assert-like" unit tests that just check the internal state of a module are terrible. BDD, even as simple as jest's describe/it, on the other hand, are the way to go.
inb4 the 1200 incoming "i told you so" comments
Unit tests for typescript/react apps are a bit heavy-handed yeah.
When I heard you were developing libraries my first thought was whether they would be tested haha. Glad to see you are though. Out of curiosity, do you do e2e tests like Cypress?
Client or Boss asking for testing and paying you by the hour, answer: "YES". Building your own startup..."NO"
Writing parsers for things, it is nice with unit test
If you think tests are guard rails, you are probably making bad tests
the wrong name counter is pure love lol... I recently made a video and felt bad I misnamed so many simple things
Typescript provides only limited safety, as it doesn't exist in runtime and only works within a single repo. TS doesn't help with external APIs that may send anything, or even with own frontend/ backend operations unless you're using something like trpc. It's great to have, but actual guards that exist in runtime seem more important.
Could use sth like zod for 3rd party API responses on runtime
It's easy to say do not write unit test on a small project. What about a project that is 1 million lines of code? Where do you draw the line?
The time has come.
And so have I.
Remove the mustache and undye the hair and you're a good Tom Cruise doppleganger.
Good written tests are a boilerplate for new engineers/developers to understand the exact context/usage of a particular use of code. "tests make it much less likely, that you're going to change the code underneath them" - wrong, tests with good coverage make you much more confident that changing code won't break the application.....
"If tests are preventing fast movement, they are also hurting your ability to fix things when things go wrong"" - again, wrong - something goes wrong in production without tests, you'll be wasting time finding/debugging/fixing the bug a test would have caught originally....
My opinion is that frontend tests are typically not all that great or useful... But backend tests are absolutely 100% necessary
I honestly do not mind tests, have em, don't have em, it's up to you...
But you bring that code coverage BS, now that's when it annoys me.
Tests should make it simple and painless to change code.
I have to write unit tests to pass a sonar quality gate 😢
The problem with testing is that someone should actually test your tests
this is something i say a lot. you have a collection of tools. unit tests are one of those tools. every tool is for a specific job. you cannot use them for everything. you _shouldnt_ use a hammer to try and drive in a screw. you shouldnt use a fork with soup. there are a lot of tools at your disposal as a programmer and i think we are still kind of in a wild west period where a lot of people, including senior developers with decades of experience, dont actually fully understand the right and wrong places to use all of these tools that are available. there's so, so very many, and so, so very many conflicting opinions on all of them that there's just no way it's possible for anyone to have a full understanding of everything and all of the best use cases for all of it. and what anyone needs to do as a programmer is question what and why other programmers are telling you are the right and wrong things to do or use and decide for yourself if their arguments work for you (or your team) or not. for a lot of people, unit tests DO work for them, and very well, but for others, not so much.
Writing tests became easier with ChatGpt, not always perfect but it great in getting things started and finish the test fast