# What’s Up With Tests
This is a transcript of [What's Up With
That](https://www.youtube.com/playlist?list=PL9ioqAuyl6ULIdZQys3fwRxi3G3ns39Hq)
Episode 4, a 2022 video discussion between [Sharon ([email protected])
and Stephen
([email protected])](https://www.youtube.com/watch?v=KePsimOPSro).
The transcript was automatically generated by speech-to-text software. It may
contain minor errors.
---
Testing is important! What kinds of tests do we have in Chromium? What are they
all about? Join in as Stephen, who led Chrome's involvement in web platform
tests, tells us all about them.
Notes:
- https://docs.google.com/document/d/1SRoNMdPn78vwZVX7YzcdpF4cJdHTIV6JLGiVC2dJUaI/edit
---
00:00 SHARON: Hello, everyone, and welcome to "What's Up With That," the series
that demystifies all things Chrome. I'm your host, Sharon. And today we're
talking testing. Within Chrome, there are so many types of tests. What are they
all? What's the difference? What are the Chromium-specific quirks? Today's
guest is Stephen. He previously led Chrome's involvement in web platform tests.
Since then, he's worked on rendering, payments, and interoperability. As a fun
aside, he's one of the first people I met who worked on Chrome and is maybe
part of why I'm here today. So welcome, Stephen.
00:33 STEPHEN: Well, thank you very much for having me, Sharon, I'm excited to
be here.
00:33 SHARON: Yeah, I'm excited to have you here. So today, we're in for maybe
a longer episode. Testing is a huge topic, especially for something like
Chrome. So grab a snack, grab a drink, and let's start. We'll start with what
are all of the things that we have testing for in Chrome. What's the purpose of
all these tests we have?
00:51 STEPHEN: Yeah. It's a great question. It's also an interesting one
because I wanted to put one caveat on this whole episode, which is that there
is no right answer in testing. Testing, even in the literature, never mind in
Chromium itself, is not a solved problem. And so you'll hear a lot of different
opinions. People will have different thoughts. And I'm sure that no matter how
hard we try, by the end of this episode, our inbox will be filled with angry
emails from people being like, no, you are wrong. So all of the stuff we're
saying here today is my opinion, albeit I'll try and be as useful as possible.
But yeah, so why do we test was the question, right? So there's a lot of
different reasons that we write tests. Obviously, correctness is the big one.
You're writing some code, you're creating a feature, you want it to be correct.
Other reasons we write them, I mean, tests can be useful as a form of
documentation in itself. If you're ever looking at a class and you're like,
what does - why is this doing this, why is the code doing this, the test can
help inform that. They're also useful - I think a topic of this podcast is sort
of security. Tests can be very useful for security. Often when we have a
security bug, we go back and we write what are called regression tests, so at
least we try and never do that security failure again. And then there are other
reasons. We have tests for performance. We have tests for - our launch process
uses tests. There's lots and lots of reasons we have tests.
02:15 SHARON: Now that you've covered all of the different reasons why we test,
how do we do each of these types of tests in Chromium? What are the test types
we have?
02:27 STEPHEN: Yeah. So main test types we have in Chromium, unit tests,
browser tests, what we call web tests, and then there's a bunch of more
specialized ones, performance tests, testing on Android, and of course manual
testing.
02:43 SHARON: We will get into each of these types now, I guess. The first type
of test you mentioned is unit tests. Why don't you tell us a quick rundown of
what unit tests are. I'm sure most people have encountered them or heard of
them before. But just a quick refresher for those who might not.
02:55 STEPHEN: Yeah, absolutely. So as the name implies, a unit test is all
about testing a unit of code. And what that is not very well defined. But you
can usually think of it as just a class, a file, a small isolated component
that doesn't have to talk to all the other bits of the code to work. Really,
the goal is on writing something that's testing just the code under test - so
that new method you've added or whatever. And it should be quick and easy to
run.
03:22 SHARON: So on the screen now we have an example of a pretty typical unit
test we see in Chrome. So there's three parts here. Let's go through each of
them. So the first type - the first part of this is `TEST_P`. What is that
telling us?
03:38 STEPHEN: Yeah. So that is - in Chromium we use a unit testing framework
called Google test. It's very commonly used for C++. You'll see it all over the
place. You can go look up documentation. The test macros, that's what this is,
are essentially the hook into Google test to say, hey, the thing that's coming
here is a test. There's three types. There is just test, which it just says
here is a function. It is a test function. `TEST_F` says that you basically
have a wrapper class. It's often called a test fixture, which can do some
common setup across multiple different tests, common teardown, and that sort of
thing. And finally, `TEST_P` is what we call a parameterized test. And what
this means is that the test can take some input parameters, and it will run the
same test with each of those values. Very useful for things like when you want
to test a new flag. What happens if the flag is on or off?
04:34 SHARON: That's cool. And a lot of the things we're mentioning for unit
test also apply to browser test, which we'll cover next. But the
parameterization is an example of something that carries over to both. So
that's the first part. That's the `TEST_P`, the macro. What's the second part,
PendingBeaconHostTest? What is that?
04:54 STEPHEN: Yeah. So that is the fixture class, the test container class I
was talking about. So in this case, we're assuming that in order to write a
beacon test, whatever that is, they have some set up, some teardown they need
to do. They might want to encapsulate some common functionality. So all you
have to do to write one of these classes is, you declare a C++ class and you
subclass from the Google test class name.
05:23 SHARON: So this is a `TEST_P`, but you mentioned that this is a fixture.
So are fixture tests a subset of parameterized tests?
05:35 STEPHEN: Parameterized tests are a subset of fixture test, is that the
right way around to put it? All parameterized tests are fixtures tests. Yes.
05:41 SHARON: OK.
05:41 STEPHEN: You cannot have a parameterized test that does not have a
fixture class. And the reason for that is how Google test actually works under
the covers is it passes those parameters to your test class. You will have to
additionally extend from the `testing::WithParamInterface`. And that says, hey,
I'm going to take parameters.
06:04 SHARON: OK. But not all fixture tests are parameterized tests.
06:04 STEPHEN: Correct.
06:04 SHARON: OK. And the third part of this, SendOneOfBeacons. What is that?
06:10 STEPHEN: That is your test name. Whatever you want to call your test,
whatever you're testing, put it here. Again, naming tests is as hard as naming
anything. A lot of yak shaving, finding out what exactly you should call the
test. I particularly enjoy when you see test names that themselves have
underscores in them. It's great.
06:30 SHARON: Uh-huh. What do you mean by yak shaving?
06:35 STEPHEN: Oh, also known as painting a bike shed? Bike shed, is that the
right word? Anyway, generally speaking -
06:40 SHARON: Yeah, I've heard -
06:40 STEPHEN: arguing about pointless things because at the end of the day,
most of the time it doesn't matter what you call it.
06:46 SHARON: OK, yeah. So I've written this test. I've decided it's going to
be parameterized. I've come up with a test fixture for it. I have finally named
my test. How do I run my tests now?
06:57 STEPHEN: Yeah. So all of the tests in Chromium are built into different
test binaries. And these are usually named after the top level directory that
they're under. So we have `components_unittests`, `content_unittests`. I think
the Chrome one is just called `unit_tests` because it's special. We should
really rename that. But I'm going to assume a bunch of legacy things depend on
it. Once you have built whichever the appropriate binary is, you can just run
that from your `out` directory, so `out/release/components_unittests`, for
example. And then that, if you don't pass any flags, will run every single
components unit test. You probably don't want to do that. They're not that
slow, but they're not that fast. So there is a flag `--gtest_filter`, which
allows you to filter. And then it takes a test name after that. The format of
test names is always test class dot test name. So for example, here
PendingBeaconHostTest dot SendOneOfBeacons.
08:04 SHARON: Mm-hmm. And just a fun aside for that one, if you do have
parameterized tests, it'll have an extra slash and a number at the end. So
normally, whenever I use it, I just put a star before and after. And that
generally does - covers the cases.
08:17 STEPHEN: Yeah, absolutely.
08:23 SHARON: Cool. So with the actual test names, you will often see them
prefixed with either `MAYBE_` or `DISABLED_`, or before the test, there will be
an ifdef with usually a platform and then depending on the cases, it'll prefix
the test name with something. So I think it's pretty clear what these are
doing. Maybe is a bit less clear. Disabled pretty clear what that is. But can
you tell us a bit about these prefixes?
08:51 STEPHEN: Yeah, absolutely. So this is our way of trying to deal with that
dreaded thing in testing, flake. So when a test is flaky, when it doesn't
produce a consistent result, sometimes it fails. We have in Chromium a whole
continuous integration waterfall. That is a bunch of bots on different
platforms that are constantly building and running Chrome tests to make sure
that nothing breaks, that bad changes don't come in. And flaky tests make that
very hard. When something fails, was that a real failure? And so when a test is
particularly flaky and is causing sheriffs, the build sheriffs trouble, they
will come in and they will disable that test. Basically say, hey, sorry, but
this test is causing too much pain. Now, as you said, the `DISABLED_` prefix,
that's pretty obvious. If you put that in front of a test, Google test knows
about it and it says, nope, will not run this test. It will be compiled, but it
will not be run. `MAYBE_` doesn't actually mean anything. It has no meaning to
Google test. But that's where you'll see, as you said, you see these ifdefs.
And that's so that we can disable it on just one platform. So maybe your test
is flaky only on Mac OS, and you'll see basically, oh, if Mac OS, change the
name from maybe to disabled. Otherwise, define maybe as the normal test name.
10:14 SHARON: Makes sense. We'll cover flakiness a bit later. But yeah, that's
a huge problem. And we'll talk about that for sure. So these prefixes, the
parameterization and stuff, this applies to both unit and browser tests.
10:27 STEPHEN: Yeah.
10:27 SHARON: Right? OK. So what are browser tests? Chrome's a browser. Browser
test, seems like there's a relation.
10:34 STEPHEN: Yeah. They test the browser. Isn't it obvious? Yeah. Browser
tests are our version - our sort of version of an integration or a functional
test depending on how you look at things. What that really means is they're
testing larger chunks of the browser at once. They are integrating multiple
components. And this is somewhere that I think Chrome's a bit weird because in
many large projects, you can have an integration test that doesn't bring your
entire product up and in order to run. Unfortunately, or fortunately, I guess
it depends on your viewpoint, Chrome is so interconnected, it's so
interdependent, that more or less we have to bring up a huge chunk of the
browser in order to connect any components together. And so that's what browser
tests are. When you run one of these, there's a massive amount of machinery in
the background that goes ahead, and basically brings up the browser, and
actually runs it for some definition of what a browser is. And then you can
write a test that pokes at things within that running browser.
11:42 SHARON: Yeah. I think I've heard before multiple times is that browser
tests launch the whole browser. And that's -
11:47 STEPHEN: More or less true. It's - yeah.
11:47 SHARON: Yes. OK. Does that also mean that because you're running all this
stuff that all browser tests have fixtures? Is that the case?
11:59 STEPHEN: Yes, that is the case. Absolutely. So there is only - I think
it's - oh my goodness, probably on the screen here somewhere. But it's
`IN_PROC_BROWSER_TEST_F` and `IN_PROC_BROWSER_TEST_P`. There is no version that
doesn't have a fixture.
12:15 SHARON: And what does the in proc part of that macro mean?
12:15 STEPHEN: So that's, as far as I know - and I might get corrected on this.
I'll be interested to learn. But it refers to the fact that we've run these in
the same process. Normally, the whole Chromium is a multi-process architecture.
For the case of testing, we put that aside and just run everything in the same
process so that it doesn't leak, basically.
12:38 SHARON: Yeah. There's flags when you run them, like `--single-process`.
And then there's `--single-process-test`. And they do slightly different
things. But if you do run into that, probably you will be working with people
who can answer and explain the differences between those more. So something
that I've seen quite a bit in browser and unit tests, and only in these, are
run loops. Can you just briefly touch on what those are and what we use them
for in tests?
13:05 STEPHEN: Oh, yeah. That's a fun one. I think actually previous on an
episode of this very program, you and Dana talked a little bit around the fact
that Chrome is not a completely synchronous program, that we do we do task
splitting. We have a task scheduler. And so run loops are part of that,
basically. They're part of our stack for handling asynchronous tasks. And so
this comes up in testing because sometimes you might be testing something
that's not synchronous. It takes a callback, for example, rather than returning
a value. And so if you just wrote your test as normal, you call the function,
and you don't - you pass a callback, but then your test function ends. Your
test function ends before that callback ever runs. Run loop gives you the
ability to say, hey, put this callback into some controlled run loop. And then
after that, you can basically say, hey, wait on this run loop. I think it's
often called quit when idle, which basically says keep running until you have
no more tasks to run, including our callback, and then finish. They're
powerful. They're very useful, obviously, with asynchronous code. They're also
a source of a lot of flake and pain. So handle with care.
14:24 SHARON: Yeah. Something a tip is maybe using the `--gtest_repeat` flag.
So that one lets you run your test however number of times you've had to do it.
14:30 STEPHEN: Yeah.
14:36 SHARON: And that can help with testing for flakiness or if you're trying
to debug something flaky. In tests, we have a variety of macros that we use. In
the unit test and the browser tests, you see a lot of macros, like `EXPECT_EQ`,
`EXPECT_GT`. These seem like they're part of maybe Google test. Is that true?
14:54 STEPHEN: Yeah. They come from Google test itself. So they're not
technically Chromium-specific. But they basically come in two flavors. There's
the `EXPECT_SOMETHING` macros. And there's the `ASSERT_SOMETHING` macros. And
the biggest thing to know about them is that expect doesn't actually cause - it
causes a test to fail, but it doesn't stop the test from executing. The test
will continue to execute the rest of the code. Assert actually throws an
exception and stops the test right there. And so this can be useful, for
example, if you want to line up a bunch of expects. And your code still makes
sense. You're like, OK, I expect to return object, and it's got these fields.
And I'm just going to expect each one of the fields. That's probably fine to
do. And it may be nice to have output that's like, no, actually, both of these
fields are wrong. Assert is used when you're like, OK, if this fails, the rest
of the test makes no sense. Very common thing you'll see. Call an API, get back
some sort of pointer, hopefully a smart pointer, hey. And you're going to be
like, assert that this pointer is non-null because if this pointer is null,
everything else is just going to be useless.
15:57 SHARON: I think we see a lot more expects than asserts in general
anecdotally from looking at the test. Do you think, in your opinion, that
people should be using asserts more generously rather than expects, or do we
maybe want to see what happens - what does go wrong if things continue beyond a
certain point?
16:15 STEPHEN: Yeah. I mean, general guidance would be just keep using expect.
That's fine. It's also not a big deal if your test actually just crashes. It's
a test. It can crash. It's OK. So use expects. Use an assert if, like I said,
that the test doesn't make any sense. So most often if you're like, hey, is
this pointer null or not and I'm going to go do something with this pointer,
assert it there. That's probably the main time you'd use it.
16:45 SHARON: A lot of the browser test classes, like the fixture classes
themselves, are subclass from other base classes.
16:53 STEPHEN: Mm-hmm.
16:53 SHARON: Can you tell us about that?
16:53 STEPHEN: Yeah. So basically, we have one base class for browser tests. I
think its `BrowserTestBase`, I think it's literally called, which sits at the
bottom and does a lot of the very low level setup of bringing up a browser. But
as folks know, there's more than one browser in the Chromium project. There is
Chrome, the Chrome browser that is the more full-fledged version. But there's
also content shell, which people might have seen. It's built out of content.
It's very simple browser. And then there are other things. We have a headless
mode. There is a headless Chrome you can build which doesn't show any UI. You
can run it entirely from the command line.
17:32 SHARON: What's the difference between headless and content shell?
17:39 STEPHEN: So content shell does have a UI. If you run content shell, you
will actually see a little UI pop up. What content shell doesn't have is all of
those features from Chrome that make Chrome Chrome, if you will. So I mean,
everything from bookmarks, to integration with having an account profile, that
sort of stuff is not there. I don't think content shell even supports tabs. I
think it's just one page you get. It's almost entirely used for testing. But
then, headless, sorry, as I was saying, it's just literally there is no UI
rendered. It's just headless.
18:13 SHARON: That sounds like it would make -
18:13 STEPHEN: And so, yeah. And so - sorry.
18:13 SHARON: testing faster and easier. Go on.
18:18 STEPHEN: Yeah. That's a large part of the point, as well as when you want
to deploy a browser in an environment where you don't see the UI. So for
example, if you're running on a server or something like that. But yeah. So for
each of these, we then subclass that `BrowserTestBase` in order to provide
specific types. So there's content browser test. There's headless browser test.
And then of course, Chrome has to be special, and they called their version in
process browser test because it wasn't confusing enough. But again, it's sort
of straightforward. If you're in Chrome, `/chrome`, use
`in_process_browser_test`. If you're in `/content`, use `content_browsertest`.
It's pretty straightforward most of the time.
18:58 SHARON: That makes sense. Common functions you see overridden from those
base classes are these set up functions. So they're set, set up on main thread,
there seems to be a lot of different set up options. Is there anything we
should know about any of those?
19:13 STEPHEN: I don't think that - I mean, most of it's fairly
straightforward. I believe you should mostly be using setup on main thread. I
can't say that for sure. But generally speaking, setup on main thread, teardown
on main thread - or is it shutdown main thread? I can't remember - whichever
the one is for afterwards, are what you should be usually using in a browser
thread. You can also usually do most of your work in a constructor. That's
something that people often don't know about testing. I think it's something
that's changed over time. Even with unit tests, people use the setup function a
lot. You can just do it in the constructor a lot of the time. Most of
background initialization has already happened.
19:45 SHARON: I've definitely wondered that, especially when you have things in
the constructor as well as in a setup method. It's one of those things where
you just kind of think, I'm not going to touch this because eh, but -
19:57 STEPHEN: Yeah. There are some rough edges, I believe. Set up on main
thread, some things have been initialized that aren't around when your class is
being constructed. So it is fair. I'm not sure I have any great advice unless -
other than you may need to dig in if it happens.
20:19 SHARON: One last thing there. Which one gets run first, the setup
functions or the constructor?
20:19 STEPHEN: The constructor always happens first. You have to construct the
object before you can use it.
20:25 SHARON: Makes sense. This doesn't specifically relate to a browser test
or unit test, but it does seem like it's worth mentioning, which is the content
public test API. So if you want to learn more about content and content public,
check out episode three with John. But today we're talking about testing. So
we're talking about content public test. What is in that directory? And how
does that - how can people use what's in there?
20:48 STEPHEN: Yeah. It's basically just a bunch of useful helper functions and
classes for when you are doing mostly browser tests. So for example, there are
methods in there that will automatically handle navigating the browser to a URL
and actually waiting till it's finished loading. There are other methods for
essentially accessing the tab strip of a browser. So if you have multiple tabs
and you're testing some cross tab thing, methods in there to do that. I think
that's probably where the content browser test - like base class lives there as
well. So take a look at it. If you're doing something that you're like, someone
should write - it's the basic - it's the equivalent of base in many ways for
testing. It's like, if you're like, someone should have written a library
function for this, possibly someone has already. And you should take a look.
And if they haven't, you should write one.
21:43 SHARON: Yeah. I've definitely heard people, code reviewers, say when you
want to add something that seems a bit test only to content public, put that in
content public test because that doesn't get compiled into the actual release
binaries. So if things are a bit less than ideal there, it's a bit more
forgiving for a place for that.
22:02 STEPHEN: Yeah, absolutely. I mean, one of the big things about all of our
test code is that you can actually make it so that it's in many cases not
compiled into the binary. And that is both useful for binary size as well as
you said in case it's concerning. One thing you can do actually in test, by the
way, for code that you cannot avoid putting into the binary - so let's say
you've got a class, and for the reasons of testing it because you've not
written your class properly to do a dependency injection, you need to access a
member. You need to set a member. But you only want that to happen from test
code. No real code should ever do this. You can actually name methods blah,
blah, blah for test or for testing. And this doesn't have any - there's no code
impact to this. But we have pre-submits that actually go ahead and check, hey,
are you calling this from code that's not marked as test code? And it will then
refuse to - it will fail to pre-submit upload if that happens. So it could be
useful.
23:03 SHARON: And another thing that relates to that would be the friend test
or friend something macro that you see in classes. Is that a gtest thing also?
23:15 STEPHEN: It's not a gtest thing. It's just a C++ thing. So C++ has the
concept of friending another class. It's very cute. It basically just says,
this other class and I, we can access each other's internal states. Don't
worry, we're friends. Generally speaking, that's a bad idea. We write classes
for a reason to have encapsulation. The entire goal of a class is to
encapsulate behavior and to hide the implementation details that you don't want
to be exposed. But obviously, again, when you're writing tests, sometimes it is
the correct thing to do to poke a hole in the test and get at something. Very
much in the schools of thought here, some people would be like, you should be
doing dependency injection. Some people are like, no, just friend your class.
It's OK. If folks want to look up more, go look up the difference between open
box and closed box testing.
24:00 SHARON: For those of you who are like, oh, this sounds really cool, I
will learn more.
24:00 STEPHEN: Yeah, for my test nerds out there.
24:06 SHARON: [LAUGHS] Yeah, Stephen's got a club. Feel free to join.
24:06 STEPHEN: Yeah. [LAUGHTER]
24:11 SHARON: You get a card. Moving on to our next type of test, which is your
wheelhouse, which is web tests. This is something I don't know much about. So
tell us all about it.
24:22 STEPHEN: [LAUGHS] Yeah. This is my - this is where hopefully I'll shine.
It's the area I should know most about. But web tests are - they're an
interesting one. So I would describe them is our version of an end-to-end test
in that a web test really is just an HTML file, a JavaScript file that is when
you run it, you literally bring up - you'll remember I said that browser tests
are most of a whole browser. Web tests bring up a whole browser. It's just the
same browser as content shell or Chrome. And it runs that whole browser. And
the test does something, either in HTML or JavaScript, that then is asserted
and checked. And the reason I say that I would call them this, I have heard
people argue that they're technically unit tests, where the unit is the
JavaScript file and the entire browser is just, like, an abstraction that you
don't care about. I guess it's how you view them really. I view the browser as
something that is big and flaky, and therefore these are end-to-end tests. Some
people disagree.
25:22 SHARON: In our last episode, John touched on these tests and how that
they're - the scope and that each test covers is very small. But how you run
them is not. And I guess you can pick a side that you feel that you like more
and go with that. So what are examples of things we test with these kind of
tests?
25:49 STEPHEN: Yeah. So the two big categories of things that we test with web
tests are basically web APIs, so JavaScript APIs, provided by the browser to do
something. There are so many of those, everything from the fetch API for
fetching stuff to the web serial API for talking to devices over serial ports.
The web is huge. But anything you can talk to via JavaScript API, we call those
JavaScript tests. It's nice and straightforward. The other thing that web tests
usually encompass are what are called rendering tests or sometimes referred to
as ref tests for reference tests. And these are checking the actual, as the
first name implies, the rendering of some HTML, some CSS by the browser. The
reason they're called reference tests is that usually the way you do this to
check whether a rendering is correct is you set up your test, and then you
compare it to some image or some other reference rendering that you're like,
OK, this should look like that. If it does look like that, great. If it
doesn't, I failed.
26:54 SHARON: Ah-ha. And are these the same as - so there's a few other test
names that are all kind of similar. And as someone who doesn't work in them,
they all kind of blur together. So I've also heard web platform tests. I've
heard layout tests. I've heard Blink tests, all of which do - all of which are
JavaScript HTML-like and have some level of images in them. So are these all
the same thing? And if not, what's different?
27:19 STEPHEN: Yeah. So yes and no, I guess, is my answer. So a long time ago,
there were layout tests basically. And that was something we inherited from the
WebKit project when we forked there, when we forked Chromium from WebKit all
those years ago. And they're exactly what I've described. They were both
JavaScript-based tests and they were also HTML-based tests for just doing
reference renderings. However, web platform test came up as an external project
actually. Web platform test is not a Chromium project. It is external upstream.
You can find them on GitHub. And their goal was to create a set of - a test
suite shared between all browsers so that all browsers could test - run the
same tests and we could actually tell, hey, is the web interoperable? Does it
work the same way no matter what browser you're on? The answer is, no. But
we're trying. And so inside of Chromium we said, that's great. We love this
idea. And so what we did was we actually import web platform test into our
layout tests. So web platform test now becomes a subdirectory of layout tests.
OK?
28:30 SHARON: OK. [LAUGHS]
28:30 STEPHEN: To make things more confusing, we don't just import them, but we
also export them. We run a continuous two-way sync. And this means that
Chromium developers don't have to worry about that upstream web platform test
project most of the time. They just land their code in Chromium, and a magic
process happens, and it goes up into the GitHub project. So that's where we
were for many years - layout tests, which are a whole bunch of legacy tests,
and then also web platform tests. But fairly recently - and I say that knowing
that COVID means that might be anything within the last three years because who
knows where time went - we decided to rename layout test. And partly, the name
we chose was web tests. So now you have web tests, of which web platform tests
are a subset, or a - yeah, subset of web test. Easy.
29:20 SHARON: Cool.
29:20 STEPHEN: [LAUGHS]
29:20 SHARON: Cool. And what about Blink tests? Are those separate, or are
those these altogether?
29:27 STEPHEN: I mean, if they're talking about the JavaScript and HTML, that's
going to just be another name for the web tests. I find that term confusing
because there is also the Blink tests target, which builds the infrastructure
that is used to run web tests. So that's probably what you're referring, like
`blink_test`. It is the target that you build to run these tests.
29:50 SHARON: I see. So `blink_test` is a target. These other ones, web test
and web platform tests, are actual test suites.
29:57 STEPHEN: Correct. Yes. That's exactly right.
30:02 SHARON: OK. All right.
30:02 STEPHEN: Simple.
30:02 SHARON: Yeah. So easy. So you mentioned that the web platform tests are
cross-browser. But a lot of browsers are based on Chromium. Is it one of the
things where it's open source and stuff but majority of people contributing to
these and maintaining it are Chrome engineers?
30:23 STEPHEN: I must admit, I don't know what that stat is nowadays. Back when
I was working on interoperability, we did measure this. And it was certainly
the case that Chromium is a large project. There were a lot of tests being
contributed by Chromium developers. But we also saw historically - I would like
to recognize Mozilla, most of all, who were a huge contributor to the web
platform test project over the years and are probably the reason that it
succeeded. And we also - web platform test also has a fairly healthy community
of completely outside developers. So people that just want to come along. And
maybe they're not able to or willing to go into a browser, and actually build a
browser, and muck with code. But they could write a test for something. They
can find a broken behavior and be like, hey, there's a test here, Chrome and
Firefox do different things.
31:08 SHARON: What are examples of the interoperability things that you're
testing for in these cross-browser tests?
31:17 STEPHEN: Oh, wow, that's a big question. I mean, really everything and
anything. So on the ref test side, the rendering test, it actually does matter
that a web page renders the same in different browsers. And that is very hard
to achieve. It's hard to make two completely different engines render some HTML
and CSS exactly the same way. But it also matters. We often see bugs where you
have a lovely - you've got a lovely website. It's got this beautiful header at
the top and some content. And then on one browser, there's a two-pixel gap
here, and you can see the background, and it's not a great experience for your
users. So ref tests, for example, are used to try and track those down. And
then, on the JavaScript side, I mean really, web platform APIs are complicated.
They're very powerful. There's a reason they are in the browser and you cannot
do them in JavaScript. And that is because they are so powerful. So for
example, web USB to talk to USB devices, you can't just do that from
JavaScript. But because they're so powerful, because they're so complicated,
it's also fairly easy for two browsers to have slightly different behavior. And
again, it comes down to what is the web developer's experience. When I try and
use the web USB API, for example, am I going to have to write code that's like,
if Chrome, call it this way, if Fire - we don't want that. That is what we do
not want for the web. And so that's the goal.
32:46 SHARON: Yeah. What a team effort, making the whole web work is. All
right. That's cool. So in your time working on these web platform tests, do you
have any fun stories you'd like to share or any fun things that might be
interesting to know?
33:02 STEPHEN: Oh, wow. [LAUGHS] One thing I like to bring up - I'm afraid it's
not that fun, but I like to repeat it a lot of times because it's weird and
people get tripped up by it - is that inside of Chromium, we don't run web
platform tests using the Chrome browser. We run them using content shell. And
this is partially historical. That's how layout tests run. We always ran them
under content shell. And it's partially for I guess what I will call
feasibility. As I talked about earlier, content shell is much simpler than
Chrome. And that means that if you want to just run one test, it is faster, it
is more stable, it is more reliable I guess I would say, than trying to bring
up the behemoth that is Chrome and making sure everything goes correctly. And
this often trips people up because in the upstream world of this web platform
test project, they run the test using the proper Chrome binary. And so they're
different. And different things do happen. Sometimes it's rendering
differences. Sometimes it's because web APIs are not always implemented in both
Chrome and content shell. So yeah, fun fact.
34:19 SHARON: Oh, boy. [LAUGHTER]
34:19 STEPHEN: Oh, yeah.
34:19 SHARON: And we wonder why flakiness is a problem. Ah. [LAUGHS]
34:19 STEPHEN: Yeah. It's a really sort of fun but also scary fact that even if
we put aside web platform test and we just look at layout test, we don't test
what we ship. Layout test running content shell, and then we turn around and
we're like, here's a Chrome binary. Like uh, those are different. But, hey, we
do the best we can.
34:43 SHARON: Yeah. We're out here trying our best. So that all sounds very
cool. Let's move on to our next type of test, which is performance. You might
have heard the term telemetry thrown around. Can you tell us what telemetry is
and what these performance tests are?
34:54 STEPHEN: I mean, I can try. We've certainly gone straight from the thing
I know a lot about into the thing I know very little about. But -
35:05 SHARON: I mean, to Stephen's credit, this is a very hard episode to find
one single guest for. People who are working extensively usually in content
aren't working a ton in performance or web platform stuff. And there's no one
who is - just does testing and does every kind of testing. So we're trying our
best. [INAUDIBLE]
35:24 STEPHEN: Yeah, absolutely. You just need to find someone arrogant enough
that he's like, yeah, I'll talk about all of those. I don't need to know the
details. It's fine. But yeah, performance test, I mean, the name is self
explanatory. These are tests that are trying to ensure the performance of
Chromium. And this goes back to the four S's when we first started Chrome as a
project - speed, simplicity, security, and I've forgotten the fourth S now.
Speed, simplicity, security - OK, let's not reference the four S's then.
[LAUGHTER] You have the Comet. You tell me.
36:01 SHARON: Ah. Oh, I mean, I don't read it every day. Stability. Stability.
36:08 STEPHEN: Stability. God damn it. Let's literally what the rest of this is
about. OK, where were we?
36:13 SHARON: We're leaving this in, don't worry. [LAUGHTER]
36:19 STEPHEN: Yeah. So the basic idea of performance test is to test
performance because as much as you can view behavior as a correctness thing, in
Chromium we also consider performance a correctness thing. It is not a good
thing if a change lands and performance regresses. So obviously, testing
performance is also hard to do absolutely. There's a lot of noise in any sort
of performance testing. An so, we do it essentially heuristically,
probabilistically. We run whatever the tests are, which I'll talk about in a
second. And then we look at the results and we try and say, hey, OK, is there a
statistically significant difference here? And there's actually a whole
performance sheriffing rotation to try and track these down. But in terms of,
yeah, you mentioned telemetry. That weird word. You're like, what is a
telemetry test? Well, telemetry is the name of the framework that Chromium
uses. It's part of the wider catapult project, which is all about different
performance tools. And none of the names, as far as I know, mean anything.
They're just like, hey, catapult, that's a cool name. I'm sure someone will
explain to me now the entire history behind the name catapult and why it's
absolutely vital. But anyway, so telemetry basically is a framework that when
you give it some input, which I'll talk about in a second, it launches a
browser, performs some actions on a web page, and records metrics about those
actions. So the input, the test essentially, is basically a collection of go to
this web page, do these actions, record these metrics. And I believe in
telemetry that's called a story, the story of someone visiting a page, I guess,
is the idea. One important thing to know is that because it's sort of insane to
actually visit real websites, they keep doing things like changing - strange.
We actually cache the websites. We download a version of the websites once and
actually check that in. And when you go run a telemetry test, it's not running
against literally the real Reddit.com or something. It's running against a
version we saved at some point.
38:31 SHARON: And how often - so I haven't really heard of anyone who actually
works on this and that we can't - you don't interact with everyone. But how -
as new web features get added and things in the browser change, how often are
these tests specifically getting updated to reflect that?
38:44 STEPHEN: I would have to plead some ignorance there. It's certainly also
been my experience as a browser engineer who has worked on many web APIs that
I've never written a telemetry test myself. I've never seen one added. My
understanding is that they are - a lot of the use cases are fairly general with
the hope that if you land some performance problematic feature, it will regress
on some general test. And then we can be like, oh, you've regressed. Let's
figure out why. Let's dig in and debug. But it certainly might be the case if
you are working on some feature and you think that it might have performance
implications that aren't captured by those tests, there is an entire team that
works on the speed of Chromium. I cannot remember their email address right
now. But hopefully we will get that and put that somewhere below. But you can
certainly reach out to them and be like, hey, I think we should test the
performance of this. How do I go about and do that?
39:41 SHARON: Yeah. That sounds useful. I've definitely gotten bugs filed
against me for performance stuff. [LAUGHS] Cool. So that makes sense. Sounds
like good stuff. And in talking to some people in preparation for this episode,
I had a few people mention Android testing specifically. Not any of the other
platforms, just Android. So do you want to tell us why that might be? What are
they doing over there that warrants additional mention?
40:15 STEPHEN: Yeah. I mean, I think probably the answer would just be that
Android is such a huge part of our code base. Chrome is a browser, a
multi-platform browser, runs on multiple desktop platforms, but it also runs on
Android. And it runs on iOS. And so I assume that iOS has its own testing
framework. I must admit, I don't know much about that at all. But certainly on
Android, we have a significant amount of testing framework built up around it.
And so there's the option, the ability for you to test your Java code as well
as your C++ code.
40:44 SHARON: That makes sense. And yeah, with iOS, because they don't use
Blink, I guess there's - that reduces the amount of test that they might need
to add, whereas on Android they're still using Blink. But there's a lot of
differences because it is mobile, so they're just, OK, we actually can test
those things. So let's go more general now. At almost every stage, you've
mentioned flakiness. So let's briefly run down, what is flakiness in a test?
41:14 STEPHEN: Yes. So flakiness for a test is just - the definition is just
that the test does not consistently produce the same output. When you're
talking about flakiness, you actually don't care what the output is. A test
that always fails, that's fine. It always fails. But a test that passes 90% of
the time and fails 10%, that's not good. That test is not consistent. And it
will cause problems.
41:46 SHARON: What are common causes of this?
41:46 STEPHEN: I mean, part of the cause is, as I've said, we write a lot of
integration tests in Chromium. Whether those are browser tests, or whether
those are web tests, we write these massive tests that span huge stacks. And
what comes implicitly with that is timing. Timing is almost always the
problem - timing and asynchronicity. Whether that is in the same thread or
multiple threads, you write your test, you run it on your developer machine,
and it works. And you're like, cool, my test works. But what you don't realize
is that you're assuming that in some part of the browser, this function ran,
then this function run. And that always happens in your developer machine
because you have this CPU, and this much memory, and et cetera, et cetera. Then
you commit your code, you land your code, and somewhere a bot runs. And that
bot is slower than your machine. And on that bot, those two functions run in
the opposite order, and something goes horribly wrong.
42:50 SHARON: What can the typical Chrome engineer writing these tests do in
the face of this? What are some practices that you generally should avoid or
generally should try to do more often that will keep this from happening in
your test?
43:02 STEPHEN: Yeah. So first of all, write more unit tests, write less browser
tests, please. Unit tests are - as I've talked about, they're small. They're
compact. They focus just on the class that you're testing. And too often, in my
opinion - again, I'm sure we'll get some nice emails stating I'm wrong - but
too often, in my opinion people go straight to a browser test. And they bring
up a whole browser just to test functionality in their class. This sometimes
requires writing your class differently so that it can be tested by a unit
test. That's worth doing. Beyond that, though, when you are writing a browser
test or a web test, something that is more integration, more end to end, be
aware of where timing might be creeping in. So to give an example, in a browser
test, you often do things like start by loading some web contents. And then you
will try and poke at those web contents. Well, so one thing that people often
don't realize is that loading web contents, that's not a synchronous process.
Actually knowing when a page is finished loading is slightly difficult. It's
quite interesting. And so there are helper functions to try and let you wait
for this to happen, sort of event waiters. And you should - unfortunately, the
first part is you have to be aware of this, which is just hard to be. But the
second part is, once you are aware of where these can creep in, make sure
you're waiting for the right events. And make sure that once those events have
happened, you are in a state where the next call makes sense.
44:28 SHARON: That makes sense. You mentioned rewriting your classes so they're
more easily testable by a unit test. So what are common things you can do in
terms of how you write or structure your classes that make them more testable?
And just that seems like a general good software engineering practice to do.
44:50 STEPHEN: Yeah, absolutely. So one of the biggest ones I think we see in
Chromium is to not use singleton accessors to get at state. And what I mean by
that is, you'll see a lot of code in Chromium that just goes ahead and threw
some mechanism that says, hey, get the current web contents. And as you, I
think, you've talked about on this program before, web contents is this massive
class with all these methods. And so if you just go ahead and get the current
web contents and then go do stuff on that web contents, whatever, when it comes
to running a test, well, it's like, hold on. That's trying to fetch a real web
contents. But we're writing a unit test. What does that even look like? And so
the way around this is to do what we call dependency injection. And I'm sure as
I've said that word, a bunch of listeners or viewers have just recoiled in
fear. But we don't lean heavily into dependency injection in Chromium. But it
is useful for things like this. Instead of saying, go get the web contents,
pass a web contents into your class. Make a web contents available as an input.
And that means when you create the test, you can use a fake or a mock web
contents. We can talk about difference between fakes and mocks as well. And
then, instead of having it go do real things in real code, you can just be
like, no, no, no. I'm testing my class. When you call it web contents do a
thing, just return this value. I don't care about web contents. Someone else is
going to test that.
46:19 SHARON: Something else I've either seen or been told in code review is to
add delegates and whatnot.
46:25 STEPHEN: Mm-hmm.
46:25 SHARON: Is that a good general strategy for making things more testable?
46:25 STEPHEN: Yeah. It's similar to the idea of doing dependency injection by
passing in your web contents. Instead of passing in your web contents, pass in
a class that can provide things. And it's sort of a balance. It's a way to
balance, if you have a lot of dependencies, do you really want to add 25
different inputs to your class? Probably not. But you define a delegate
interface, and then you can mock out that delegate. You pass in that one
delegate, and then when delegate dot get web content is called, you can mock
that out. So very much the same goal, another way to do it.
47:04 SHARON: That sounds good. Yeah, I think in general, in terms of Chrome
specifically, a lot of these testing best practices, making things testable,
these aren't Chrome-specific. These are general software engineering-specific,
C++-specific, and those you can look more into separately. Here we're mostly
talking about what are the Chrome things. Right?
47:24 STEPHEN: Yeah.
47:24 SHARON: Things that you can't just find as easily on Stack Overflow and
such. So you mentioned fakes and mocks just now. Do you want to tell us a bit
about the difference there?
47:32 STEPHEN: I certainly can do it. Though I want to caveat that you can also
just go look up those on Stack Overflow. But yeah. So just to go briefly into
it, there is - in testing you'll often see the concept of a fake version of a
class and also a mock version of a class. And the difference is just that a
fake version of the class is a, what I'm going to call a real class that you
write in C++. And you will probably write some code to be like, hey, when it
calls this function, maybe you keep some state internally. But you're not using
the real web contents, for example. You're using a fake. A mock is actually a
thing out of the Google test support library. It's part of a - Google mock is
the name of the sub-library, I guess, the sub-framework that provides this. And
it is basically a bunch of magic that makes that fake stuff happen
automatically. So you can basically say, hey, instead of a web contents, just
mock that web contents out. And the nice part about mock is, you don't have to
define behavior for any method you don't care about. So if there are, as we've
discussed, 100 methods inside web contents, you don't have to implement them
all. You can be like, OK, I only care about the do Foobar method. When that is
called, do this.
48:51 SHARON: Makes sense. One last type of test, which we don't hear about
that often in Chrome but does exist quite a bit in other areas, is manual
testing. So do we actually have manual testing in Chrome? And if so, how does
that work?
49:03 STEPHEN: Yeah, we actually do. We're slightly crossing the boundary here
from the open Chromium into the product that is Google Chrome. But we do have
manual tests. And they are useful. They are a thing. Most often, you will see
this in two cases as a Chrome engineer. You basically work with the test team.
As I said, all a little bit internal now. But you work with the test team to
define a set of test cases for your feature. And these are almost always
end-to-end tests. So go to this website, click on this button, you should see
this flow, this should happen, et cetera. And sometimes we run these just as
part of the launch process. So when you're first launching a new feature, you
can be like, hey, I would love for some people to basically go through this and
smoke test it, make sure that everything is correct. Some things we test every
release. They're so important that we need to have them tested. We need to be
sure they work. But obviously, all of the caveats about manual testing out
there in the real world, they apply equally to Chromium or to Chrome. Manual
testing is slow. It's expensive. We require people - specialized people that we
have to pay and that they have to sit there, and click on things, and that sort
of thing, and file bugs when it doesn't work. So wherever possible, please do
not write manual tests. Please write automated testing. Test your code, please.
But then, yeah, it can be used.
50:33 SHARON: In my limited experience working on Chrome, the only place that
I've seen there actually be any level of dependency on manual test has been in
accessibility stuff -
50:38 STEPHEN: Yeah.
50:38 SHARON: which kind of makes sense. A lot of that stuff is not
necessarily - it is stuff that you would want to have a person check because,
sure, we can think that the speaker is saying this, but we should make sure
that that's the case.
50:57 STEPHEN: Exactly. I mean, that's really where manual test shines, where
we can't integration test accessibility because you can't test the screen
reader device or the speaker device. Whatever you're using, we can't test that
part. So yes, you have to then have a manual test team that checks that things
are actually working.
51:19 SHARON: That's about all of our written down points to cover. Do you have
any general thoughts, things that you think people should know about tests,
things that people maybe ask you about tests quite frequently, anything else
you'd like to share with our lovely listeners?
51:30 STEPHEN: I mean, I think I've covered most of them. Please write tests.
Write tests not just for code you're adding but for code you're modifying, for
code that you wander into a directory and you say, how could this possibly
work? Go write a test for it. Figure out how it could work or how it couldn't
work. Writing tests is good.
51:50 SHARON: All right. And we like to shout-out a Slack channel of interest.
Which one would be the - which one or ones would be a good Slack channel to
post in if you have questions or want to get more into testing?
52:03 STEPHEN: Yeah. It's a great question. I mean, I always like to - I think
it's been called out before, but the hashtag #halp channel is very useful for
getting help in general. There is a hashtag #wpt channel. If you want to go ask
about web platform tests, that's there. There's probably a hashtag #testing.
But I'm going to admit, I'm not in it, so I don't know.
52:27 SHARON: Somewhat related is there's a hashtag #debugging channel.
52:27 STEPHEN: Oh.
52:27 SHARON: So if you want to learn about how to actually do debugging and
not just do log print debugging.
52:34 STEPHEN: Oh, I was about to say, do you mean by printf'ing everywhere in
your code?
52:41 SHARON: [LAUGHS] So there are a certain few people who like to do things
in an actual debugger or enjoy doing that. And for a test, that can be a useful
thing too - a tool to have. So that also might be something of interest. All
right, yeah. And kind of generally, as you mentioned a lot of things are your
opinion. And it seems like we currently don't have a style guide for tests or
best practices kind of thing. So how can we -
53:13 STEPHEN: [LAUGHS] How can we get there? How do we achieve that?
53:19 SHARON: How do we get one?
53:19 STEPHEN: Yeah.
53:19 SHARON: How do we make that happen?
53:19 STEPHEN: It's a hard question. We do - there is documentation for
testing, but it's everywhere. I think there's `/docs/testing`, which has some
general information. But so often, there's just random READMEs around the code
base that are like, oh, hey, here's the content public test API surface. Here's
a bunch of useful information you might want to know. I hope you knew to look
in this location. Yeah, it's a good question. Should we have some sort of
process for - like you said, like a style guide but for testing? Yeah, I don't
know. Maybe we should enforce that people dependency inject their code.
54:04 SHARON: Yeah. Well, if any aspiring test nerds want to really get into
it, let me know. I have people who are also interested in this and maybe can
give you some tips to get started. But yeah, this is a hard problem and
especially with so many types of tests everywhere. I mean, even just getting
one for each type of test would be useful, let alone all of them together. So
anyway - well, that takes us to the end of our testing episode. Thank you very
much for being here, Stephen. I think this was very useful. I learned some
stuff. So that's cool. So hopefully other people did too. And, yeah, thanks for
sitting and answering all these questions.
54:45 STEPHEN: Yeah, absolutely. I mean, I learned some things too. And
hopefully we don't have too many angry emails in our inbox now.
54:52 SHARON: Well, there is no email list, so people can't email in if they
have issues. [LAUGHTER]
54:58 STEPHEN: If you have opinions, keep them to yourself -
54:58 SHARON: Yeah. [INAUDIBLE]
54:58 STEPHEN: until Sharon invites you on her show.
55:05 SHARON: Yeah, exactly. Yeah. Get on the show, and then you can air your
grievances at that point. [LAUGHS] All right. Thank you.