#58 - Principles for Writing Valuable Unit Tests - Vladimir Khorikov - podcast episode cover

#58 - Principles for Writing Valuable Unit Tests - Vladimir Khorikov

Oct 04, 202153 minEp. 58
--:--
--:--
Download Metacast podcast app
Listen to this episode in Metacast mobile app
Don't just listen to podcasts. Learn from them with transcripts, summaries, and chapters for every episode. Skim, search, and bookmark insights. Learn more

Episode description

“The main goal of unit testing is to enable sustainable growth of your software project that enables you to move faster with a more quality code base."

Vladimir Khorikov is the author of “Unit Testing: Principles, Practices, and Patterns” and the founder of Enterprise Craftsmanship blog. In this episode, we discussed in-depth about unit testing. Vladimir broke down the four pillars of unit testing and the anatomy of a good unit test, as well as mentioned a couple of common unit testing anti-patterns. We also discussed topics such as test-driven development, code coverage and other unit testing metrics, test mocks and how to use it properly, and how to be pragmatic when writing unit tests.

Listen out for:

  • Career Journey - [00:05:32]
  • Unit Testing - [00:08:20]
  • The Goal of Unit Testing - [00:11:34]
  • Test-Driven Development - [00:12:55]
  • Code Coverage & Other Successful Metrics - [00:17:35]
  • Pragmatic Unit Tests - [00:21:04]
  • 4 Pillars of Unit Testing - [00:23:40]
  • Anatomy of a Good Unit Test - [00:34:01]
  • Test Mocks - [00:38:16]
  • Unit Testing Anti-Patterns - [00:47:05]
  • Tech Lead Wisdom - [00:49:56]

_____

Vladimir Khorikov’s Bio
Vladimir Khorikov is the author of the book “Unit Testing: Principles, Practices, and Patterns”. He has been professionally involved in software development for over 15 years, including mentoring teams on the ins and outs of unit testing. He’s also the founder of the Enterprise Craftsmanship blog, where he reaches 500 thousand software developers yearly.

Follow Vladimir:


Our Sponsor

Are you looking for a new cool swag?
Tech Lead Journal now offers you some swags that you can purchase online.
These swags are printed on-demand based on your preference, and will be delivered safely to you all over the world where shipping is available.
Check out all the cool swags by visiting https://techleadjournal.dev/shop.


Like this episode?
Subscribe on your favorite podcast app and submit your feedback.
Follow @techleadjournal on LinkedIn, Twitter, and Instagram.
Pledge your support by becoming a patron.
For more info about the episode (including quotes and transcript), visit techleadjournal.dev/episodes/58.

Transcript

The main goal of unit testing is enabling sustainable. Growth of your software project. This is the main goal because as the project grows the amount of code in that project increases and with a it increases, the surface area for potential box in that code. So basically the more could you write the more probability there is that you introduce a buck in that code unit tests, help you to avoid this situation so you don't introduce any regressions in those.

Features and also they allow you to refactor your code more freely because you're not afraid of doing that. So you're able to maintain the quality of your code. And therefore you're able to move faster Glitz more quality. Cool piece. Hey everyone. My name is Henry Surya be Robin. And you're listening to the

tekhelet journal. The show will be bringing you the greatest technical leaders practitioners and thought leaders in the industry to discuss about their Journey ideas and practices that we all can learn and apply to build a highly performing technical team and to make an impact in your personal work. So let's dive into our Journal. Hello, everyone. Welcome to another episode of the tekhelet journal podcast. Thank you for tuning in and spending your time with me today, listening to this

episode. If you haven't, please follow technology, you know on your podcast app and social media channels on LinkedIn Twitter and Instagram, and if you have been enjoying this podcast, consider supporting the show by subscribing at technology, no, dot f /, Patron, and support me to continue producing, great content every week.

Unit testing, as a software development, best practice has been around for a long time despite the history of it and the major benefits, that unit testing brings from my own experience. I still witness. There are still many software Engineers who do not understand the concept, and even for some who understand it. They do not practice it in their day-to-day software development

work. Another thing that I observe from my own experience is unit, testing that is not done, optimally ranging from flaky tests. Sometimes pass or sometimes fail randomly that normally just require a rerun brittle, test test that easily break.

Every time we make any changes to the code under test test without any assertion or unit test code coverage that has to be mandated from the top of the organization that could introduce wrong incentives and culture in order to spread more knowledge about unit testing and its best practices for today's episode. I'm happy to share my conversation with Vladimir korotkov.

Vladimir is the author of unit, testing principles practices and patterns and the founder of Enterprise craftsmanship blog in this episode Vladimir. And I discussed in depth about unit testing Vladimir broke down the four pillars of unit. Testing a very important principles that I find very insightful to a deer. For our unit testing practices and also the anatomy of a good unit tests as well as mentioned a couple of common unit. Testing & Repair.

