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.