Beware of videos, blogs and courses on TDD. Most add steps that don't belong or miss important points. This results in TDD not being adopted. Test-Driven Development by Kent Beck is the original text and the source of truth on TDD. This video and other videos on this channel seem to be consistent with Beck's original teachings. So, that is wonderful! Great channel!
@ContinuousDelivery Жыл бұрын
Thank you!
@M1SMR2 жыл бұрын
I have started to use TDD for every line of code I write in 2022 and it truly changed everything! I can recommend the book "Test Driven Developement by Example" from Kent Beck, it is a great way to learn TDD.
@nox5282 Жыл бұрын
That don’t sound very organic, but if it works good for you. I work best when the tests are basically user stories, so that there are no implementation detail at all. I have been thinking that we should seperate unit tests and component or part test, testing different building blocks should be a debugging tool but not run each build
@wuilliam3212 жыл бұрын
Great video! I'm a big fan of TDD, I'm a big promoter on my company, some of my partners are now TDD fans, and their code looks great, very professional. We really need more videos like this one.
@MatthewChaplain2 жыл бұрын
5:00 I've had a number of components that have gone through a redesign. My TDD'd unit tests have helped me here because, at the end of the day, I usually want the same overall behaviours. What this means is that I can comment my tests out and re-enable them one by one, rewriting as necessary in the new design. In this way, because I have already specified the complete behaviour before, I find that I get much higher quality re-designs where I can be sure that I have not accidentally introduced regressions. As a side-effect, if it suddenly becomes hard to express an old behaviour in the new design, I get to re-think whether the new design really is better and make a proper decision about that.
@ContinuousDelivery2 жыл бұрын
Yes, as I say in the video, I find it easier to change my code when I have tests than when I don't. Actually, not sure if "easier" is the right word, it may take a little thought to figure out how to move the test and the code, but safer certainly, and well, just better, even if it is a little more typing and a little more thought sometimes. Most of the time it is less of both.
@florianfanderl66742 жыл бұрын
This so much 100%. A few years ago I created a domain model that had a bunch of rules. Over time requirements got added and I think I had to reimplement the model 3 times, because my model did not fit new requirements. I literally through away the whole implementation and started from scratch. I luckily did not have to change the tests. But it's such a great feeling when all your tests are back to green after changing every single line of code. That convinced me completely and I will never not do TDD ❤ when it was hard to get all of them green, it also helps to comment out some tests just get your focus back. And then go one by one.
@florianfanderl66742 жыл бұрын
I was a little worried before this video. Now I definitely know that I have solid basics. Still some nice nuances, especially the part at the end, which says that sometimes you have to change your tests.
@gauravpareek37832 жыл бұрын
True even I felt the same
@madmanX13142 жыл бұрын
I actually liked the video before even watching it.
@upgradeplans7772 жыл бұрын
You'll like it even more after 🙂
@gerelltroche2 жыл бұрын
Dislike, like, rewatch
@pianochess18822 жыл бұрын
@@gerelltroche underrated comment :)
@Mr_Bob84 Жыл бұрын
One of the important things I have learned about test driven development is that even if you can't adopt it for some reasons (oh, there are always plenty of them), you must at least be "test aware". If you're not completely "test driven", but "test aware", just asking yourself or developers you work with a question "how will we test it? And if not by hand?" can influence dramatic changes in the design of code.
@StephenMoreira2 жыл бұрын
Still my biggest weakness as a developer. Every time I have a bug I'm like....a good test could have gotten that way before it got to production.
@purdysanchez Жыл бұрын
11:00, this is the heart of TDD. If you cant replace the code and still have the tests pass, you're not doing TDD. TDD is inherently bound to interfaces and design.
@denisitskovich9285 Жыл бұрын
What is so bad in component tests? Component tests allow testing the integration of all the subcomponents within the component, while isolating it from the outer world. These kinds of tests are not supposed to be a replacement to more granular unit tests, they also should not be avoided. Because if you avoid them - the first time you will find inner integration problems will be only in full system integration tests, which are much more complex to debug
@christianschafer37242 жыл бұрын
Dave, you mention that it is a mistake to use TDD on legacy code. I think dealing with legacy code and tests is a major part of most developers work. Can you elaborate a bit more on what exactly do you see as the problem? Could you produce a video on how to improve legacy code and especially it's tests? Many thanks for this great channel! There is almost too much excellent content here.
@DavidRodenas2 жыл бұрын
If I understood correctly, he explains that we cannot add tests to legacy code. He says, "retrofit TDD style Unit Tests", which, TDD style Unit Test is not TDD, just tests that they look like being created with TDD. TDD requires making the test fail first, so we know that tests are reliable. But if the code already exists, we cannot add those tests making them fail first, so we cannot trust them. One trick (that sometimes I use when updating tests) is breaking the code to make sure that the test fails correctly, but there is another thing. The best thing (one of them, it has too many best things) about TDD is that tests capture the intention of the feature that you are implementing. They become part of the specification. If you create the tests later, that specification is lost, and you are just testing whatever logic was implemented (no matter if it was desired, or accidental). So, I believe that is the second one. I recommend you to read "The Two Disks Parable" that I have here: drpicox.medium.com/ac1a16803c58 It's short by I believe that it collects the final idea.
@ContinuousDelivery2 жыл бұрын
@@DavidRodenas I like your "Two disks parable" 😎
@ContinuousDelivery2 жыл бұрын
The point I was trying to make is that code that was written Test-first is a different shape to code that wasn't. Reworking code to put it into "testable shape" is a disruptive change, so don't do it for the sake of it. Do it tactically. Refactor code where you need to do work to make it testable. Do all new work using TDD. So I am not saying don't use TDD in legacy code, I am saying, don't restructure code to support TDD for the sake of it.
@DiogoMudo2 жыл бұрын
Dave, I tried to promote TDD on the company I work for, and the seniors Al roll their eyes, saying that "it's a fad", "it's a waste of time", etc. Do you have a solid example of a company that benefits from it with testimonials from the devs?
@MikeStock882 жыл бұрын
Try and find out why they think its a fad, unless you can find out the underlying reason they think that way you won't be able to convince them You can still do TDD though, it makes your life much easier :)
@raymitchell97362 жыл бұрын
Same thing in my company... the biggest problems they say are: 1. takes longer to develop code that way, and 2. we can write the tests afterwards... (but then they don't because they're busy putting out fires) . Lather, Rinse, Repeat. History repeats last 30 years of the company (before I joined) and same guy is still around "influencing" the upper management... they think they're "getting results" I would say that it is despite of that advice and some good engineers that can pull a rabbit out of a ... hat... but not because of him... so it's normal to have chaos and that's part of the normal way projects run because there's no perfect project or design. Does that sound familiar? I've worked in a few places and that's the usual stuff that goes down. I think I can safely post here, I doubt anyone cares about TDD at my company and will watch this video let alone read the comments section.
@chja002 жыл бұрын
Try to get a sense of what they think TDD means. If they just think it means extensive testing (common misconception), then I could see why they think it's a waste of time. Many overburdening test suites have been abandoned over the years because they're just more trouble than they're worth. On the other hand, simple unit testing written in tandem with the code it tests is simply good practice. It's more useful and productive than running the program hundreds of times to test its behaviour. All you really need is a CI/CD pipeline that runs the test suite - beyond that, just write tests as you write your code. Inspire others to do the same. It's much easier to lead by example.
@MikeStock882 жыл бұрын
@@raymitchell9736 It's a myth that TDD makes you write code slower, its just a real hard myth to bust :( It's certainly not a myth that its easier to maintain code that is written using TDD
@MikeStock882 жыл бұрын
@@chja00 I think the testing implementation was a key one for brittle unit tests, rather than behaviour, that could result in multiple tests failing for a simple change I made that mistake in the past, testing how it did it rather than the end result
@-Jason-L2 жыл бұрын
Chasing coverage - this is a Test Last phenomenon. Writing tests to pass - this is a Test Last phenomenon.
@ContinuousDelivery2 жыл бұрын
Yes, but people all too commonly confuse test-last with TDD, as you point out, they are VERY different things.
@johnwellbelove1482 жыл бұрын
As a library writer, I feel there are certain times (though not very often) when knowledge of the implementation must be taken into account when the test is written. For example, I wrote a deque (double ended queue) that used a fixed sized contiguous buffer. It was very important that I tested the various boundary conditions where I *knew* that the data would wrap around the ends of the buffer. The tests were specifically written to set up to create these boundary conditions.
@ContinuousDelivery2 жыл бұрын
I'd argue that what you have described is not implementation detail, it is a requirement - how do you want your system to respond when the limit is reached? Throw an exception, or in your case, wrap around - so now the user has lost their first entry in the buffer, how will you communicate that? This isn't implementation detail, it forces us to think about the user of your code. That's really what I mean.
@johnwellbelove1482 жыл бұрын
@@ContinuousDelivery It was definitely an implementation dependent test. The scenario you gave was not what was being tested. The deque was able to be iterated from the first to last element; the deque was not necessarily full. I needed to check that if the permutation of 'push-backs'/'pop-fronts' to the deque had caused the end index to wrap, then the iteration would still operate correctly. In this circumstance, the end index would be before the begin index. Ensuring that this scenario had occurred depended on me knowing that the size of the underlying buffer for a deque of size N was N+1. If I had decided to implement the deque with an underlying buffer of 3N/2 or 2N then the test setup would necessarily have been different. I would not have been able to ensure that the particular scenario had been tested by assuming an abstract deque implementation.
@leprotto89 Жыл бұрын
@@johnwellbelove148 you probably need to decouple your indexes logics from the behaviour of the deque, so you can test it without having sub systems to implement the real behaviour. Random guess, it happens a lot to me
@mikkolukas2 жыл бұрын
A good mental start is to think: "In reality, tests are just all the requirements we have for our solution, written in a certain syntax."
@BorGyn12 жыл бұрын
Thank you for the informative video. I have a question about retrofitting tests: What would be a good form of tests to retrofit to existing untested code? A while back I added unit test style tests to some existing module, with a far from ideal separation of units along the existing classes. Now I think a different style of tests could have given the same confidence with the change I needed to make while at the same time paving the way for refactoring.
@ContinuousDelivery2 жыл бұрын
There is more to this than I can cover in a comment, there are several modules in my training course on this topic, but the quick answer is, yes, don't aim for great unit test coverage for the sake of it in legacy code. Approval testing and acceptance testing may be more helpful than pure unit tests in this context. Not quite the answer to your question, but you can see me using Approval testing and refactoring some "legacy code" in this (free) refactoring tutorial: courses.cd.training/courses/refactoring-tutorial
@BorGyn12 жыл бұрын
@@ContinuousDelivery Thanks! I will check out the tutorial.
@HKCS-yn5nc2 жыл бұрын
If I understand correctly, TDD is a good design tool for the ports, while CRC cards are a good design tool for the corresponding adapters.
@ContinuousDelivery2 жыл бұрын
No, I don't think so. They are complimentary, and certainly not exclusive. If you really wanted to divide them up like this, I'd probably say that CRC is a bit more at the Ports level, specifically the "Collaborators" part. I confess I don't often feel the need for CRC these days, TDD replaces it, very occasionally I find it useful to stop and think about the CRC picture, and I certainly have a laser focus on "Responsibilities" all the time when I am doing TDD. I want to do my best to ensure that any piece of code has only one responsibility.
@RU-qv3jl2 жыл бұрын
I’d be interested to hear your thoughts on unit testing DBs and the best approach to it. I find that testing causes me to write smaller and easier stored procedures. However, I just test for the behaviour that I expect to see and rarely try and get down to individual statements. I don’t see the benefit of testing each statement and all the extra tests I would need to write. Like I say, your thoughts would be most welcome.
@ContinuousDelivery2 жыл бұрын
Mostly I try not to put too much code in a DB, so I don't need to test it. My main approach is to abstract the "edges" of my system, where I/O happens so that I can test to the abstractions. That makes life a lot easier. I talk about that in this video: kzfaq.info/get/bejne/e7l4oZhk0t-maGw.html I also talk about some other aspects of dealing with data, more generally, in CD here: kzfaq.info/get/bejne/gLaWlc180Z_Okqs.html
@kahnfatman2 жыл бұрын
There's another adjective for bad tests: Detestable.
@ContinuousDelivery2 жыл бұрын
🤣👍
@hypenage24152 жыл бұрын
Dave, you mention that tests should still make sense even after changing the implementation code. This makes sense. But When it comes to changing functionality even slightly, doesnt this mean the test also has to change? Some tests are extremely simple with one or two assertions, does the need of changing a test go against the goal of TDD? To better word my question, how does one manage and use TDD for updating existing code to add functionality? (I am a huge fan and advocate of TDD, just curious to hear thoughts from you on this)
@upgradeplans7772 жыл бұрын
When you have ideal tests from tdd, this means three things: - The tests only test required behavior (so, no implementation details are tested) - The tests cover all required behavior (so, no missing tests) - No implementation code was written without a test that required it to be written (so, very little accidental behavior) At first glance, you might think this means that every reasonable change of functionality will result in a failing test, but this is actually not true. New functionality are usually new requirements that do not conflict with any of the previously required behavior. So, for most new functionality, you can simply write a new test and do the red-green-refactor cycle. None of the existing tests should fail, because they only test other behavior that is also (still) required. Sometimes, it does happen that a requirement becomes deprecated, at which point the test for it can just be deleted. When you have a new requirement that is incompatible with a deprecated requirement, you can just write a new test for the new functionality and do red-green-refactor as usual. If you are stuck in a situation where an existing test fails when working on the "green" step in the cycle for a new test, this could mean you have a bug in your requirements. Maybe there actually is a solution to make both tests pass if you think about it further, OR the requirements really are mutually exclusive which means implementing it correctly is impossible whatever you do.
@ApprendreSansNecessite2 жыл бұрын
"When it comes to changing functionality even slightly, doesn't this mean the test also has to change?" The tests have to change, for if they didn't they would be testing the wrong thing. Your tests are an ever changing, always up to date, runnable encoding of your spec. "does the need of changing a test go against the goal of TDD?" It is true that we don't want tests to be fragile, but this is not what we mean by that: you want to be able to use your tests to guide you while refactoring (that is to say: change the design but not the behaviour). If tests break every time you change your design, you have no way to know if the behaviour is intact. Tests must only test the behaviour so that you can change the design without breaking them. In that sense, we don't want changes to break tests. But design changes only.
@scharlyochoa65292 жыл бұрын
@@upgradeplans777 it makes sense to me, thanks.
@praecorloth2 жыл бұрын
5:11 Tests that test the implementation. Or perhaps...Fragile Development? :D
@ponypapa67852 жыл бұрын
Here's a thing that I still don't know what the best approach is using TDD: Given that I have a class that calls an external service, which is passed into it via constructor - a mock in a test. The logic within the class accepts a "raw" object, does some computations on it, maybe constructs other objects, and passes those to the external service. Then, the service returns another object, which in turn is being examined, evaluated, and then a given result is being returned. Testing the final outcome is, of course, simple. But my class relies on the modifications before passing to the service and after receiving from the service. Currently, I treat the service as an external boundary, meaning I write tests that assert that the service has been called with all the correct modifications, and then write tests that deal with the returned values and the subsequent visible results when the initial sut call returns. Here now is the point: I am explicitly configuring my mocks/stubs to explicitly accept everything and return a configurable object, and I explicitly use a way of verification - usually Mockito.verify - to assert that the behavior is correct. I often get called out on that and am being told that I should instead configure my mocks specifically in every test, meaning "this only returns when the input is exactly this" or similar, and not use verification at all, as the service calls are technically an implementation detail (because everything that is done in the service COULD be done manually in the class itself). So the question is: Which approach is "better" in your opinion(s)? general configuration and explicit verification, or explicit configuration without verification?
@SujaiSD2 жыл бұрын
I have worked and working with a similar design. There are 2 layers which I use to separate. - A Proxy interface used only to wrap the external call without any changes. The Proxy just passes value from the argument to the external service. The objective is more about communication not about the data transformation. - An Adapter or similar class which does the logic as explained by you. The objective is to test the logic of transformation not the communication. Each layer has its own tests and some interaction test just to ensure it works end to end.
@SujaiSD2 жыл бұрын
Explicit configuration only to test the scenario.
@TinBryn2 жыл бұрын
My answer to your question is, neither It seems like most of the work that is actually done in your class is setting up some objects that are passed to the external service, the response is just immediately returned. So the correct place to assert something is on the objects that are to be passed to the service. Mocking out the service and asserting on the objects it returns via your class means you're basically testing your mock, not your actual production code, the only thing your production code is doing is the setup for testing your mock which seems completely backwards to how testing should be. So you should probably refactor a little, first is an extract method that returns the objects that are to be passed to the external service. This method should be private, because as you said "implementation detail". In order to test this method you need to move it onto a new class and change it to public, your existing class will need a reference to this new class (that can be a field of a local variable depending on what it needs, I would prefer local variable if possible). Now you have a class with public methods that do the majority of the work of your old class and don't have any direct concept of your external service so you have absolutely no need to mock that to test it. Hence my answer to your question, if you don't need to mock at all, how you configure your mock becomes irrelevant.
@ContinuousDelivery2 жыл бұрын
It is hard to tell from a description like this, but my guess is that some of your pieces are doing too many things, this may be a "separation of concerns" problem. If so, this is a good example of how TDD helps, and you have already spotted it. You code is hard to test. That isn't a problem to be fixed in the test, it's a problem to be fixed in the design. Try and make each pieces of code focus on one thing. Maybe assembling and modifying the objects for the service is a separate step from processing the results from it? Imagine a piece of glue code. It takes a reference to the service, a reference to some code that prepares the inputs, and a reference to something that processes the results. It's job is to orchestrate this little workflow between the pieces. This would be easy to test! Now the prep code doesn't need a reference to the service at all, and the "process results" part doesn't either. So both of these are easy to test too. These may be a bad ideas in the context of your code, but the principle is correct - aim for Modular code with a very good separation of concerns, it is testable, and ultimately, it's more flexible too.
@mateuszszczecinski82412 жыл бұрын
How do you deal with extract class refactoring? Do create test for new class behaviour, and change test of previous to verify that it called new collabolator? Or you test whole behaviour only by interface of first class?,
@ContinuousDelivery2 жыл бұрын
Ideally a testSuite is focussed on related behaviours rather than a class. So I would probably have separate test suites for AddFractions and SubtractFractions, but both testing one Fraction class. But taking the more general point, if I find I need to extract a new class, and that means move the tests, then I move the tests to a better place.
@THEMithrandir092 жыл бұрын
I'm sure I am still not perfectly designing tests initially. Can anybody recommend and link a github repo that's somewhat small yet adapts good TDD as well as possible?
@michelchaghoury96292 жыл бұрын
Please can you make a demo video on TDD on a Spring Boot Application so see how to think, implement, and practice and when and how to do TDD pleasee keep going
@oscareriksson94142 жыл бұрын
One problem I can't really grasp yet is what is the purpose of TDD? I mean you can test interfaces and make tests pass but it does not say anything about if the code will actually work. I am trying to learn and I just think sometimes it feels like you do the test, not testing implementation, and then the program crashes when running it. According to what you guys are saying, my test was good because it still worked when I fixed implementation, but I just felt it was completely useless. So my question is really, is TDD testing not actually ment to test the actual code but to give a hint at how you can sovle a problem in a certain style of programming? Another question is but what if I really need to test implementation of some critical points in the program? I mean I need to know if the program really does the things when it is running, and some times using unit testing to run some methods is very comfortable and useful to me. Are you suggesting that is all together a bad practise ? I think you should also REALLY test your program no matter how many unit tests you do. Especially if you are not testing implementation at all. I have a difficult time seing what is what with TDD. Only trying to learn I am not trying to argue against TDD. Good videos.
@georganatoly66462 жыл бұрын
I think it might help to take a step back from all the jargon and the dogmatic prescription and look at a higher level why it's important to test the code you wrote regardless of how you might describe the implementation details of the tests themselves. For example, let's pretend I asked you to write me a piece of code that took a string, looked for a specific marker, parsed x number of characters after that marker and returned either just those characters or the original string unaltered. As a user of your code, I'm not going to create a little console program to plug your function into and play around with to make sure it works as we agreed on so instead you can write an individual test for each desired behavior that answers the question, "What does my function do given this input, does it do what we agreed?" TDD isn't about how/what or even when to test but instead why. The why is being: 'I want or need to increase my confidence in that my solution is correct for every situation I can imagine my code being applied to.'
@oscareriksson94142 жыл бұрын
@@georganatoly6646 ok I think we are closing in . Yes of course the user will expect it to do what we agreed on. I just don't quite see how I would feel more confident that my code would work with various different implementations if I (or QA) didn't actually test at least one of the implementations. By solution, do you mean then a diagram of sorts, but with interfaces?
@georganatoly66462 жыл бұрын
@@oscareriksson9414 You're actually hitting the nail on the head when you say, 'I don't quite see how I would feel more confident...' without full integration testing/QA testing, etc. The scope of your personal tests should be a measure of local confidence, in terms of your local changes/updates relative to your current understanding of the production code, basically the next level of testing you're referring to is at the pull request/code review/making sure your code works with the production code. and at that point it's no longer a matter of considering your code in isolation. -- at which point you might learn that while you've done nothing wrong in terms of your code/tests, the solution you're presenting no longer applies and is invalidated: maybe the requirements have changed in some way, or you're understanding of the problem was slightly off target and you ended up solving the wrong problem by mistake and your code is 'wrong' as in it doesn't work with the production code and need to change/scrap some of your work. (I actually had this happen to me this morning, I've been working on a feature for the past 2 days and the morning of the 3rd day discovered that while my solution was correct in and of itself, a core assumption I made was incorrect invalidating my solution, I'm in the process right now of re-evaluating my approach through researching/learning about the area I made the incorrect assumption in)
@Geza_Molnar_2 жыл бұрын
@@oscareriksson9414 You might have a look on the V-model. If I understand correctly your point.
@oscareriksson94142 жыл бұрын
@@Geza_Molnar_ I'll have a look, thank you
@elfnecromancer2 жыл бұрын
I feel there's a lot of confusion on what "testing implementation" means. Do you have an example of testing implementation?
@ContinuousDelivery2 жыл бұрын
I don't have any that I can sensibly share here, but they are very common. There is a very easy way to tell. Can you change the code (implementation) without changing the test? Can you change everything about the implementation without changing the test? It's really a design thing, making a conscious separation between the external view of the code, it's interface, and the internal, implementation.
@Bulkje2 жыл бұрын
Here's a great talk about property based testing. kzfaq.info/get/bejne/f7-qd6l53Zy1gI0.html
@danielc42672 жыл бұрын
In c++, my understanding is that private methods are implementation detail. And public methods are requirements. But I could be wrong. I am a TDD beginner
@ContinuousDelivery2 жыл бұрын
@@danielc4267 Sorry, but I don't think that definition cuts it. Public methods aren't "requirements", they are the external public expression of the class. The interface through which all interactions (let's ignore protected for now) take place. This is the API for this class. Everything behind the interface, even in a public method is implementation detail, that may include private methods, which are really just a mechanism that allows you to decompose your implementation into simpler, easier to understand pieces. Private variables are part of the implementation too, while public variables are part of the API. Think of the public API as the "contract" with the outside world.
@Sergio_Loureiro2 жыл бұрын
This guy is a video flavour of Joel Spolsky v2.0.
@HoD999x2 жыл бұрын
i've never seen a test without assertions... who does that kind of thing?