And we also discussed topics such as test-driven development code coverage, and other unit testing metrics past, mocking and how to use it properly. And how we can be more pragmatic. When writing unit tests. I really enjoyed my conversation with Vladimir discussing many things about unit, testing, clarifying, common misconceptions and anti patterns, and improving my own understanding of unit testing, and I hope you will find some unit testing insights from this episodes as well.

L consider helping the show by living it, a rating and review on your podcast app or comments on the social media channels, those reviews and comments are one of the best ways to help me get this podcast to reach more listeners and hopefully, they will also benefit from all the contents in this podcast. Let's go to our episode right after our sponsor message. Are you looking for a new cool swag? Taglit Journal. Now, offers you some swags that

you can purchase online? These wax are printed on demand. Based on your preference and will be delivered safely to you all over the world where shipping is available. Check out all the cool swag is available by visiting technology, you know dot, f / shop and don't forget to break yourself. Once you receive any of those tracks. Hello everyone, welcome back to another new episode of the pack leader of podcast. Today. I have a guest with me. His name is Vladimir Corey cough.

He's the author of the book unit. Testing principles practices and Patterns. So today we'll be talking about unit testing and how to do a unit testing of the best practices tips and tricks. And what you need to do in order to come up with a good code coverage for your software. Interestingly, bloody made also is a popular author in plural site.

So if you look at his profile in plural site, you'll see a number of courses that he has done over the number of years, including topics like domain driven design, cqrs some C sharp courses and things like that, so really, The top a lot about software best practices with you today in particular about any testing Vladimir. Welcome to the show. Thank you. I thank you for having me so that they may be in the beginning for people to know

more about. You. Could, you introduce yourself may be telling you more about your career Journey or highlights and turning points? Sure. So, if we start from the first beginning, I started to program professionally and 2005 about seven years ago, I moved from Russia to the US and about that same time. I started blogging at Enterprise craftsmanship.com. So that was one of the turning points in my career. By that time.

I was already pretty established as a professional programmer, but writing on my blog, forced me to learn even more than I did before. So, to put my thoughts down, I had to basically learn much more than before in other add turning point. For me, was when I Indeed, joint pluralsight. I did an audition with them. They liked my test course, so to speak and they accepted me as an author on the platform since then I created several courses about wealth or something like that.

A lot of them were as you mentioned about domain-driven design. They also authored a learning path with the same subject. Most of the courses in that learning paths, our mind about the main models, cqrs, and so on. The couple years ago. I also started to think about writing a book, the two topics that were the closest to me or about the moon giving design and also about unit testing. So, these are the topics that I

enjoyed the most. So I thought that maybe I could write a book on one of these subjects. I thought about DDD for a while about the mentoring design, but I decided not to write a book on that topic, because although I think people, Like my horses about them into design. I don't really think that I have a lot of new stuff to say, on that topic. There are quite a few of good books already on the market that covered all.

Or most of the stuff that I teach on puerile site about the Mandarin Zayn and so I decided not to write about that. But with regards to unit, testing this situation here was different. So there were quite a few books already but the walleye the targeted. Beginners, they didn't basically teach it the material that I wanted to teach about unit testing.

So basically, there wasn't a comprehensive material for people who are not beginners, who already mastered the foundation walls, and who wanted to take the next step towards intermediate, or advanced level. And for those people, there weren't a lot of materials on the web. So I decided to write my own book on that topic, so, it's been a pleasure to talk about, you know, Testing in particular, especially for you who have been around in the industry for so

long and also have practice. A lot of things about these software best practices. So in the beginning, maybe we can recap a little bit. What do you think is the definition of unit tests there? I think two definitions that are applicable here. One has some more high level and the other one is more low level. So the high level definition is that you can say that a unit test is any task. That is real. Eaten by developers by the same.

People who write the coat that is covered by a dose unit tests. More, low-level definition would be something like a unicast is an automated tasks, that covers a single unit of behavior. It does it quite fast quickly. And also it does it in isolation, from other unit tests. So these are the three properties of unit test. If an automated past, doesn't meet any of these three properties, then it's not the unit.

Asked it's an integration test. And then if we go hire them as pyramid, there are also end-to-end tests or functional pants or whatever. You may call them. Those are a subset of integration tests and the line between let's say end-to-end tests. And integration tests is much more blurry than between unit tests and integration tests. But even the line between unit tests and integration tests is not as clear.

And so, sometimes I just put Both unit tests and integration tests and even end-to-end tests into the whole bucket of unit tests. So tests that are written by developers themselves. So, speaking about unit tests versus integration tests. I know they are always this long debate about the, you really need unit tests. Can we just do integration tests? What do you think about this argument? Yeah. This is an understandable point. I would say that I disagree with that point, but it is

