TDD also have the inherent advantage of making you define the public interface before everything else by writing a test for something that doesn't yet exist. I find that it leads to code that is way easier to read. In general, I find it really helpful to start by writing the outermost part, and then define the functions afterwards, instead of jumping up and down the call stack. Programming by wishful thinking, many call it.
It also focusses your attention on the most important part of the design - how easy it is to use the API. Having to write tests first forces you to EYOD (Eat Your Own Dog food).
Thank you for the evangelization of TDD. I've been hooked on it for a few years now... it's funny.... before I did TDD, I thought I wrote good code but had many fewer metrics from which to judge this. Test Driven Development has been the best tool i've had to help me design better solutions, faster. - There are fewer bugs - Tests give me feedback on whether my code is doing too many things (If a test is hard to write) - Faster feedback cycles - I run my tests thousands of times each day automatically, which has made progress faster. ...etc The problem is that people give up too soon. . I'd tried it only for about 1 month intervals before and had the attitude of "Oh, it's good only for some things".... It took me nearly 6 months to really bake it in... I'd had a few false starts before then. But now, i'm never going back. What's more, is my code just reads better too
and, learning tdd has made it easier for me to hop into other programming environments because of the fast feedback cycles. Practicing TDD makes language learning trivial, IMO
@@ContinuousDelivery I've been watching more of your videos and it's driven me to purchase your books. They're great. But I have one request... Could you make a video short for "How to convince your company/significant other to buy Dave Farley training materials"?
@@ContinuousDelivery Management pressure / lack of management support from people who've never written a line of code in their lives... And lack of leadership from experienced engineers. I've worked with people whose attitude is literally "I don't need to do TDD because I don't write code with bugs in it"... For myself, I literally did some TDD at Uni and was explicitly told that the lecturer wasn't interested in my tests!
writing high quality code in TDD way starts from the beginning of big projects. We usually get into a companies where code was written based on Proof of Concept, in a fast and dirty way. Then it is expected from us to extend and maintain this code for MONEY!! Money is the key here in our business, and 95% of people cannot see the value of good code, until it is too late. So all good practices are useless in our broken world where money is the real driver, not quality...
One thing that has kept me from TDD is that my tasks often have a datamodel that isn't finalized or the feature is open ended and I figure it out and revise it as I go
I also love when I have tests. Because it makes refactoring the code very easy and reliable. The big problem is the huge overhead you have. Writing good tests takes at least as much time as the code itself. Refactoring all the tests will create more work down the road. If I have 100% test coverage I also need to adjust the test signatures all the frigging time. Most bugs are as simple as a forgotten null check that takes 5 minutes to fix. My test takes me 3 hours to write and increases the overhead to refactoring in the future. Many problems simply won't get caught by tests. If I mock an API my program will still crash once I get unexpected results from the API. So in the cases I need to manually debug anyway. My test was useless. Having tests is better than not having tests. But they create a huge overhead and often there's no budget for that. The customer wants to see results.
That sounds to me as though you may be writing your tests AFTER you have written the code, not before as we do in TDD. This results in tests that are too-tightly coupled to the code, and you don't get the design support that leads you to define better abstractions, that prevent the tests from testing implementation detail and instead focus on behaviours. If you write the tests first, and let that guide your design, you don't usually get the problems that you mention. Also, in factoring in the costs, you have to include the cost of fixing all the bugs that you find in production. The data says that teams that practice the kind of approach that I recommend spend 44% more time on new features than teams that don't. That is because of the cost of finding and fixing bugs from production.
I feel there is a bit of confusion in the explanation about how to make the car/engine example testable (starting at 11:11 ). It's not that making the engine injectable enables us to test the engine here, since the original PetrolEngine lacks the startedSuccessfully() mehtod in the first place. For some reason, substituting a mock object is suggested, yet I'm not very interested in knowing whether my FakeEngine reports that it can start. As one senior engineer I know would put it, "What are we testing here, the ability of the language to make method calls?" No. The only thing which tells me whether my engine started or not is the addition of the startedSuccessfully() method to the original class. (again, it must gain this method anyway, even if we go for the DI solution). Yes, this is my compiled/statically-typed language bias showing; no apologies. DI is fine and can be elegant. But I don't feel this example shows why it would solve the stated problem.
I'm still grappling with my dissatisfaction around this very popular testing strategy. Passing object members simply for testing purposes adds so much complexity. I'm in a dynamic language right now where this is the case in almost every class. It makes following code extremely painful - everything is available to be passed in, or not. Might just be me. But there has to be a better way. It has made me lean MORE into statically-typed languages in order to lean more on syntax, and less on tests, when possible.
I agree with you that the creation of the method itself makes this testable. I would like to hear Dave's opinion on when he would use fakes/mocks and when he wouldn't in relation to TDD. I often see excessive use of mocks in test code which results in code that becomes difficult to refactor without breaking tests. To me the move to a use of a fake engine makes much more sense as soon as we decide that multiple engine types are a requirement. The hard part is often spotting the initial high level abstractions such as the engine (in this case) which keep the code modular rather than exposing the whole interior workings of the engine to the user of the car.
the problem with the example is it is too simple to put any tests, not only TDD. We need to have a real "unit" to demonstrate which testing approach we should apply and why it's worthy. I agree that we should apply tests but why it is unit tests, not integration tests? Has anyone ever applied integration test only (no unit test, no tdd) in a particular project to compare with the other approach? "Unit" is like. "Single Responsibility", easy to say but hard to determine
11:20 This is really a great example. Because even before watching this video, I found that to properly make the code more modular and testable, I will need to make use of precisely this pattern (I think it's called dependency injection?).
I started automated unit testing way back when Extreme Programming was first introduced but we found that the tests required as much maintenance as the code it was testing. However, the big problem is that it started making the code complex and messy just to accommodate the tests, which were not really reliable anyway. (e.g. engineStarted okay, why do you trust that assertion? Do you need a test for your test?) In your example you added an engine parameter that otherwise would not be needed under the assumption maybe in the future you would need it, but that contradicts the simple code principle. (YAGNI - You ain't gonna need it) I understand TDD in theory, but in practice it makes your code and test hard to maintain with a false sense of security. (the tests are prone to error as much as your code, would test insignificant things, were incomplete, or weren't maintained with the code.) In the end, I found keeping code clean was the best approach along with functional testing. Perhaps more high-level automated tests is a better approach as opposed to unit tests since it actually tests the end result that is desired from the application.
Hi @Paul Lee, you wrote "the tests ... weren't maintained with the code." I'm interested what's your opinion, as we might have different point-of-views - Should a test follow the change of the code, or should it follow the change of the requirements / specification / acceptance criteria?
Dave, I'm curious what kind of test strategy one could use to test implementations of complex algorithms. For example, I'm using (a slightly modified version of) the A* algorithm to find routes for gliders given weather patterns. How could one verify the correctness (in all cases) of the algorithm with the aid of TDD? The number of possible states is quite large (a decent route explores millions of nodes).
4:14 there are many indications that it originated in China in the Classical period. For instance they used red ink to indicate a negative number and it says in the Tao Te Ching (c. 600BCE) "therefore the sage takes possession of the left hand tally".
How do you write a test for something you're not sure of the results on? For example, a prime number generator. You can have a have a list of prime numbers that's already known to check if it outputs those. But lets say you're working with unknown ones. To write the test, you essentially have to write the generator inside the test. How do you test that it is actually working correctly?
I think you’re sort of treading on the line of what tests can cover. They are just tests after all. For your example if you really wanted to *know* it was producing primes you’d have to prove it. But given a sufficiently long list of primes you should have some degree of confidence.
You are hitting at the prime issue of testing in general here, which is a good thing to do. Software is, at the end of the day, just that: software. It may be crafted according to a tedious model of mathematics, physics, social engineering, etc. Testing comes in to verify that the software has been coded to fit the model, and, to satisfy the user's expectations, or, (even better:) to exceed those expectations. Software is not meant to accomplish more than that, unless we're talking data-mining and AI. It may well be that after scrutinous testing, the software is approved for release, but the user community detects issues with the software output. It may be that during modelling and designing, the software invisibly acquired errors that only show themselves afterwards. That is also the reason as to why testing is supposed to be an integral part of development. You have to be aware of specs and the checks that result from the unit tests, the integration tests, the system tests and the user acceptance tests. It is senseless to demand an all-seeing eye from the developers, when all they are supposed to do is develop software, not omniscient wisdom.
@@jadenrodriguez7592 Yes, I agree. Tests are meant to verify the development's accomplishments. If the coders make a mistake, then testing ought to detect this. If the specification is not matching the unspoken wishes of the user community, then this was an error in the specification, not an error by the developers who pursued the specs. Redefine the specs, and feed this information back to development, if the project allows for this. The developers are not expected to be smarter regarding the material expertise than the authors of the specification. They may indicate issues that may interfere with a proper and logical flow of the software under development, but they are not getting paid for rewriting the functional model from the user's perspective, if the user is in the driver's seat of the main specification.
8:59 I've always described cohesion as the way in which methods in a class use data fields. If many methods use one or two data fields out of 6-8 lets say, the cohesion is low and the class can probably be spit into smaller pieces (structure-wise, not especially concern-wise).
That is only one example of cohesion of course. I'd even count two variables that always change together being close together within the same function. for example. It is more importantly about concepts than about only variables though - are the functions in a class or module related?
@@ContinuousDelivery They might be related - I'm looking at my mouse right now thinking. Let's say we have a control app which we can set the sensor refresh rate and RGB lighting for this device. This is that kind of relation - probably good to split them into separate classes and yet they are connected in some way. Also I must say - I like your approach to programming very much. The CI concept, the TDD, focusing on delivering quality rather than just writing code that works. And of course the engineering approach when it comes to coding and analysis of the problems. This one is particularly close to my heart as I started my education as aspiring mechanical engineer and then switched to programming. Have a great day Dave!
For production code I do only TDD for about 10 years now, I forgot how to write functionality in other ways, and this is because I don't like do ... debug!
This is not a pop at TDD ;) but where does TDD start? How do you test the tests themselves as they’re code too which needs testing? Do you write tests for the test and repeat (chicken-and-egg) or rely on the basic manual testing that TDD is there to get rid of. Everywhere I’ve worked who’ve been all-in for TDD have had problems with the tests either being wrong or the process of writing the tests took longer than writing the code itself. The companies might have been crap at TDD ;) but “who tests the tests?” and “how long are we going to spend writing them?” are the two questions that prevent successful take-up of TDD and if they’re not asked and/or TDD isn’t implemented well, then TDD causes a false sense of security and bakes problems in.
Its a good question. Testing the tests is built-in to the approach, the “Red” step in “Red, Green, Refactor” is to run the test and see it fail, specifically, see it fail they way that you predict. If the test doesn’t fail, before you have written the code to make it pass, there is a big problem with the test - It is not testing anything! If the test fails with an error message that you didn’t expect, there is a mistake in the test. Taking this disciplined approach gives us no guarantee that the test is perfect, but eliminates the vast majority of the common mistakes in tests.
@@ContinuousDelivery Just came from your CP2077 video, and I wish I had you as my CompSci teacher! Putting code development in science and engineering terms helped me a lot (hypothesis testing, etc.) since the natural sciences have been my forte.
@@Chemdawg0360 Thanks, I am pleased that you have found it helpful. That is why I make these videos, I think that we are on to something here, and I think it can help us, as an industry, to do a better job. Thanks for the feedback.
If one manually writes a test, those common code errors you talk about are as likely to be included in a test as they are the end code. To make sure the test works and has no errors, one has to go through the same manual test process that non-TDD code goes through and then do the RGR tests. So TDD has one writing 2 sets of code, both of which can have the standard errors in, one of which tests the other. If one is modifying methods TDD tests allow for checking if the modifications have created errors, but only if the new code is compatible with the test, so one needs to go back and check the tests are still valid. And if you’re following the Open-closed principle, then an entity shouldn’t need to be tested as it’s not being modified and and extensions to the entity would need new tests written. Again, not having a pop ;) I’ve worked at a lot of places which have used TDD and it’s never made any sense as increasing (doubling???) the code to be written, gives room for more errors and with a false sense of security especially when the person writing the test gets it wrong (which has happened with frightening regularity). I’m happy to say this might have been the implementation rather than an intrinsic problem with TDD as a principle, but even if that’s true, implementation problems need to considered when using TDD as well as the increases to development time and complexity due to more code being written and the tests not being valid any more.
@@kettchuk918 The flaw you're pointing at is present in any form of test, should we stop testing all together because of that ? I think you're missing the point of TDD which is to force the developper to break problems in tiny incremental pieces of logic so that they can be tested step by step and thouroughly. Tests in TDD should be the equivalent (or a prolongation) of a specification: if tests are not valid anymore after adding more code then there you're not doing TDD right because it means you changed the code before you changed the tests. I believe the problem with TDD is the same than with Agile: lots of people say they do it, but most of the time it's just using the syntax rather than really implementing it as intented.
May I ask what if you are just building prototypes to explore some business opportunity - would you need to do TDD in that case given you are unsure of the final requirements. One of the major criticisms of the waterfall model in general, when I was at University many years ago by our head of department - a PHD in Computer Science, was that the Waterfall model assumes fixed requirements. And most business environments are chaotic, so requirements are generally always changing. In this kind of chaos, the use of prototyping is powerful generally. Sure you are inverting the last two steps of the waterfall model - but it is still the waterfall model - you are just ensuring "testability" of code in advance. Is there an assumption with TDD you are doing things which have less certainty - e.g. doing a calculator program? A chess metaphor - TDD is bit like Capablanca in a chess game, transitioning into won endgames - begin with the end in mind, and that makes the later parts easier. But fundamentally, prototyping as opposed to the waterfall model, is about exploring ideas with code and getting feedback from users, which could completely change requirements - and for me, prototyping is a critical tool. For me it seems as though in prototyping TDD has less of a place. Surely it is only when you are really sure you have "Effective" goals in place for the code, that one can be more confident to go ahead with a TDD approach? Other "Abilities" of code include "Readability" - "Maintainability" but most importantly - is the code actually on target for effective goals the business still needs. With Agile, you might be making small experimental steps with a prototype and want to get feedback from users. It seems in this case TDD might be an unnecessary burden and overhead. This sentiment is also echoed in a quora discussion if you google "What are the problems, if any, with TDD?" - Cheers, K
I can answer that from real experience, when prototyping I nearly always use TDD. It helps me to think in more concrete terms about what I want to achieve to experiment with different design ideas. I don't see it as a barrier to prototyping at all. I know that some other people do, I think that they are missing something. TDD forces you to be clearer about what it is that you want. If you adopt my BDD-flavoured version of TDD, that is ALWAYS from the perspective of a consumer of the behaviour - a focus on the outcome, rather than a focus on implementation detail. Sure, you may change your mind about the outcome that you want, and then you will need to throw away the tests that represent the old outcome that you were aiming for, but you get to that point more clearly and more quickly if you are being disciplined in your own mind about what the outcome is.
@@ContinuousDelivery Thank you so much for clarifying that - I was really quite in love with the beauty of prototyping and I see that with your alternate BDD, it makes a lot more sense because requirements are being explored. You are convincing me - I will try and do a better online chess server with your approaches :) Myself and my brother really love your videos btw - you are the most down to earth in my view of all the GOTO conference videos where we found you - excellent channel and keep up the great work :) Cheers, Tryfon
I do get the idea about writing a test before the code it should test; and when I make stuff that is sufficiently complex I often do make small test snippets with simple inputs and hand calculated expected returns (though I often don't bother making an actual assert as it's usually pretty obvious if the returned value is wrong). And once its working I usually remove the test stuff because I don't want the browser to download that extra testing code. Only very occasionally, if some code has some potential gotcha for future developers I add some sanity checks in the code itself to flag if the code is fed bad data or if someone "optimize" away something that really needs to be there. But I never (voluntarily) use a full test libraries to make formal "real" tests. I find that they are usually so hard to use, requires so much setup and weird syntax so the test code becomes really unreadable and buggy; and it's SOO SLOW. It often takes much longer spinning up the test framework nonsense than it takes just compiling normally and test it in the browser. Maybe I have happened to only have used really bad frameworks, but it seems so weird to me why you would use them over just making an ad hoc test in the code itself.
Why would you want the liability of maintaining the ad hoc tests? Do you comment them out after? How do you know it won’t get “resurrected”? Do you delete them? How do you know you’ve deleted all of them? When you extend and break some coupling, do you know where to look? Or are you left guessing what to retest or uncomment? This sounds like it makes it difficult not to clutter your applications and leave them prone to errors or leaks. Not to mention it’d be a nightmare for someone else to pick up amd maintain. If it keeps you from having to filter garbage and from those all too familiar hours debugging poorly implemented architectures idk how much of a bad thing it is that writing the tests takes a significant chunk of time. I think the trade off is that with writing the tests initially your code is kept functional and maintainable through continuous development.
He has a vid somewhere about helping to speed some if the testing up, such as making sure you don’t execute some involved process for simple checks, and using mock interfaces to simulate slower external interactions.
Hi @Stein Gauslaa Strindhaug, do you create code that is maintained, extended, optimised etc. for long years by many developers or its usage is mostly one-time or for a short period of time?
12:46 "as long as it fulfilled the contract" is the point a contract which you haven't really defined or tested. you have tested that your fake engine works in your start engine function. but there is absolutely no guarantee that the turbine engine doesn't require two start calls 5 seconds apart to work properly. or maybe it only requires that in certain edge case, when you have a special igniter class inserted into your engine class, and only when you are processing a special fuel class. but you will never know that, because you are using a mock engine that has no link to the wonky igniter class someone else added in a different part of the code base. what you have done is wasted time writing a trivial class with a trivial test. any class that is easily testable is also easily eliminated. and any test that you write tests things you already considered. you could try to rescue this catastrophe by writing tests that make sure combinations of classes work, but you will very quickly end up with exponentially growing number of things you need to test. and each of these tests will be far more complicated that your trivial unit tests. point being, I have no idea how those studies got those numbers of bugs. I have never worked with people who shipped code that had bugs in the particular functions that they wrote. all the catastrophic show stopper bugs resulted from slight differences in unexamined assumptions, or changes in assumptions, about how different parts of the code interfaced. unit tests caught none of these, the tests always passed. in fact, I don't recall unit tests ever catching a bug, except when you are still writing the initial code. all failures were failures to modify the unit test that you didn't know was there. seems like TDD is basically circular reasoning: it produces great code, because great code is defined as the kind of code that is produced by TDD. here is an alternative theorem: a test that never detects a bug during the life of the product was, in retrospect, a waste of time to write and a waste of resources to run.
Feels a bit outdated. Been working with agile methodologies for a while now, which is flexible and dynamic. This feels like going back to the highly documented ways of the past. Why build all these tests first, when you dont know how the final design will be anyway, thats the point of Agile, its flexible. But let me see how it goes, working on a new assignment with TDD, I'll come back in 6 months and let you know what I think.
Can someone please explain to me how this differs from Requirements Driven Design? Here we say we start with defining tests that represent functionality and behavior we want to see in a system (not speaking in the context of code only). Presumably, to fully define the test, you have to have some metric of success. How does that differ from writing a functional or performance requirement prior to establishing the actual system itself? I only see a difference in the way we consider the metric we establish: as a test or as a requirement. With requirements, test cases are also established so are we just talking about the same thing two different ways? What I will say is with requirement driven design, the norm is to perform testing as a verification event (a one off exercise that should be passed). With TDD, testing is seen as an incremental or iterative effort where tests are expected to fail as the system is developed, until the product is finally completed and all specified tests pass. Regression testing is inherently woven in that way. Curious to see what people have to say about TDD vs RDD or other philosophies of design.
hmm.. As a Jr dev.. I'm starting to see some of Agile's cons. The constant requirement changes.. I'll add this to my watch later list. Maybe you address how you can do TDD when requirements are constantly changing.
i used to think that agile and tdd does not work together since you have to do the same work twice with changing requirements. the fundamental challange with the agile approach practised today is that you are changing existing code which violates the open closed principle. agile should be lived on a higher level by embracing strategic pivotation (maybe killing a module, or wiriting it new) i am sure you know that the typical workflow for agile team is to write down the user story and then to extract task from the story to create as much value as possible for the people ending up using the software. the flexible part is at the user story level, not at the task level
How do I write a test for a function which does not exist? Like computing the length of a vector. Perhaps I have a data class which stores a tuple of (x,y,z) or something. A feature I want is to compute the length of that vector. But if I dogmatically follow the rules of TDD, I must write the test before I write "def get_length(vec: Vector) -> float". But now my test won't even run/compile because WHERE DO I IMPORT THE FUNCTION? There is not a function to test. Perhaps I can stub out the function, in my case return 0 or something, just so that the test at least has a thing for which to test. But haven't I broken the rule by writing code before I write the test before I write the code? Maybe my autism is showing and I'm taking things too literally, but for me, this is why I write tests _after_ writing my code. So that my f*5&king tests will at least run/compile.
A few words in favor of independently written test automation. If a dev forgets to code a case emergent from parameter combinations, and he wrote the tests there won't be a test to catch it either.. Can't stack two slices of the same cheesewheel without some holes lining up.
Thanks Great video. To my experience so far is that using a fake class instead of a mock is much simpler when contracting the test most of the time. Of course that approach only applies for simple fakes.
I'm kind of confused. How can it mean something when the test failed the first time? The no code to try the test on, chances are you will create an instance of a non-existing class or you will call a method that hasn't been implemented yet so it's obvious the test will fail even if it's useless so the fact that the test fails doesn't mean that it's testing something useful. Can I have some help here please?
Treat every change as an experiment and monitor it in some way. Make change in small steps and you get more chances to monitor. The aim of treating things as an experiment, is to force you to think about what you are trying to achieve and how to measure its success. Want better stability? Measure change failure rate and mean time to recovery. Want more users, measure user sign-up and so on. Define a goal, an impact that you intend to achieve overall, and find a way to track that. Do you changes move you toward, or away from the goal? It is going to be imprecise and subjective, but that is how you spot failure early. Work incrementally, in small steps, and measure.
Hi Dave, great stuff. Unfortunately, the link for the research on project failures doesn't seem to work. Is there someplace else we can get that information? Thanks again.
"Or if I was slightly crazy I could create a car with a jet engine...". People trying to break land speed records - Who are you calling slightly crazy? We are completely mental!!!
So I test a TDD for a 2 weeks and my conclusion is that: 1)you build app faster by not waiting time on what will be needed 2) it help you to split big problem on small component 3) you find small problems way faster If you are not really that smart(let be onset with our self ), TDD is the way to go. I do know TDD only on my home project but I will implement it in my next projects. The only problem I have now is how implement TDD to existing project
are there ways to practice all these things that charecterises "good code"? I will definetly be using the next 2 weeks to practice TDD but I also need to practice the things you mentioned like loose coupling etc
I think that TDD will help you with that. It tends to naturally "encourage" a more loose-coupled approach to design. My new book "Modern Software Engineering" (out in Dec) explores these ideas in more detail with a few code examples, so may be helpful.
I joined a new company recently and when I went over the code i saw there there were not tests, when I asked my manager why he told me that the codebase is not complex enough to write tests for , i don't agree with him but I'm not sure what to do , any advice? Thanks for your videos I've been binging on them recently
Obviously I agree with you, there is no code that is too simple to test. If they ran the code before they released it, then that is a form of test. So now the discussion is really how to test efficiently, for which we need automated tests and TDD. How to convince others of this though us a different problem. In your place, I'd at least write tests for my own work, and try and convince others of the benefits, and in my case, if that didn't work I'd look for a better job.
@@ContinuousDelivery Hi, thank you so much for your comment i really appreciate it . Yeah it seems that some sort of manual testing is done but I think it's highly inefficient and prone to bugs. And after going over the codebase i really don't think that it's too small to test, testing help us write more maintable goal which will also help feature devs joining the project later. Thanks for your advice I'll try taking your advice, i hope I can make a change. On a side note, I'm really enjoying your new book ,thanks a lot for writing it . It's an honor to learn from someone such as yourself
I don't understand at all how this leads to better design. I do see that it does help to minimize errors by creating constraints to what its expected of the units being tested to ensure "they deliver". But guaranteeing delivery doesn't necessarily make for better code design. Perhaps I'm missing something on this philosophy?
I describe this in a lot more detail in my book, but fundamentally it is that TDD drives you to increase modularity, cohesion, separation of concerns, abstraction and reduce coupling. All of these are markers for high-quality in code. It is hard to do TDD, unless you improve these things, so TDD acts as a kind of "talent amplifier" for developers.
@@ContinuousDelivery I agree all those are indeed markers of quality code, but I fail to see the connection as to how TDD drives their "amplification ". You still need to start with a well defined idea of the requirements of your unit to test anything, and to me it is this that drives those markers , understanding what your problem really is. Testing them is crucial to ensure boundary cases, exceptions etc, are being handled and expected by the unit, but one can still get away with terrible design and meet these tests. I suppose I'll go read more on it as you suggest.
I struggle with testing. I keep watching videos about it heh. My tests are apps that print out data. And I just am looking at the output to see if it's working. Which is probably wrong heh.
The first video on my channel is a simple worked example of TDD. The sound isn’t great, it was my first video, but I think that you might find the ideas interesting. th-cam.com/video/xUi2951ufaw/w-d-xo.html
Isolate the logic part (i.e. the formatting) from the printing part. Test if the document is build as expected independently from moving it to the printer.
I have been enjoying your videos quite a bit and have learned a lot. Man one criticism if I may about this playlist is that I can't just turn it on and do some work physically and listen... which is normally what these types of videos are good for, and that is because your ads play for way too long :( Otherwise, great information
IMO,your example of writing, 'testable code' was shifting test code into the production code. Rarely a good idea. I would have just accepted that you cannot assert that the engine had started and relied on a later test - maybe asserting an increase in speed or rpm after accelerating - to catch that failure. Both rpm and speed would be integration tests. Testing the integration between the instruments and the engine. So this would be caught later in the pipeline. Not ideal but preferable to adding code to make your engine 'testable'.
Hi @Bradley Atkins, two questions - I assume we have different point-of-views on quality and productivity :-) a) Don't you want to be sure that the small pieces are working, and know that as soon as possible, also with a direct check? c) Don't you like the consequence that a Car is able take other (future) types of engines, a consequence of design change?
I am an bit confused, i thought for 6 years of programming, that it would be normal and every programmer, would be programming like that xD Are there realy programers, how dont do tests all the time? 😅
so yes test, but test driven design means that you place testing above all other goals. I know later you will get into behavioral design and you argue that that TDD is behavior as you are testing functions, but then ODD could make the same claim as they literally are a collection of functions. No behavior and design do not happen at the functional or object levels. Behavior such as factory patterns and guard clauses for early exits on complex functions are not effected by the paradigm chosen. Behavior is more about creating a system or engineering for the problem at hand and may not and often dos not include coding of any sort. Here is where flowcharting, data modeling, pattern constraints, and more behavior features are added to the project. It is those behaviors that then drive both TDD and ODD. If you are you using like ReactJS that uses Flux concepts, then component driven design, by separating concerns of HOC's to given a framework for display and pure components to actually display data. In these ways we are doing SOLID principle without regards to the paradigm and language used. This is the true essence of BDD and is not to be confused or obfuscated by other design principles.
It is lazy to speak about a site, for instance that "cyber dojo", but not to put it in the description. Although you never forget to put there your own links. There are many sites with "cyber dojo" in the description. This attitude directly translates into your poor numbers of subscribers and views.
It may take time for people to learn, but decades of studies show that TDD reduces the overall time taken and improves quality, because of a very significant reduction in the number of bugs, much less time spent fixing bugs, and positive impact on design.
Hmmm TDD is cool and all, I like it and want to get better at it. I just don't think one should go crazy with it. Just like every programming thing people get all crazy about some method or other that will make better code for you. At the end of the day TDD or not, hot programming paradigm or trending programming thingy or not, you yourself need to improve skills and knowledge about computers and to think about problems and problem solving. I mean think about it this way, you do TDD for 3 years, now your code is better on all levels. But why? Because of TDD alone, or because you have 3 more years of experience? I mean it was not because of TDD you researched some programming thingy for some problem you had, and it was not because of TDD alone that you consumed that information and learned about it. Think about it. TDD is a great tool, I just am not super impressed by it no matter how hype it is :)
Test Driven Development... when your software passes every one of your 800 million autogenerated test cases and crashes the first time it gets to interact with an actual user.
@@ContinuousDelivery Yes, if you think that you can predict the most stupid thing a user will do with your product, then you are definitely doing it wrong.
I don't believe in TDD. To certain extent it can be helpful. From some point it must hold down efficiency and flexibility. Masters put refactoring in the center. Not test.
too much exaggerated comments to convince audience the importance of TDD, actually how many projects use this way to delivery? talk about the process not incite
TDD also have the inherent advantage of making you define the public interface before everything else by writing a test for something that doesn't yet exist. I find that it leads to code that is way easier to read. In general, I find it really helpful to start by writing the outermost part, and then define the functions afterwards, instead of jumping up and down the call stack. Programming by wishful thinking, many call it.
It also focusses your attention on the most important part of the design - how easy it is to use the API.
Having to write tests first forces you to EYOD (Eat Your Own Dog food).
I've never once found it lead to code that is easier to read, but I have seen it lead to code where everything is (unfortunately) public.
When should we test - "All the #$%&! time" absolutely the best lead in to a video I have heard in some time.
Thanks :)
Right after he said that, I shouted, "I'll drink to that!"
Im laughing so #$%&! hard right now with that intro LOL
Caught my attention! I was about to fall asleep but damn!
Caught me off guard😂
You had me at “All the f$#&*# time” 😂 You’re a great teacher, subscribed!
Thank you for the evangelization of TDD.
I've been hooked on it for a few years now... it's funny.... before I did TDD, I thought I wrote good code but had many fewer metrics from which to judge this. Test Driven Development has been the best tool i've had to help me design better solutions, faster.
- There are fewer bugs
- Tests give me feedback on whether my code is doing too many things (If a test is hard to write)
- Faster feedback cycles - I run my tests thousands of times each day automatically, which has made progress faster.
...etc
The problem is that people give up too soon. . I'd tried it only for about 1 month intervals before and had the attitude of "Oh, it's good only for some things".... It took me nearly 6 months to really bake it in... I'd had a few false starts before then. But now, i'm never going back.
What's more, is my code just reads better too
and, learning tdd has made it easier for me to hop into other programming environments because of the fast feedback cycles. Practicing TDD makes language learning trivial, IMO
Thanks for the "thanks". I agree with everything that you say. I confess that I still don't really understand why everyone doesn't work this way.
@@ContinuousDelivery
I've been watching more of your videos and it's driven me to purchase your books. They're great.
But I have one request...
Could you make a video short for "How to convince your company/significant other to buy Dave Farley training materials"?
@@ContinuousDelivery Management pressure / lack of management support from people who've never written a line of code in their lives... And lack of leadership from experienced engineers.
I've worked with people whose attitude is literally "I don't need to do TDD because I don't write code with bugs in it"...
For myself, I literally did some TDD at Uni and was explicitly told that the lecturer wasn't interested in my tests!
I gotta get started with ttd asap
writing high quality code in TDD way starts from the beginning of big projects. We usually get into a companies where code was written based on Proof of Concept, in a fast and dirty way. Then it is expected from us to extend and maintain this code for MONEY!! Money is the key here in our business, and 95% of people cannot see the value of good code, until it is too late. So all good practices are useless in our broken world where money is the real driver, not quality...
“All the f$#%%^* time.”… faded… disappeared… amidst the mystery sound. Excellent effect!
One thing that has kept me from TDD is that my tasks often have a datamodel that isn't finalized or the feature is open ended and I figure it out and revise it as I go
Hello @Daniil Zadorozhnyy,
could you, please, tell a few reasons what causes this?
@@Geza_Molnar_ Real world. Real requirements. Time constraints. Evolving requirements.
This is easily the best and most understandable explanation for and about TDD I've ever heard.
I also love when I have tests. Because it makes refactoring the code very easy and reliable.
The big problem is the huge overhead you have.
Writing good tests takes at least as much time as the code itself.
Refactoring all the tests will create more work down the road.
If I have 100% test coverage I also need to adjust the test signatures all the frigging time.
Most bugs are as simple as a forgotten null check that takes 5 minutes to fix.
My test takes me 3 hours to write and increases the overhead to refactoring in the future.
Many problems simply won't get caught by tests.
If I mock an API my program will still crash once I get unexpected results from the API.
So in the cases I need to manually debug anyway. My test was useless.
Having tests is better than not having tests. But they create a huge overhead and often there's no budget for that. The customer wants to see results.
That sounds to me as though you may be writing your tests AFTER you have written the code, not before as we do in TDD. This results in tests that are too-tightly coupled to the code, and you don't get the design support that leads you to define better abstractions, that prevent the tests from testing implementation detail and instead focus on behaviours.
If you write the tests first, and let that guide your design, you don't usually get the problems that you mention.
Also, in factoring in the costs, you have to include the cost of fixing all the bugs that you find in production. The data says that teams that practice the kind of approach that I recommend spend 44% more time on new features than teams that don't. That is because of the cost of finding and fixing bugs from production.
I feel there is a bit of confusion in the explanation about how to make the car/engine example testable (starting at 11:11 ). It's not that making the engine injectable enables us to test the engine here, since the original PetrolEngine lacks the startedSuccessfully() mehtod in the first place. For some reason, substituting a mock object is suggested, yet I'm not very interested in knowing whether my FakeEngine reports that it can start. As one senior engineer I know would put it, "What are we testing here, the ability of the language to make method calls?" No. The only thing which tells me whether my engine started or not is the addition of the startedSuccessfully() method to the original class. (again, it must gain this method anyway, even if we go for the DI solution). Yes, this is my compiled/statically-typed language bias showing; no apologies. DI is fine and can be elegant. But I don't feel this example shows why it would solve the stated problem.
I'm still grappling with my dissatisfaction around this very popular testing strategy. Passing object members simply for testing purposes adds so much complexity. I'm in a dynamic language right now where this is the case in almost every class. It makes following code extremely painful - everything is available to be passed in, or not. Might just be me. But there has to be a better way. It has made me lean MORE into statically-typed languages in order to lean more on syntax, and less on tests, when possible.
I agree with you that the creation of the method itself makes this testable. I would like to hear Dave's opinion on when he would use fakes/mocks and when he wouldn't in relation to TDD. I often see excessive use of mocks in test code which results in code that becomes difficult to refactor without breaking tests. To me the move to a use of a fake engine makes much more sense as soon as we decide that multiple engine types are a requirement. The hard part is often spotting the initial high level abstractions such as the engine (in this case) which keep the code modular rather than exposing the whole interior workings of the engine to the user of the car.
the problem with the example is it is too simple to put any tests, not only TDD. We need to have a real "unit" to demonstrate which testing approach we should apply and why it's worthy. I agree that we should apply tests but why it is unit tests, not integration tests? Has anyone ever applied integration test only (no unit test, no tdd) in a particular project to compare with the other approach? "Unit" is like. "Single Responsibility", easy to say but hard to determine
11:20 This is really a great example.
Because even before watching this video, I found that to properly make the code more modular and testable, I will need to make use of precisely this pattern (I think it's called dependency injection?).
I started automated unit testing way back when Extreme Programming was first introduced but we found that the tests required as much maintenance as the code it was testing. However, the big problem is that it started making the code complex and messy just to accommodate the tests, which were not really reliable anyway. (e.g. engineStarted okay, why do you trust that assertion? Do you need a test for your test?) In your example you added an engine parameter that otherwise would not be needed under the assumption maybe in the future you would need it, but that contradicts the simple code principle. (YAGNI - You ain't gonna need it) I understand TDD in theory, but in practice it makes your code and test hard to maintain with a false sense of security. (the tests are prone to error as much as your code, would test insignificant things, were incomplete, or weren't maintained with the code.) In the end, I found keeping code clean was the best approach along with functional testing. Perhaps more high-level automated tests is a better approach as opposed to unit tests since it actually tests the end result that is desired from the application.
You may want to take a look at tonight's video, which will explore these problems in a bit more detail, it's called "TDD vs BDD"
Hi @Paul Lee,
you wrote "the tests ... weren't maintained with the code."
I'm interested what's your opinion, as we might have different point-of-views - Should a test follow the change of the code, or should it follow the change of the requirements / specification / acceptance criteria?
Thank you so much! I am a junior data engineer. This video is eye-opening.
Glad it was helpful!
You again, tests again, seems like a sign of the gods xD
Wow, thank you very much for this episode, this was super helpful!
this is really nice thank for the really good advice
Thanks
Dave, I'm curious what kind of test strategy one could use to test implementations of complex algorithms.
For example, I'm using (a slightly modified version of) the A* algorithm to find routes for gliders given weather patterns. How could one verify the correctness (in all cases) of the algorithm with the aid of TDD? The number of possible states is quite large (a decent route explores millions of nodes).
4:14 there are many indications that it originated in China in the Classical period. For instance they used red ink to indicate a negative number and it says in the Tao Te Ching (c. 600BCE) "therefore the sage takes possession of the left hand tally".
How do you write a test for something you're not sure of the results on?
For example, a prime number generator. You can have a have a list of prime numbers that's already known to check if it outputs those. But lets say you're working with unknown ones. To write the test, you essentially have to write the generator inside the test. How do you test that it is actually working correctly?
I think you’re sort of treading on the line of what tests can cover. They are just tests after all. For your example if you really wanted to *know* it was producing primes you’d have to prove it. But given a sufficiently long list of primes you should have some degree of confidence.
You are hitting at the prime issue of testing in general here, which is a good thing to do.
Software is, at the end of the day, just that: software. It may be crafted according to a tedious model of mathematics, physics, social engineering, etc. Testing comes in to verify that the software has been coded to fit the model, and, to satisfy the user's expectations, or, (even better:) to exceed those expectations.
Software is not meant to accomplish more than that, unless we're talking data-mining and AI. It may well be that after scrutinous testing, the software is approved for release, but the user community detects issues with the software output. It may be that during modelling and designing, the software invisibly acquired errors that only show themselves afterwards.
That is also the reason as to why testing is supposed to be an integral part of development. You have to be aware of specs and the checks that result from the unit tests, the integration tests, the system tests and the user acceptance tests. It is senseless to demand an all-seeing eye from the developers, when all they are supposed to do is develop software, not omniscient wisdom.
@@jadenrodriguez7592 Yes, I agree. Tests are meant to verify the development's accomplishments. If the coders make a mistake, then testing ought to detect this. If the specification is not matching the unspoken wishes of the user community, then this was an error in the specification, not an error by the developers who pursued the specs. Redefine the specs, and feed this information back to development, if the project allows for this.
The developers are not expected to be smarter regarding the material expertise than the authors of the specification. They may indicate issues that may interfere with a proper and logical flow of the software under development, but they are not getting paid for rewriting the functional model from the user's perspective, if the user is in the driver's seat of the main specification.
Is there a preferred order to watch your videos? Some sort of roadmap
Not really, each is meant to be stand-alone.
8:59 I've always described cohesion as the way in which methods in a class use data fields. If many methods use one or two data fields out of 6-8 lets say, the cohesion is low and the class can probably be spit into smaller pieces (structure-wise, not especially concern-wise).
That is only one example of cohesion of course. I'd even count two variables that always change together being close together within the same function. for example. It is more importantly about concepts than about only variables though - are the functions in a class or module related?
@@ContinuousDelivery They might be related - I'm looking at my mouse right now thinking. Let's say we have a control app which we can set the sensor refresh rate and RGB lighting for this device. This is that kind of relation - probably good to split them into separate classes and yet they are connected in some way.
Also I must say - I like your approach to programming very much. The CI concept, the TDD, focusing on delivering quality rather than just writing code that works. And of course the engineering approach when it comes to coding and analysis of the problems. This one is particularly close to my heart as I started my education as aspiring mechanical engineer and then switched to programming.
Have a great day Dave!
For production code I do only TDD for about 10 years now, I forgot how to write functionality in other ways, and this is because I don't like do ... debug!
This is not a pop at TDD ;) but where does TDD start? How do you test the tests themselves as they’re code too which needs testing? Do you write tests for the test and repeat (chicken-and-egg) or rely on the basic manual testing that TDD is there to get rid of. Everywhere I’ve worked who’ve been all-in for TDD have had problems with the tests either being wrong or the process of writing the tests took longer than writing the code itself.
The companies might have been crap at TDD ;) but “who tests the tests?” and “how long are we going to spend writing them?” are the two questions that prevent successful take-up of TDD and if they’re not asked and/or TDD isn’t implemented well, then TDD causes a false sense of security and bakes problems in.
Its a good question. Testing the tests is built-in to the approach, the “Red” step in “Red, Green, Refactor” is to run the test and see it fail, specifically, see it fail they way that you predict. If the test doesn’t fail, before you have written the code to make it pass, there is a big problem with the test - It is not testing anything!
If the test fails with an error message that you didn’t expect, there is a mistake in the test.
Taking this disciplined approach gives us no guarantee that the test is perfect, but eliminates the vast majority of the common mistakes in tests.
@@ContinuousDelivery Just came from your CP2077 video, and I wish I had you as my CompSci teacher! Putting code development in science and engineering terms helped me a lot (hypothesis testing, etc.) since the natural sciences have been my forte.
@@Chemdawg0360 Thanks, I am pleased that you have found it helpful. That is why I make these videos, I think that we are on to something here, and I think it can help us, as an industry, to do a better job. Thanks for the feedback.
If one manually writes a test, those common code errors you talk about are as likely to be included in a test as they are the end code. To make sure the test works and has no errors, one has to go through the same manual test process that non-TDD code goes through and then do the RGR tests.
So TDD has one writing 2 sets of code, both of which can have the standard errors in, one of which tests the other. If one is modifying methods TDD tests allow for checking if the modifications have created errors, but only if the new code is compatible with the test, so one needs to go back and check the tests are still valid. And if you’re following the Open-closed principle, then an entity shouldn’t need to be tested as it’s not being modified and and extensions to the entity would need new tests written.
Again, not having a pop ;) I’ve worked at a lot of places which have used TDD and it’s never made any sense as increasing (doubling???) the code to be written, gives room for more errors and with a false sense of security especially when the person writing the test gets it wrong (which has happened with frightening regularity). I’m happy to say this might have been the implementation rather than an intrinsic problem with TDD as a principle, but even if that’s true, implementation problems need to considered when using TDD as well as the increases to development time and complexity due to more code being written and the tests not being valid any more.
@@kettchuk918 The flaw you're pointing at is present in any form of test, should we stop testing all together because of that ?
I think you're missing the point of TDD which is to force the developper to break problems in tiny incremental pieces of logic so that they can be tested step by step and thouroughly. Tests in TDD should be the equivalent (or a prolongation) of a specification: if tests are not valid anymore after adding more code then there you're not doing TDD right because it means you changed the code before you changed the tests.
I believe the problem with TDD is the same than with Agile: lots of people say they do it, but most of the time it's just using the syntax rather than really implementing it as intented.
May I ask what if you are just building prototypes to explore some business opportunity - would you need to do TDD in that case given you are unsure of the final requirements. One of the major criticisms of the waterfall model in general, when I was at University many years ago by our head of department - a PHD in Computer Science, was that the Waterfall model assumes fixed requirements. And most business environments are chaotic, so requirements are generally always changing. In this kind of chaos, the use of prototyping is powerful generally. Sure you are inverting the last two steps of the waterfall model - but it is still the waterfall model - you are just ensuring "testability" of code in advance. Is there an assumption with TDD you are doing things which have less certainty - e.g. doing a calculator program? A chess metaphor - TDD is bit like Capablanca in a chess game, transitioning into won endgames - begin with the end in mind, and that makes the later parts easier. But fundamentally, prototyping as opposed to the waterfall model, is about exploring ideas with code and getting feedback from users, which could completely change requirements - and for me, prototyping is a critical tool. For me it seems as though in prototyping TDD has less of a place. Surely it is only when you are really sure you have "Effective" goals in place for the code, that one can be more confident to go ahead with a TDD approach? Other "Abilities" of code include "Readability" - "Maintainability" but most importantly - is the code actually on target for effective goals the business still needs. With Agile, you might be making small experimental steps with a prototype and want to get feedback from users. It seems in this case TDD might be an unnecessary burden and overhead. This sentiment is also echoed in a quora discussion if you google "What are the problems, if any, with TDD?" - Cheers, K
I can answer that from real experience, when prototyping I nearly always use TDD. It helps me to think in more concrete terms about what I want to achieve to experiment with different design ideas. I don't see it as a barrier to prototyping at all. I know that some other people do, I think that they are missing something.
TDD forces you to be clearer about what it is that you want. If you adopt my BDD-flavoured version of TDD, that is ALWAYS from the perspective of a consumer of the behaviour - a focus on the outcome, rather than a focus on implementation detail. Sure, you may change your mind about the outcome that you want, and then you will need to throw away the tests that represent the old outcome that you were aiming for, but you get to that point more clearly and more quickly if you are being disciplined in your own mind about what the outcome is.
@@ContinuousDelivery Thank you so much for clarifying that - I was really quite in love with the beauty of prototyping and I see that with your alternate BDD, it makes a lot more sense because requirements are being explored. You are convincing me - I will try and do a better online chess server with your approaches :) Myself and my brother really love your videos btw - you are the most down to earth in my view of all the GOTO conference videos where we found you - excellent channel and keep up the great work :) Cheers, Tryfon
My God I'm at a point where I can read code and sorta understand what is happening even though I haven't seen it before wtf is happening lol
Is there content on how to handle TDD when we are talking about data engineering?
I do get the idea about writing a test before the code it should test; and when I make stuff that is sufficiently complex I often do make small test snippets with simple inputs and hand calculated expected returns (though I often don't bother making an actual assert as it's usually pretty obvious if the returned value is wrong). And once its working I usually remove the test stuff because I don't want the browser to download that extra testing code. Only very occasionally, if some code has some potential gotcha for future developers I add some sanity checks in the code itself to flag if the code is fed bad data or if someone "optimize" away something that really needs to be there.
But I never (voluntarily) use a full test libraries to make formal "real" tests. I find that they are usually so hard to use, requires so much setup and weird syntax so the test code becomes really unreadable and buggy; and it's SOO SLOW. It often takes much longer spinning up the test framework nonsense than it takes just compiling normally and test it in the browser. Maybe I have happened to only have used really bad frameworks, but it seems so weird to me why you would use them over just making an ad hoc test in the code itself.
Why would you want the liability of maintaining the ad hoc tests? Do you comment them out after? How do you know it won’t get “resurrected”? Do you delete them? How do you know you’ve deleted all of them?
When you extend and break some coupling, do you know where to look? Or are you left guessing what to retest or uncomment?
This sounds like it makes it difficult not to clutter your applications and leave them prone to errors or leaks. Not to mention it’d be a nightmare for someone else to pick up amd maintain. If it keeps you from having to filter garbage and from those all too familiar hours debugging poorly implemented architectures idk how much of a bad thing it is that writing the tests takes a significant chunk of time. I think the trade off is that with writing the tests initially your code is kept functional and maintainable through continuous development.
He has a vid somewhere about helping to speed some if the testing up, such as making sure you don’t execute some involved process for simple checks, and using mock interfaces to simulate slower external interactions.
Hi @Stein Gauslaa Strindhaug,
do you create code that is maintained, extended, optimised etc. for long years by many developers or its usage is mostly one-time or for a short period of time?
12:46 "as long as it fulfilled the contract" is the point
a contract which you haven't really defined or tested. you have tested that your fake engine works in your start engine function. but there is absolutely no guarantee that the turbine engine doesn't require two start calls 5 seconds apart to work properly.
or maybe it only requires that in certain edge case, when you have a special igniter class inserted into your engine class, and only when you are processing a special fuel class. but you will never know that, because you are using a mock engine that has no link to the wonky igniter class someone else added in a different part of the code base.
what you have done is wasted time writing a trivial class with a trivial test. any class that is easily testable is also easily eliminated. and any test that you write tests things you already considered.
you could try to rescue this catastrophe by writing tests that make sure combinations of classes work, but you will very quickly end up with exponentially growing number of things you need to test. and each of these tests will be far more complicated that your trivial unit tests.
point being, I have no idea how those studies got those numbers of bugs. I have never worked with people who shipped code that had bugs in the particular functions that they wrote. all the catastrophic show stopper bugs resulted from slight differences in unexamined assumptions, or changes in assumptions, about how different parts of the code interfaced. unit tests caught none of these, the tests always passed. in fact, I don't recall unit tests ever catching a bug, except when you are still writing the initial code. all failures were failures to modify the unit test that you didn't know was there.
seems like TDD is basically circular reasoning: it produces great code, because great code is defined as the kind of code that is produced by TDD.
here is an alternative theorem: a test that never detects a bug during the life of the product was, in retrospect, a waste of time to write and a waste of resources to run.
¡Gracias!
Feels a bit outdated. Been working with agile methodologies for a while now, which is flexible and dynamic. This feels like going back to the highly documented ways of the past. Why build all these tests first, when you dont know how the final design will be anyway, thats the point of Agile, its flexible. But let me see how it goes, working on a new assignment with TDD, I'll come back in 6 months and let you know what I think.
Please do, over that period of time I am pretty sure that you will have changed you mind. 😎
Please any update ? What do you think now ?
Super informative; thank you!
Can someone please explain to me how this differs from Requirements Driven Design? Here we say we start with defining tests that represent functionality and behavior we want to see in a system (not speaking in the context of code only). Presumably, to fully define the test, you have to have some metric of success. How does that differ from writing a functional or performance requirement prior to establishing the actual system itself? I only see a difference in the way we consider the metric we establish: as a test or as a requirement. With requirements, test cases are also established so are we just talking about the same thing two different ways?
What I will say is with requirement driven design, the norm is to perform testing as a verification event (a one off exercise that should be passed). With TDD, testing is seen as an incremental or iterative effort where tests are expected to fail as the system is developed, until the product is finally completed and all specified tests pass. Regression testing is inherently woven in that way.
Curious to see what people have to say about TDD vs RDD or other philosophies of design.
most badass intro ever seen on YT
🙊 😂
When you wrote performance-critical code for the exchange, was profiling part of the Refactor stage?
Yes. We also did a lot of performance testing and used that for profiling.
so, "test driven development" should really be 'specification driven design'... Quite a useful perspective.
Yes.
hmm.. As a Jr dev.. I'm starting to see some of Agile's cons. The constant requirement changes.. I'll add this to my watch later list. Maybe you address how you can do TDD when requirements are constantly changing.
i used to think that agile and tdd does not work together since you have to do the same work twice with changing requirements.
the fundamental challange with the agile approach practised today is that you are changing existing code which violates the open closed principle.
agile should be lived on a higher level by embracing strategic pivotation (maybe killing a module, or wiriting it new)
i am sure you know that the typical workflow for agile team is to write down the user story and then to extract task from the story to create as much value as possible for the people ending up using the software.
the flexible part is at the user story level, not at the task level
How do I write a test for a function which does not exist? Like computing the length of a vector. Perhaps I have a data class which stores a tuple of (x,y,z) or something. A feature I want is to compute the length of that vector. But if I dogmatically follow the rules of TDD, I must write the test before I write "def get_length(vec: Vector) -> float". But now my test won't even run/compile because WHERE DO I IMPORT THE FUNCTION? There is not a function to test.
Perhaps I can stub out the function, in my case return 0 or something, just so that the test at least has a thing for which to test. But haven't I broken the rule by writing code before I write the test before I write the code?
Maybe my autism is showing and I'm taking things too literally, but for me, this is why I write tests _after_ writing my code. So that my f*5&king tests will at least run/compile.
A few words in favor of independently written test automation.
If a dev forgets to code a case emergent from parameter combinations, and he wrote the tests there won't be a test to catch it either..
Can't stack two slices of the same cheesewheel without some holes lining up.
Thanks Great video. To my experience so far is that using a fake class instead of a mock is much simpler when contracting the test most of the time. Of course that approach only applies for simple fakes.
Awesome video, thank you daddy
You're welcome son!
Any tips on doing this when building the GUI. I've struggled to get good front end tests.
I'm kind of confused. How can it mean something when the test failed the first time? The no code to try the test on, chances are you will create an instance of a non-existing class or you will call a method that hasn't been implemented yet so it's obvious the test will fail even if it's useless so the fact that the test fails doesn't mean that it's testing something useful. Can I have some help here please?
How can I detect software development project failure earlier ?
Treat every change as an experiment and monitor it in some way. Make change in small steps and you get more chances to monitor. The aim of treating things as an experiment, is to force you to think about what you are trying to achieve and how to measure its success. Want better stability? Measure change failure rate and mean time to recovery. Want more users, measure user sign-up and so on. Define a goal, an impact that you intend to achieve overall, and find a way to track that. Do you changes move you toward, or away from the goal?
It is going to be imprecise and subjective, but that is how you spot failure early. Work incrementally, in small steps, and measure.
Hi Dave, great stuff. Unfortunately, the link for the research on project failures doesn't seem to work. Is there someplace else we can get that information? Thanks again.
"Or if I was slightly crazy I could create a car with a jet engine...". People trying to break land speed records - Who are you calling slightly crazy? We are completely mental!!!
So I test a TDD for a 2 weeks and my conclusion is that:
1)you build app faster by not waiting time on what will be needed
2) it help you to split big problem on small component
3) you find small problems way faster
If you are not really that smart(let be onset with our self ), TDD is the way to go.
I do know TDD only on my home project but I will implement it in my next projects.
The only problem I have now is how implement TDD to existing project
I have a free course on "refactoring legacy code to testability" here courses.cd.training/courses/refactoring-tutorial that may help.
@@ContinuousDelivery thx, after i write this comment youtube suggest me your video about it. Im on video 2.
are there ways to practice all these things that charecterises "good code"? I will definetly be using the next 2 weeks to practice TDD but I also need to practice the things you mentioned like loose coupling etc
I think that TDD will help you with that. It tends to naturally "encourage" a more loose-coupled approach to design. My new book "Modern Software Engineering" (out in Dec) explores these ideas in more detail with a few code examples, so may be helpful.
I joined a new company recently and when I went over the code i saw there there were not tests, when I asked my manager why he told me that the codebase is not complex enough to write tests for , i don't agree with him but I'm not sure what to do , any advice?
Thanks for your videos I've been binging on them recently
Obviously I agree with you, there is no code that is too simple to test. If they ran the code before they released it, then that is a form of test. So now the discussion is really how to test efficiently, for which we need automated tests and TDD. How to convince others of this though us a different problem. In your place, I'd at least write tests for my own work, and try and convince others of the benefits, and in my case, if that didn't work I'd look for a better job.
@@ContinuousDelivery Hi, thank you so much for your comment i really appreciate it . Yeah it seems that some sort of manual testing is done but I think it's highly inefficient and prone to bugs. And after going over the codebase i really don't think that it's too small to test, testing help us write more maintable goal which will also help feature devs joining the project later. Thanks for your advice I'll try taking your advice, i hope I can make a change.
On a side note, I'm really enjoying your new book ,thanks a lot for writing it . It's an honor to learn from someone such as yourself
I don't understand at all how this leads to better design. I do see that it does help to minimize errors by creating constraints to what its expected of the units being tested to ensure "they deliver". But guaranteeing delivery doesn't necessarily make for better code design. Perhaps I'm missing something on this philosophy?
I describe this in a lot more detail in my book, but fundamentally it is that TDD drives you to increase modularity, cohesion, separation of concerns, abstraction and reduce coupling. All of these are markers for high-quality in code. It is hard to do TDD, unless you improve these things, so TDD acts as a kind of "talent amplifier" for developers.
@@ContinuousDelivery I agree all those are indeed markers of quality code, but I fail to see the connection as to how TDD drives their "amplification ". You still need to start with a well defined idea of the requirements of your unit to test anything, and to me it is this that drives those markers , understanding what your problem really is. Testing them is crucial to ensure boundary cases, exceptions etc, are being handled and expected by the unit, but one can still get away with terrible design and meet these tests.
I suppose I'll go read more on it as you suggest.
I struggle with testing. I keep watching videos about it heh. My tests are apps that print out data. And I just am looking at the output to see if it's working. Which is probably wrong heh.
The first video on my channel is a simple worked example of TDD. The sound isn’t great, it was my first video, but I think that you might find the ideas interesting. th-cam.com/video/xUi2951ufaw/w-d-xo.html
Isolate the logic part (i.e. the formatting) from the printing part. Test if the document is build as expected independently from moving it to the printer.
@@kevinfleischer2049 Imagine he is writing a printer driver and follows your advice... :-)
I have been enjoying your videos quite a bit and have learned a lot.
Man one criticism if I may about this playlist is that I can't just turn it on and do some work physically and listen... which is normally what these types of videos are good for, and that is because your ads play for way too long :(
Otherwise, great information
IMO,your example of writing, 'testable code' was shifting test code into the production code. Rarely a good idea.
I would have just accepted that you cannot assert that the engine had started and relied on a later test - maybe asserting an increase in speed or rpm after accelerating - to catch that failure.
Both rpm and speed would be integration tests. Testing the integration between the instruments and the engine. So this would be caught later in the pipeline.
Not ideal but preferable to adding code to make your engine 'testable'.
Hi @Bradley Atkins,
two questions - I assume we have different point-of-views on quality and productivity :-)
a) Don't you want to be sure that the small pieces are working, and know that as soon as possible, also with a direct check?
c) Don't you like the consequence that a Car is able take other (future) types of engines, a consequence of design change?
Hi, I would very much appreciate a link to the research paper you referencing to. Best Regards
I have added it to the description, but it's here too: bit.ly/3bVdLwb
I am an bit confused, i thought for 6 years of programming, that it would be normal and every programmer, would be programming like that xD
Are there realy programers, how dont do tests all the time? 😅
Me too! Sadly there are lots of programmers who don't practise TDD.
Why Most Unit Testing is Waste
By James O Coplien
so yes test, but test driven design means that you place testing above all other goals. I know later you will get into behavioral design and you argue that that TDD is behavior as you are testing functions, but then ODD could make the same claim as they literally are a collection of functions. No behavior and design do not happen at the functional or object levels. Behavior such as factory patterns and guard clauses for early exits on complex functions are not effected by the paradigm chosen. Behavior is more about creating a system or engineering for the problem at hand and may not and often dos not include coding of any sort. Here is where flowcharting, data modeling, pattern constraints, and more behavior features are added to the project. It is those behaviors that then drive both TDD and ODD. If you are you using like ReactJS that uses Flux concepts, then component driven design, by separating concerns of HOC's to given a framework for display and pure components to actually display data. In these ways we are doing SOLID principle without regards to the paradigm and language used. This is the true essence of BDD and is not to be confused or obfuscated by other design principles.
13:45 Would not be an amplifier, if it made bad programmers less bad.
It is lazy to speak about a site, for instance that "cyber dojo", but not to put it in the description. Although you never forget to put there your own links. There are many sites with "cyber dojo" in the description. This attitude directly translates into your poor numbers of subscribers and views.
I like it for learning but in the real world it makes the team slower.
It may take time for people to learn, but decades of studies show that TDD reduces the overall time taken and improves quality, because of a very significant reduction in the number of bugs, much less time spent fixing bugs, and positive impact on design.
Hmmm TDD is cool and all, I like it and want to get better at it. I just don't think one should go crazy with it. Just like every programming thing people get all crazy about some method or other that will make better code for you. At the end of the day TDD or not, hot programming paradigm or trending programming thingy or not, you yourself need to improve skills and knowledge about computers and to think about problems and problem solving. I mean think about it this way, you do TDD for 3 years, now your code is better on all levels. But why? Because of TDD alone, or because you have 3 more years of experience? I mean it was not because of TDD you researched some programming thingy for some problem you had, and it was not because of TDD alone that you consumed that information and learned about it. Think about it. TDD is a great tool, I just am not super impressed by it no matter how hype it is :)
Test Driven Development... when your software passes every one of your 800 million autogenerated test cases and crashes the first time it gets to interact with an actual user.
I think you are doing it wrong 🤣🤣🤣
@@ContinuousDelivery Yes, if you think that you can predict the most stupid thing a user will do with your product, then you are definitely doing it wrong.
It will make a good programmer gooder and a bad programmer badger.
Don’t write a line of code unless a test demands it.
Yup! 😁
I don't believe in TDD. To certain extent it can be helpful. From some point it must hold down efficiency and flexibility. Masters put refactoring in the center. Not test.
The guy literally just explains unit testing as some new thing that is somehow different. 17 minutes wasted.
too much exaggerated comments to convince audience the importance of TDD, actually how many projects use this way to delivery? talk about the process not incite
@mrtestdrivendevelopment