understandable. Comes from. So I would say that people who argue against unit tests. If we were all feet aggression tests, only argue from the standpoint of their unit tests being too low value. So they are not valuable enough to keep around in the project they fail with each refract from. So they are fragile. They are brittle and they don't introduce a lot of protection against potential box.

And so, from that perspective, I understand why they would say so Integration tests on the other hand, they can be more valuable because they cover a larger slice of cold. They have a higher probability of catching a regression a book and they also tend to feel less frequently. They tend to produce less false positives, false alarms to that point. I would say that it just means that you don't write unit tests properly. You're probably writing them in a way that makes them brittle.

And in a way that doesn't provide. You lot of overall paleo. The better approach here is not to get rid of unit tests all together but instead refactor, your unit tests and make them more valuable. So maybe if you approach to the argument of unit tests, maybe can you define some of the benefits of goals of actually writing unit tests, especially for developers who are still not yet into unit past practices. The main goal of unit. Testing is enabling sustainable growth of your software.

Eject, this is the main goal because as the project grows the amount of code in that project increases and we the it increases the surface area for potential box in that code. So basically the more could you write the more probability of there is that you introduced a bug in that code that in turn leads to a situation where you or lease a lot of box with each deployment to production or you spend so much time when you look testing that release that Time-to-market metric, suffers

drastically, unit tests. Help you to avoid this situation. They act as a safety net. That ensures that when you introduce new features in your software, the old features stay operational. So, you don't introduce any regressions in those features, and also, they allow you to refactor your code more freely because you're not afraid of doing that. So, you're able to maintain the quality of your code. And therefore you're able to move faster Glitz me.

More quality. Cool piece, spaghetti about unit test. A lot of people. Also associate that b PD like tests pass, or is it like production could first? Do you have any flavor around? Which one do you think is best test first versus test last verses cooled, first approaches. I didn't cover it in the book. But yeah, I do have some opinions on that. So I do believe that tdd test-driven development or test.

First approach is where you belong, but you need to apply it in the Right moment of your project. I would say the general guideline here would be that when you just start with the project. When you don't have enough information of what you're doing, or how your main model would look, or how your project would be structured. This is not a good time to apply test-driven development because here you are in the mood of exploring, your problem domain, exploring your possible

solutions. And here are your tests will only drag you down. At this stage, I would recommend to do some outlining some prototyping. When you are comfortable enough with your problem domain, and you're pretty sure that the way you are implementing. Your domain model is to be, you're going to move with moving forward when you're sure about that, Dan, you can apply the test driven development approach, start with tests.

And then basically in forward to it, this approach that during development is especially useful when you all Do you have some code base? And even a test suite and you need to, let's say fix umbach. This is where the test driven development approach shrines because you are able to first put a test that reproduces, it the Box itself and then fix the BOK this way. Your are sure that your test is

correct. Because you saw that this just feels for good reason because there is a bug in your code base and then fix the park and then make your Test pass basically test-driven development. Solves two problems. It allows you to check that your test itself is correct. I would say it allows you to do that more easily because we've

cooled first approach. You still can do that, but it's just a little bit harder to do because when you write your code first and then you write your tests in order to past that past you have to modify your coat back into incorrect. One to see that your test fails first and And you modify back and see. Then the test started to pass. So with test-driven development with test, first approach, you avoid these unnecessary stamp because you write your test.

First ensure that it fails for good reason, and only Dan you fix that past, and make it pass. So yeah, that's the first benefit of T DT. Is that it helps you to check your tests more easily. The second benefit of T DT, is that it helps you structure your development process. Process. When you have a specific goal in mind, he can introduce a high level test that checks the

specific functionality. And then, you can also introduce lower level test unit tests, when you implement specific pieces of that functionality and so, it provides some structure. So get that's in the second benefit of test-driven development, but even without tdd, you will capture a lot of benefits. If you just apply UD testing, even if you write unit tests after your Your coat. Thanks for highlighting all these benefits. I also learn from other people, and also from my experience.

Sometimes the tests pass approach could actually lead to better design, simply because you think from the perspective of the client, or the user of your production code, and then you come with the task force, which then leads you to a better design, maybe more testability in mind, and things like that. Do you have any thought around test drive?

See that the design? Well, I would say, testing the ability at to test your called ears and necessary but not sufficient requirement for good quote design because I would say that it's a good - indicator but it's a bad positive. One when you're cold, doesn't allow you to easily passed that code? Then it's a good sign that there's something wrong with that coat.

It structured thing correctly. Maybe you didn't Implement dependence injection properly or something like that, but even if you are able To cover this code with unit tests or with integration tests, even then it's not a 100% guarantee. If they aren't your code itself is structured properly. So, I would say that it does help with cool design. But only partially, a lot of times when we write unit tests. We associated with code coverage.

So almost, every time when someone is writing unit tests, they will care about code coverage. Maybe you can explain a little bit why the emphasis on code coverage or if there are any other Successful Matrix for people to use when they write unit tests, code, coverage shoes and very popular metric. Because it's may be the only metric regarding unit tests that you can apply automatically. So you can use some to link that will give you some specific number and you can track that number.

So that's basically one of the reasons why people like this metric. So much, in terms of its merits, I would say that I would apply the same framework here. The same framework. As with the ability to test your Walt called coverage metrics are also negative indicator, but they are bad positive one. That's because if you have a low coverage number, let's say anything below 50 or 40 percent. That's a good indication that you don't test enough. You don't have enough unit tests.

But even if you have a high coverage number ninety percent or even 100%, it doesn't guarantee if that your tests are of good quality. You can still have tests that Dawn. Don't properly test your code, a contrived example, here, would be assertion for unit. Testing. It is when you right past sit that execute your production code, but they don't assert

anything. This is a simple and easy way for you to bump up your coverage, metrics up to 90 or even 100%, without actually trying because those tests are will, basically useless and they will not feel and they don't check anything. And you mentioned a little bit about what you should aim for when you start with unit tests, right? So there are three important things that you cover their, which is that the test should be integrated with your development cycle.

And then you should Target the most important parts of the code base and you should get maximum value with minimum maintenance costs. If you want to explain more on these three properties p.m. The only way to benefit from test, is when you use those tests, this is why Why I put an emphasis on that point, that you should have those unit tests, but integration tests as well, integrated into the development cycle so that you run them as often as possible.

Ideally, you should run them after each called change, but of course, it is not always feasible, but it's the ideal and then your unit tests also should Target the most important parts of your code paste first because not all your code base is equally important. There are some parts that are more important for the Checked. And there are some parts that are less important. For example, business logic, or the main model is at the most important part of your project.

And this is what you need to cover first. When doing unit, testing some infrastructure codes are maybe utility classes, the might be important, but they are usually not as important as business logic. And the third Point here is that you shouldn't just write tests for the sake of unit test. You should be pragmatic about it and you should email. At getting the maximum possible value of those tests with the minimum maintenance costs.

That is what I cover in the rest of this book because this is easier said than done in the remaining of the book. I cover how exactly can do that. So when you talk about being pragmatic, right, A lot of people has a certain number of coverage that they would aim for some. Put it pretty high somebody in the middle. What do you think is a good prank mattock number for code coverage? And then when you say about minimum maintenance, cause, right?

Can you explain a little bit, why some unit tests could actually create a high maintenance cost? Yeah, that's a good question. So, in terms of the specific Commerce number, I wouldn't say intent. There is any specific number and that is good for any project. I wouldn't even put specific coverage number as a target for developers because you often can see if that there is some coverage number. Let's say 80% or 70%. Ain't that is put as a Target and your built fails.

If you don't achieve that Target or keep this, number goes below that limit. This is not a very good practice because it introduces perverse incentives for your developers. So you may end up in a situation where the you write a lot of all over your past said that exercise basically useless cold or cold. That is not very prone to bugs

or even as I mentioned earlier. Can just omit some assertions because they are hard to implement and you just exercise your code for the sake of increasing, these coverage number. So first of all, you shouldn't fail your bill because of some specific covers numbered at most, that should be an alert that alerts your developers, which prompts them to investigate.

Why the converse number has become so low, but the rule of thumb here would be if you can, you should separate your domain model from all other parts of your project. The main model should Should have a high coverage number. So this is the only part of your project where you can possibly Target some High coverage number. Something like 90%, that would be justified in the vast majority of cases, but you shouldn't apply these magic

blankly in your whole project. That would be counterproductive. The second question is about maintenance. What do you think? Like way are the place where sometimes in unit tests? You might have a high maintenance cause right there. Elapsed several? But the most prominent the most notorious area for tests, being low value is when they are brittle.

So, when people write tests that fail with, no good reason after basically each refactoring and we can talk about this more because this is one of the pillars of unit testing that I also described in the books, one of the four pillars. So maybe let's move on to that four pillars to to describe the different pillars that you have for a good unit test. So the frame I work with that. I tried to lay out and it's about four properties of a good unit test four pillars as I

called it in the book. They are first it is protection against bugs. The second one is resilience to refactoring. The third one is fast feedback and the fourth one is maintainability. So let's start with the second two and then we'll go back to the first two because the second two are easy to explain fast. Feedback is pretty self-explanatory and it's about how Your test, execute this metric is important because the faster, your tests execute the quicker.

You get the feedback if your code stops working properly. This saves you time. Moving in the wrong direction. You need to. Just keep in mind that a block found during development is basically Costless to fix. It doesn't take you anything to fix this part but a bark that is found in later stages of development. Let's say create or even in production, it requires orders

of magnitude more. Effort to fix the bug found in development and so you want to push those box as far back to the development stage as possible. Fast unit tests, help you do exactly that. The fourth metric is maintainability and it's a function of how easy it is to read your tests and understand them which itself is a function of whiskey.

How large your test is because larger you test the hardest on the stand that past also they maintainability metric the And subcategory that metric is how easy it is to maintain dependencies with that test operational. If your test works with out of France, dependency is such as the data police, or the file system, or any other third party systems, you will have to maintain those dependencies for that test. You will be ski.

Have to make sure that your database who science in the proper state that your network connectivity is fine and so on and so forth that also adds up maintainability. Costs for your tests. So that was the third and the fourth metrics. The first and trick is as sad is protection against box or protection against regressions.

It is How likely your test to catch the bug if you introduce that bug in your code base, so that's basically a function of how much of your coat your test executes, the larger that slice of CO2 detector test executes, it the higher the probability that your test will find the bug and that code base.

That slice of cold, of course, given that your test has all the required assertions and then the most controversial, or the least, and would say, and the stood property of a good unit test is resilience to refactoring and that is How likely your test to fail to turn red. If you refactor the code that this test covers. So another way to describe what this metric is, How likely your tests to raise false. Arms or false positives.

And the false alarm is a situation where your test fails, but the underlying code works fine. This usually happens when you refactor your code because when you refractor, you change some implementation details of your code base. And if your tests couple to those implementation details, well, they may basically not like that. You changed those details, and they will notify you about those details changed. So, that's not how you want your tests to work.

You want them? To whale only when you change the observable behavior of your code, but it's not the implementation details of it. And so, this second metric is, what is responsible for test brittleness or test for Geology, the better this metric. The last French, all your tests are and there are several things that contribute to this metric. In my experience this metric resilience to reflect on the most important metric and it's at a single most important

factor. That differentiates between good and bad pasts because nowadays a lot of developers understand the importance of the first mantric protection against regressions, the third metric of tests being fast and the fourth metric, the tasks being a lamentable, but not a lot of developers understand the importance of the second metric which is resilience to refactoring. I can only speculate why that is but my idea is that you don't need refactoring right away.

So if we Outline. The importance of the first metric protection against the regressions, and the second metric with time, you will have these situation word protection

against blocks is important. Pretty much from the very beginning of your project because you want your tests to catch any bugs from the get-go, but with resilience to refactoring, it's not as important in the beginning because think portent skin refactoring is not as immediate either because in the beginning of the project, your code doesn't Eat a lot of refactorings. It's only a when you introduce more and more features when your project involves you need to do refactoring.

You need to start doing constant or factoring off your existing code base to make sure that it reflects your new domain knowledge, your viewer apartments and so on. And so because you don't need refraction, right? The way the importance of the second metric of your past resilience to that refactoring is also not immediate. That's why I think a lot of Developers Don't be too much attention to that metric, but it does come important as important

as the first metric. When their project evolves, when it matures often times what you can see, and a lot of projects is that you write tests and you are quite happy with those tests. But then, when you write quite a bit of cold, you have to reflect or something, you have to introduce feature, that doesn't quite fit, your existing, code base, your existing architecture. And you need to do some modifications to that architecture to that existing code base.

And that oftentimes leads to modifications, not only to your production code, please, but to your tests as well, because they were structured in a way, it didn't, they coupled to those implementation details and when you're a factor, those implementation details you basically have to refactor your

tests as well. DC is an example of a false positive of a false alarm when your tests raise those alarms, and they are fools because it's Necessarily true that after effect from you introduced in ebooks, your code base, you might have refactored, everything perfectly, and the cool to still working, but your tests will still fail, even though your coat, my still work. And so, this is the second

metric. You can also think of the first two Matrix protection against regressions, or protection against bugs and resilience to refactoring, you can think of them as of signal-to-noise ratio. It's when you have well, Basically a signal and / notes, a signal part is what your first metric gives you. It gives you the possibility of the probability of catching any regression Senor. Could weights. The noise part is what resilience tour, fact on allows

you to minimize. So once again signal is how good your tests are at Kitchen in your box and noise is about how many false alarms of those tests race while they can't shoot those books. Both of these metrics are important. Well signal is important because it's obviously important for your tests to be able to catch blocks because otherwise in, they were just useless.

But noise is also important. Because even if your tests are able to teach any bugs in your software, even then, if they raised a lot of noise, a lot of false alarms, then you will not be able to differentiate proper failures from false failures. So you will lose all that signal in the sea of noise. And then your tests generate. And so it's important to maximize both of these metrics protection against bugs and resilience of your tests to refactoring.

Thanks for the in-depth explanation about these four pillars of four Matrix of unit tests. I get a sense that some people might still be confused about the second metric, which is resilience to refactoring. I would also assume that when you do refactoring, of course your test will fail, right, of course, you will need to also change your task code, but few things that I pick up from your

explanation. Just now is that there's a situation where the test could fail, but the actual production code, is actually looking fine, which is what you called, post positive. And the other one is actually ran. The tests realize Is too much on implementation details. Could you maybe briefly explain about this for people who buy be confused? What do you mean by a task? That should not rely too much on

implementation detail. So you want your tests to check the behavior of your cold from the end users perspective. For example, if you have a class that well, let's say it, renders some message, and it returns an HTML representational that message as a result. There are two ways to change. That class first is to check the resultant HTML message as a whole. And second is to check how exactly this class generates at that HTML message.

This is an example of a good test and Baptist in the sense of a brutal test. The second version of that past, which checks, the internal implementation details is brittle, because it binds, it couples to those implementation details. It basically insists on a specific implementation. Ocean of that class if you change and that implementation that past will fail. There are countless number of equally applicable

implementations. Usually when you implement classes, that's why it's a bad idea to couple to specific implementation of your class when you unit tests that class, basically, because it will raise too many false alarms, and that will inhibit your ability to find blocks in your software because you will not be able to rely on that desk with that raises too many false positives. I'm If it answers your question, but maybe you can elaborate on that with further questions.

Yeah. Sure. I get a sense that we'll cover this more when we talk about mocking later on, but you mentioned a couple times already that some of the bad practice of doing a unit test, when you have a test with the assertion and then in terms of lines of code a long test, could you probably describe a little bit more about the anatomy of a good unit test? Yeah, that's the second chapter were I tried to provide a refresher on how unit test is

structured. So the usual structure as a ranch act and cert structure where you have three parts of unit tests. The first one, arranges some input, we use or test pictures the second part in bulks of the coat under test and third part. Asserted that the result of that application is correct. And there are some basic guidelines here of how you shoot and shoot. And Implement those unit test parts.

And the first idea is that you shouldn't use if statements anywhere in your tests because your tests should be as dumb as possible. They basically need to verify. Only one use case with those if statements you diverge from that guideline, you start to check multiple use cases with just one test. This is bad because your unit test is called as well, if you introduce. Exit E2, that code, it becomes more prone to bugs.

That's something that you want to avoid because you don't have other set of tests that test these tests you basically want your task to be as simple as possible so that they don't have any box in themselves. So, that's the first guideline, the second guideline. Well, I would say that this is the most important guideline to the second. Most important guideline is that your test should be autonomous. They shouldn't depend. And on each other.

So what you often see is that when you have a class that contains several tests, you may introduce some duplications in those tests. The first inclination of lot of developers is to extract those duplications into something like, class feels or class properties, which, you can reuse in those test. This is understandable, and it is good for production code, but it is not a good practice for your test code. Because another important Line

here. Important property of a good test is that they do not depend on each other. This is important because you don't want your tests to fail. When you modify some other tests. You want them to be independent from each other and you want them to test their own aspects of the production code base independently from each other. So I would say, then these two are the most important guidelines when it comes to just some foundational guidelines or basic guidelines.

And I guess the other one is about assertions itself, right invention like test without assertion, or how about multiple assertions? Yeah. This is a debatable topic. There is an opinion. That there is a view of that unit tests should contain only one assertion. I disagree, with that opinion. And I think that a test can contain multiple assertions. That's perfectly fine. This view on a test. Having only one assertion comes from another presupposition.

I would say. So this is The View that unit test should only cover a small piece of code. This is also incorrect in my opinion because it's not the code, that is important for test coverage. It is a specific unit of behavior that your unit tests should cover the size of the code that it takes you to implement that behavior is relevant. Here. It may take you one class, one method or multiple classes. Is a sighted shouldn't matter for your unit test. Even if your unit of behavior

takes multiple. Classes, you should test that unit of behavior as one unit. So you should create all these multiple classes than invoked are here under under test. And then you can ascertain, the result of that vacation, using those multiple classes. So you can assert part of the result in one class part of the result in the second class. And so that's why I believe that multiple assertions protest, he is perfectly fine. So I think this is a good segue

to move to the mocking. Sometimes you would do this mocking techniques or maybe some other test doubles that people need to do in order to structure the particular class of the particular method in order to have the proper arrange, the first step of the arranged I can assert. So could you maybe share a so that the bit more about this technique about test doubles about mocking and yeah probably

will suffer from that. This is another controversial, a pretty controversial topic of where exactly, you should apply more. There are two schools of thought he had two major schools of thought. The first one is the London school. It is also sometimes called Mark East school and it advocates for applying marks for any dependencies other than immutable dependencies and other school.

He is at the classical school or classic school and this school is also called Detroit or Chicago School of unit, testing. This cool air is about applying marking only for out of practice. Dependencies such as the database SMTP service. And so, on Marx is one of the most frequent reasons for your tests being brittle. So they affect the second property of a good unit tests. A lot. The second property being resilience to refactoring, that's because marks are good way to cement. The way.

The system under test works with its dependencies usually don't want these communication pattern between the system under test and Dependencies don't want it to be cemented. You don't want it to be set in stone because it, that is an implementation detail. As I said, the London school and her kids for using marks for any mutable dependencies, to provide

an example. Let's say that you could test the user class and this class has another Clause company as a dependency and that company class is mutable. Then these company class would be a dependency for the system under test and this dependency would be mutable. And so with a lot, And on school would advocate for mocking this

dependency in tests. So, basically, for replacing that dependency with detestable, this is a better approach because it has sapped the particular way of communication between the user and the company has nothing to do with the user of that duration that has nothing to do with how the end user perceives the result of that operation.

So what matters is the aunt state of these two classes of the user class and the company class how exactly they communicated with Each other to achieve that end state is not important at all. The company classy. Shouldn't bind your tests to these specific way. These two classes communicate with each other. Otherwise, it would lead to task brittleness. So that's why I think the London school is incorrect about the use of mocks. The classical school is a sad.

It doesn't advocate for the use of marks for all mutable dependencies. But only for those of them that are out of process. For example, the database, the A classical school, I would say that it also is incorrect in its treatment of marks. That's because just like, you shouldn't mock in-process mutable dependencies. You also shouldn't mock all out of process. Mutable dependencies to describe this point. We need to take a step back and talk about how applications

evolve together. And what is the main benefit of mocks the main benefit of my ex when it comes to unit testing. Is that, as I said, it allows you to semantics Way, your system under test communicates, with those dependencies, you only want to do that. If that communication is part of observable behavior of your software. It is part of observable behavior.

Only, when the communication with that dependency, is visible to the outside world, for example, to the client who invoked some alteration in your system. So, let's take some example. Now, let's see that. You develop an API and disappear. I works with two out of practice. Dependencies. The first one is application database and the second one is a master bus. So the client application invokes, your API and the result of that implication would be that your application modifies,

some state in the database. And it also amides a message on the message bus. The question here is, which of these two out of practice dependencies. You should Mark the database and the message bus. You need to look at how your application involves together

with those out of practice. Pendants, it's the main guide line here is that you should enable backwards compatibility of when you deploy new versions of your software and that's because you cannot deploy your application smelt Aeneas lie with other applications that depend on your software.

For example, when you develop microservice in your organization, and then there is another micro service event, depends on your application, or that reads the messages that you put on the bus, if those micro servicers are Lloyd separately, for example, your microservices is developed by your team and this second microservices deployed by a separate team in the same organization. You may not be able to deploy

them simultaneously. And so when you introduce you versions, you should maintain backward compatibility so that the clients of your software will be able to catch up benchley and start to use new versions of your interface. But before that, you should still be able to use it, the old version of that interface to help you with maintaining that backward compatibility. This is where Marx come helpful because of this is their primary goal.

They allow you to make sure that the communication pattern between your application and the message bus remains of the same. As I said, you cannot change these communication pattern, because if you change the structure of your messages, that your application, amit's on the bus, then they add applications will not understand those messages. You have to maintain these data contract. Between those two applications. This is where Marx will be

helpful. They will help you to samaan to these A pattern between your application and this message bus, but at the same time, these backward compatibility is not the case for the application database. And that's because no other application has a direct access to that database. You are able to deploy your code base, your project alongside with database. So you're basically deploy the database with your project simultaneously. And so there is no backward.

Compatibility requirement between your application and the database. That's why you shouldn't use mocks. When you check your database because those Communications between your application and the database, they are also implementation details because if they are not visible to the outside world, this prayer of two applications, your application and the database, they essentially act as one system and the dual-sim.

So from the end user perspective because of the end user doesn't know that you have a database behind the scenes. The only thing it knows is that you provide some of the eyes for that and user and then when it involves So those apis the state some of your data changes, but it doesn't see the data itself. It can only reach out to the Theta through your IP eyes.

And so because in this situation, your system behaves as one with the database, you shouldn't mock the communications between your application and this database, you only need to check the end result of those Communications. So the state of your database after that parisians completed, but again, it's not the keys for the message bus because communication, Sweet. The message parts are visible to

the outside world. They are visible to the client or other applications who work with your application. And so here, if we go back to the mocking topic from the classical perspective, all out of process dependencies can be subdivided into two categories. The first one is managed dependencies, and the second one is unmanaged dependencies. When it's dependencies, is basically an application

database. It is the communications with which are not visible to the The outside world and you shouldn't mock those dependencies. You should only mark on mens dependencies. This is something like a message bars and third-party your system, something like a payment, the pi at third part 2 micro service or something like an SMTP service. So this is something that you do need to mark because Communications with those

dependencies are visible. Externally, they are observable externally and so you shoot maintain backward, compatibility with those dependencies. Thank you for The in-depth explanation, including some examples. You mentioned a lot of things here, especially including like some styles of unit. Testing. Be it like asserting just the end result of the output and also like how do you also check whether the state after certain operation runs? Whether it's correct.

Also communication right bike, for example, third-party message bars and database. So thanks for explaining all this in one go. So before we move on to the last section of the composition, which is about three, tactics them before that. Maybe if you can give us some and deeper. Turns or bad practices of unit tests for people to know be aware of, that's the first thing. And also for them not to repeat the same mistakes. Yeah. There are some common, widespread anti-patterns amount

comes to you testing. They all flow from these four pillars of a good unit tests. They all can be derived from these four properties, but it's still interesting to reiterate those patterns on their own. Well, just review them because they are quite common as that. I would say theme more. Common anti-pattern is unit, testing private methods and unit. Testing private state of your

production code, please. This is bad because private methods, they are private for a reason and that reason is usually because your production could be is doesn't need them. So, let's say, if we take a class and it has some public mountains and also has some private methods, those methods are private because declines of that class doesn't need to know about those methods. So those methods are not part of the The public API of the observable behavior of that

Clause by exposing them. Just for the sake of unit, testing you by definition, exposed implementation details. Because of those private methods, they are by definition implementation details and so by finding your tests to those implementation details, you'll make your tests fragile, make them more brittle. This is also a consequence of violating the second pillar of a good unit test. So its resilience to refactoring.

Swen, you blind your tests and coupled them to implementation details instead of observable behavior of the production code. The second most common anti-pattern is similar. It's about exposing not methods, but state, it is also important to avoid that. Basically for the same reasons, because in your production code, if some class has private State, this state is private also for reason is because the clients of

that class. I don't need that state to operate and so it Also becomes blind ation detail by definition because there are no client in the production code that require that state. So when you bind your tests to that private State, you are also violating the second pillar, you are making your tests less resilient to refactoring because when you change that implementation detail, it's a you might defy it way, you store that private State. You will have to update your tests as well.

And you don't want to do that. You only want to update your tests when they point out. Out modification in The Observer here, not implementation details. So I would say these are the two most prominent and their patterns when it comes to unit testing. So thank you so much Vladimir for all this knowledge about unit testing best practices what to avoid what to do. So I really enjoyed that and I learned a lot myself.

But before I let you go, normally, I have this one question for all my guests, which is for you to share your tree. Technical leadership is them so that people can learn from you. I'm sure I can come up with 3x and what I have one, so, I would say, put your thoughts in writing. This comes from my personal

experience. Because as I said, when I started blogging at my block, I forced myself to learn much more than I learned before, even though, before that I worked as a programmer for almost 10 years and still since I started blogging I learned much more than 10 years prior to that. This is the main reason why you should put your thoughts in writing. Because they force you to structure, your thoughts and even if no one reads that stuff.

And by the way, I do recommend that you make those thoughts public. Because even if nobody will read your thoughts, it still beneficial for yourself because you will be forced to structure your thoughts and to find connections that you didn't find before. Even if you somehow subconsciously, understood some topics, some prominent topics. You probably didn't understand them as deeply We as after you start to write about them. So that's my main advice for everyone, just to clarify this.

When you say put thoughts in writing is not writing code, right? Yeah, it's more about blogging. So, thanks again for the me for people who want to connect with you, or follow the discussion about unit testing further when they can find you. Yeah, their main touch point in his, my blog and Enterprise craftsmanship.com. So I recommend my book, you need testing free.

Police practices and patterns. And by the way, these books together, the highest reading among all books, published by Manning according to my tech editor. So yeah, definitely recommend you. Check out the book and also check out my courses on puerile sites. They are about the main German design and Mica my model secure Essence on. So, for the technology Now podcast listener, I have a special discount for all, meaning books. And also, for this episode itself, will be giving you one

free ebook for giveaway. So make sure to To get that book so that you can learn about unit testing and plus, let me set. It's a highly rated book. So, thanks again Vladimir. I really enjoyed this conversation and see you next time. Thank you so much. Thanks for having me. Thank you for listening to this episode and for staying right till the end. If you highly enjoyed, please share it with your friends and colleagues who you think would also benefit from listening to

this episode. And if you're new to the podcast, make sure to subscribe and leave me your valuable review and feedback. It really, really helps me a lot in order to grow these podcasts better. You can also find the full show notes of this conversation on the episode page at tackling journal. The death website. Putting the full transcript interesting quotes and links to the resources and mentions from

the conversation. And lastly make sure to subscribe to the show's mailing list on technology. No, the deaf to get notified for any future episodes. Stay tuned for the next technique Journal episode. And until then. Goodbye.

Transcript source: Provided by creator in RSS feed: download file
For the best experience, listen in Metacast app for iOS or Android