tag:blogger.com,1999:blog-150459802024-03-16T11:53:09.120-07:00Google Testing BlogIf it ain't broke, you're not trying hard enough.Markohttp://www.blogger.com/profile/16755629501705100354noreply@blogger.comBlogger382125tag:blogger.com,1999:blog-15045980.post-30079402379441906642024-02-27T10:43:00.000-08:002024-03-04T19:05:56.187-08:00Increase Test Fidelity By Avoiding Mocks<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
@media (max-width: 480px), (max-height: 480px) {
.post-content table, .post-content td {
width: auto !important
}
</style>
<div class="body">
<p><span style="font-family: "Times New Roman", serif; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">This article was adapted from a Google </span><a href="http://googletesting.blogspot.com/2007/01/introducing-testing-on-toilet.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Testing on the Toilet</span></a><span style="font-family: "Times New Roman", serif; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> (TotT) episode. You can download a </span><a href="https://docs.google.com/document/d/1h2wbaTg2Ve6v9JDQukHsZGqCL45LdLY_xjONPxHLm1w/edit" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">printer-friendly version</span></a><span style="font-family: "Times New Roman", serif; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> of this TotT episode and post it in your office.</span></p><p><span style="font-family: "Times New Roman", serif; white-space-collapse: preserve;">By Andrew Trenk and Dillon Bly</span></p><span id="docs-internal-guid-dc1a2da9-7fff-3fdf-26f1-0d114e9a820f"><p dir="ltr" style="line-height: 1.2; margin-bottom: 8pt; margin-top: 5pt;"><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Replacing your code’s dependencies with mocks can make unit tests easier to write and faster to run. However, </span><a href="https://testing.googleblog.com/2013/05/testing-on-toilet-dont-overuse-mocks.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">among other problems</span></a><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, using mocks can lead to tests that are less effective at catching bugs.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 8pt; margin-right: -12pt; margin-top: 8pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">The</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> fidelity</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> of a test refers to how closely the behavior of the test resembles the behavior of the production code</span><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. A test with higher fidelity gives you higher confidence that your code will work properly. </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 8pt; margin-top: 8pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">When specifying a dependency to use in a test,</span><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">prefer the highest-fidelity option. </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Learn more in the </span><a href="https://abseil.io/resources/swe-book/html/ch13.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Test Doubles</span></a><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> chapter of the </span><a href="https://abseil.io/resources/swe-book" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Software Engineering at Google</span></a><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> book.</span></p><ol style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px; text-align: left;"><li><span id="docs-internal-guid-23a4b702-7fff-75c3-c87b-955ca3ffbd11"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Try to use the real implementation</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. </span><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">This provides the most fidelity, because the code in the implementation will be executed in the test. There may be tradeoffs when using a real implementation: they can be slow, non-deterministic, or difficult to instantiate (e.g., it connects to an external server). Use your judgment to decide if a real implementation is the right choice.</span></span></li><li><span><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span id="docs-internal-guid-bc85b23d-7fff-1649-0ba7-55b5f88ec7a2"><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Use a fake if you can’t use the real implementation</span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">.</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;"> </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">A </span><a href="https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">fake</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> is a lightweight implementation of an API that behaves similarly to the real implementation, e.g., an in-memory database. A fake ensures a test has high fidelity, but takes effort to write and maintain; e.g., it needs its own tests to ensure that it conforms to the behavior of the real implementation. Typically, the owner of the real implementation creates and maintains the fake. </span></span></span></span></li><li><span><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span id="docs-internal-guid-dc383552-7fff-3f2d-4338-5476b8954325"><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Use a mock if you can’t use the real implementation or a fake</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. A mock reduces fidelity, since it doesn’t execute </span><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">any</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> of the actual implementation of a dependency; its behavior is specified inline in a test (a technique known as </span><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">stubbing</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">), so it may diverge from the behavior of the real implementation. Mocks provide a basic level of confidence that your code works properly, and can be especially useful when testing a code path that is hard to trigger (e.g., an error condition such as a timeout).</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">(</span><span style="font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">Note: Although “mocks” are objects created using mocking frameworks such as </span><a href="https://site.mockito.org/" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">Mockito</span></a><span style="font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> or </span><a href="https://docs.python.org/3/library/unittest.mock.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">unittest.mock</span></a><span style="font-size: 12pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">, the same problems will occur if you manually create your own implementation within tests.)</span></span></span></span></li></ol><div><br /></div><div><span id="docs-internal-guid-d67d67ea-7fff-ac8e-4383-533c4c742131"><div align="left" dir="ltr"><table style="border-collapse: collapse; border: none;"><colgroup><col width="359"></col><col width="406"></col></colgroup><tbody><tr style="height: 33pt;"><td style="border-bottom: solid #999999 1pt; border-color: rgb(153, 153, 153); border-left: solid #999999 1pt; border-right: solid #999999 1pt; border-style: solid; border-top: solid #999999 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="Arial, sans-serif" style="font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">A low-fidelity test</span><span face="Arial, sans-serif" style="font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">: Dependencies are replaced
with mocks. Try to avoid this.</span></p></td><td style="border-bottom: solid #999999 1pt; border-color: rgb(153, 153, 153); border-left: solid #999999 1pt; border-right: solid #999999 1pt; border-style: solid; border-top: solid #999999 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="Arial, sans-serif" style="font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">A high-fidelity test:</span><span face="Arial, sans-serif" style="font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> Dependencies use real implementations or fakes. Prefer this.</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #999999 1pt; border-color: rgb(153, 153, 153); border-left: solid #999999 1pt; border-right: solid #999999 1pt; border-style: solid; border-top: solid #999999 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">@Mock OrderValidator </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">validator</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">;</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">@Mock PaymentProcessor </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">processor</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">;
</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">...</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding-top: 27px;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; white-space-collapse: preserve;">ShoppingCart cart =</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> new ShoppingCart(</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> validator</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">processor</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">);</span></p></td><td style="background-color: #d9ead3; border-bottom: solid #999999 1pt; border-color: rgb(153, 153, 153); border-left: solid #999999 1pt; border-right: solid #999999 1pt; border-style: solid; border-top: solid #999999 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">OrderValidator </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">validator</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> =</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> createValidator();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">PaymentProcessor </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">processor</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> =</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> new FakeProcessor();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">...</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">ShoppingCart cart =</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> new ShoppingCart(</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> validator</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">processor</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">);</span></p></td></tr></tbody></table></div></span></div></span><p><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Aim for as much fidelity as you can achieve without increasing the </span><a href="https://abseil.io/resources/swe-book/html/ch11.html#test_size" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">size</span></a><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> of a test</span><span style="color: #444444; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. At Google, tests are classified by size. Most tests should be small: they must run in a single process and must not wait on a system or event outside of their process. Increasing the fidelity of a small test is often a good choice if the test stays within these constraints. A healthy test suite also includes medium and large tests, which have higher fidelity since they can use heavyweight dependencies that aren’t feasible to use in small tests, e.g., dependencies that increase execution times or call other processes.</span></p>
</div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-17680914197142263302023-12-12T08:06:00.000-08:002023-12-12T08:09:24.066-08:00Let Code Speak for Itself<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
td{
white-space: nowrap;
}
}
</style>
<div class="body">
<span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html" style="color: #4184f3; text-decoration-line: none;">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html" style="color: #4184f3; text-decoration-line: none;">Testing on the Toilet</a> episode. </i></span><span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17px;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1ohtX8DsDeDolm7OgpmEB-JTITImULJZO4eSvwy_7Yo8/edit" style="color: #4184f3; text-decoration-line: none;">printer-friendly version</a> to display in your office.</i></span></span><div><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span><div><span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif;"><span><div><span style="font-size: 17.3333px; white-space-collapse: preserve;">by Shiva Garg and Francois Aube</span></div><div><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></div><div><span id="docs-internal-guid-07d15453-7fff-46c3-1781-8d2c2d227b59"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Comments can be invaluable for understanding and maintaining a code base. But </span><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">excessive</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> comments in code can become unhelpful clutter full of extraneous and/or outdated detail. </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Comments that offer useless (or worse, obsolete) information hurt readability.</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> Here are some tips to let your code speak for itself: </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"></p><ul style="text-align: left;"><li><span id="docs-internal-guid-a88723a2-7fff-e3e2-7042-97d9e362102d"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Write comments to explain the “why”</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> behind a certain approach in code. The comment below has two good reasons to exist: documenting non-obvious behavior and answering a question that a reader is likely to have (i.e. </span><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">why doesn’t this code render directly on the screen?</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">):</span></span></li></ul><p></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="733"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">// Eliminate flickering by rendering the next frame off-screen and swapping into the</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">// visible buffer.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">RenderOffScreen();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">SwapBuffers();</span></p></td></tr></tbody></table></div><div><ul style="text-align: left;"><li><span id="docs-internal-guid-c8d28ee7-7fff-3604-6ed1-81373ef791e6"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Use well-named identifiers </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">to</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">guide the reader and reduce the need for comments:</span></span></li></ul></div><div><table style="border-collapse: collapse; border: none; font-family: Times;"><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// Payout should not happen if the user is</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// in an ineligible country.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">std::unordered_set<std::string> ineligible =</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> {"Atlantis", "Utopia"};</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">if (!ineligible.contains(country)) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Payout(user.user_id);</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span id="docs-internal-guid-9a852830-7fff-04af-bf6e-89488b8518d5"></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p></td><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Roboto Mono, monospace;"><span style="font-size: 13.3333px; white-space: pre;">if (IsCountryEligibleForPayout(country)) {
Payout(user.user_id);
}</span></span></p></td></tr></tbody></table></div><div><ul style="text-align: left;"><li><span id="docs-internal-guid-163a2205-7fff-9926-c146-e0c7d0347d60"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Write function comments (a.k.a. API documentation) that describe intended meaning and purpose, not implementation details. </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Choose unambiguous function signatures that callers can use without reading any documentation. Don’t explain inner details</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">that could change without affecting the contract with the caller:</span></span></li></ul></div><div><table style="border-collapse: collapse; border: none; font-family: Times;"><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// Reads an input string containing either a</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// number of milliseconds since epoch or an</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// ISO 8601 date and time. </span><span style="background-color: transparent; color: #0674b3; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">I</span><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">nvokes the</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// Sole, Laces, and ToeCap APIs, then</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// returns an object representing the Shoe</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// available then or nullptr if none were.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span id="docs-internal-guid-73f821bc-7fff-2b18-2cbe-da3ba7ff45a8"></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">Shoe* ModelAvailableAt(char* time);</span></p></td><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// Returns the Shoe that was available for</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// purchase at `time`. If no model was</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">// available, throws a runtime_error.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span id="docs-internal-guid-3c0d9ae7-7fff-a34d-d9c9-d85fe285be69"></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">Shoe ModelAvailableAt(time_t time);</span></p></td></tr></tbody></table></div><div><ul style="text-align: left;"><li><span id="docs-internal-guid-b39f4ef8-7fff-3271-8591-4b0021aa4687"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Omit comments that state the obvious</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. Superfluous comments increase code maintenance when code gets refactored and don’t add value, only overhead to keep these comments current:</span></span></li></ul></div><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="733"></col></colgroup><tbody><tr style="height: 28.5pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">// Increment counter by 1.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; padding: 0pt 0pt 0pt 18pt; text-indent: -18pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">counter++;</span></p></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Learn more about writing good comments: </span><a href="https://testing.googleblog.com/2017/07/code-health-to-comment-or-not-to-comment.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">To Comment or Not to Comment?</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><a href="https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Best practices for writing code comments</span></a></p><div><br /></div></span></div><div style="font-size: 17.3333px; font-style: italic; white-space-collapse: preserve;"><br /></div></span></span></div></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-31944748354322652402023-12-05T05:19:00.000-08:002023-12-13T05:43:55.923-08:00Exceptional Exception Handling<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body">
<span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html" style="color: #4184f3; text-decoration-line: none;">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html" style="color: #4184f3; text-decoration-line: none;">Testing on the Toilet</a> episode. </i></span><span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17px;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1DplVjabYLpASmmr6DRXfA5L3y4NPsiiYVhW-BNVP4ZM/edit" style="color: #4184f3; text-decoration-line: none;">printer-friendly version</a> to display in your office.</i></span></span><div><span color="rgba(0, 0, 0, 0.87)" style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span><div><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;">by Yiming Sun</span></div><div><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;"><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span></span></div><div><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;"><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">Have you ever seen huge exception-handling blocks? Here is an example in Java, although you may have seen similar problems in Python, TypeScript, Kotlin, or any language that supports exceptions.</span></span></div><div><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;"><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span></span></div><div><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;"><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">Let's assume we are calling </span><span style="color: black; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">bakePizza()</span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> to bake a pizza, and it can be overbaked, throwing a </span><span style="color: black; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">PizzaOverbakedException</span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">.</span>
</span></div><div><span color="rgba(0, 0, 0, 0.87)"><span id="docs-internal-guid-667a9b14-7fff-51a3-d4d8-eaee6b379caa"><div align="left" dir="ltr" style="color: rgba(0, 0, 0, 0.87); font-family: "Times New Roman", serif; font-size: 17px; margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 545pt;"><colgroup><col></col></colgroup><thead><tr style="height: 0pt;"><th scope="col" style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">class </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">PizzaOverbakedException</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> extends Exception {};</span></p><div style="text-align: left;"><br /></div><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">void </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">bakePizza </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">() throws </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">PizzaOverbakedException</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> {};</span></p><div style="text-align: left;"><br /></div><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">try {</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">// 100+ lines of code to prepare pizza ingredients.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; color: #1c4587; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">...</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">bakePizza</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">();</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> // Another 100+ lines of code to deliver pizza to a customer.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">...</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">} catch (</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Exception</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> e) {</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;"> throw new IllegalStateException(); </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">// Root cause ignored while throwing new exception.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline; white-space-collapse: preserve;">}</span></p></th></tr></thead></table></div><p dir="ltr" style="color: rgba(0, 0, 0, 0.87); font-family: "Times New Roman", serif; font-size: 17px; line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Here are the problems with the above code:</span></p><ul style="color: rgba(0, 0, 0, 0.87); font-family: "Times New Roman", serif; font-size: 17px; margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px; text-align: left;"><li><span id="docs-internal-guid-42e9b2ec-7fff-f83f-4997-e34204d4b18d"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Obscuring the logic</span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. The method </span><span style="background-color: #efefef; color: black; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">bakePizza()</span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, is obscured by the additional lines of code of preparation and delivery, so unintended exceptions from preparation and delivery may be caught.</span></span></li><li><span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span id="docs-internal-guid-361fb7c4-7fff-2a64-c7b6-59d13bc2d4e5"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Catching the general exception</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. </span><span style="background-color: #efefef; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">catch (Exception e)</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> will catch everything, despite that we might only want to handle </span><span style="background-color: #efefef; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">PizzaOverbakedException</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> here.</span></span></span></span></li><li><span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span id="docs-internal-guid-5abdef6f-7fff-563c-75a3-89958d45b451"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Rethrowing a general exception, with the original exception ignored</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. This means that the root cause is lost - we don't know what exactly goes wrong with pizza baking while debugging.</span></span></span></span></span></span></li></ul><p dir="ltr" style="color: rgba(0, 0, 0, 0.87); font-family: "Times New Roman", serif; font-size: 17px; line-height: 1.2; margin-bottom: 5pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Here is a better alternative, rewritten to avoid the problems above.</span></p><div align="left" dir="ltr" style="color: rgba(0, 0, 0, 0.87); font-family: "Times New Roman", serif; font-size: 17px; margin-left: 0pt;"><table style="border-collapse: collapse; border: none; white-space: nowrap;"><colgroup><col width="739"></col></colgroup><tbody><tr style="height: 21pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">class </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">PizzaOverbakedException</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> extends Exception {};</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">void </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">bakePizza </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">() throws </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">PizzaOverbakedException</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> {};</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">// 100+ lines of code to prepare pizza ingredients.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">...</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">try {</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">bakePizza</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">();</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">} catch (</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">PizzaOverbakedException</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> e) { </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> // Other exceptions won’t be caught.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> // Rethrow a more meaningful exception; so that we know pizza is overbaked.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> throw new IllegalStateException(“You burned the pizza!”, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">e</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">); </span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">}</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">// Another 100+ lines of code to deliver pizza to a customer.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">...</span></p></td></tr></tbody></table></div></span></span></div></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-73144105543239629812023-11-28T05:00:00.000-08:002023-11-28T05:27:41.274-08:00Clean Up Code Cruft<span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html" style="color: #4184f3; text-decoration-line: none;">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html" style="color: #4184f3; text-decoration-line: none;">Testing on the Toilet</a> episode. </i></span><span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17px;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1zAkHB8jMa75kpvAVAAi--tJTwXOOc6Md8CvwKHn6SsM/edit" style="color: #4184f3; text-decoration-line: none;">printer-friendly version</a> to display in your office.</i></span></span><div><span color="rgba(0, 0, 0, 0.87)" style="font-family: "Times New Roman", serif; font-size: 17px;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span style="font-family: "Times New Roman", serif; font-size: 13pt; white-space-collapse: preserve;">By Per Jacobsson</span></div><div><span id="docs-internal-guid-6f346e48-7fff-b748-81f5-ec7dd2815b8a"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">The book </span><a href="https://www.oreilly.com/library/view/clean-code-a/9780136083238/" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Clean Code</span></a><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> discusses a camping rule that is good to keep in the back of your mind when writing code:</span></p><br /><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #fff2cc; border-bottom: dotted #aaaaaa 0.75pt; border-left: dotted #aaaaaa 0.75pt; border-right: dotted #aaaaaa 0.75pt; border-top: dotted #aaaaaa 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-left: 28pt; margin-right: 28pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Leave the campground cleaner than you found it</span></p></td></tr></tbody></table></div><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">So how does that fit into software development? The thinking is this: </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">When you make changes to code that can potentially be improved, try to make it just a little bit better</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">This doesn't necessarily mean you have to go out of your way to do huge refactorings. </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Changing something small can go a long way</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">:</span></p><ul style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Rename a variable to something more descriptive. </span></p></li><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Break apart a huge function into a few logical pieces.</span></p></li><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Fix a lint warning.</span></p></li><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Bring an outdated comment up to date.</span></p></li><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Extract duplicated lines to a function.</span></p></li><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Write a unit test for an untested function.</span></p></li><li aria-level="1" dir="ltr" style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Whatever other itch you feel like scratching.</span></p></li></ul><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Cleaning up the small things often makes it easier to see and fix the bigger issues.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">But what about "If it's not broken, don't fix it"? Changing code can be risky, right? There's no obvious rule, but if you're always afraid to change your code,</span><span style="color: #7e0021; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">you have bigger problems. </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Cruft in code that is actively being changed is like credit card debt. Either you pay it off, or you eventually go bankrupt.</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Unit tests help mitigate the risk of changing code. When you're doing cleanup work, be sure there are unit tests for the things you're about to change. This may mean writing a few new ones yourself.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">If you’re working on a change and end up doing some minor cleanup, you can often include these cleanups in the same change. Be careful to not distract your code reviewer by adding too many unrelated cleanups. An option that works well is to </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">send the cleanup fixes in multiple tiny changes that are small enough to just take a few seconds to review</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">As mentioned in the book: </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">"Can you imagine working on a project where the code simply got better as time passed?"</span></p><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">“Clean Code: A Handbook of Agile Software Craftsmanship” by Robert C. Martin was published in 2008.</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></span></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-62110366896123807712023-11-06T05:45:00.000-08:002023-11-06T05:45:35.503-08:00Write Clean Code to Reduce Cognitive Load<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body">
<span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1AgcBG2iOmzEWIKYs6hNDyTMZ-an0SOwmJZRsX898XD0/edit?pli=1">printer-friendly version</a> to display in your office.</i></span></span><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;">By Andrew Trenk</span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="white-space-collapse: preserve;"><span id="docs-internal-guid-efdb23bd-7fff-5b45-0037-501b8ba2ac7c"><p dir="ltr" style="font-size: 17.3333px; line-height: 1.2; margin-bottom: 10pt; margin-top: 5pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">Do you ever read code and find it hard to understand? You may be experiencing cognitive load!</span></p><p dir="ltr" style="font-size: 17.3333px; line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><a href="https://en.wikipedia.org/wiki/Cognitive_load" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">Cognitive load</span></a><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;"> refers to the amount of mental effort required to complete a task</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">.</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;"> </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">When reading code, you have to keep in mind information such as values of variables, conditional logic, loop indices, data structure state, and interface contracts. </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Cognitive load increases as code becomes more complex</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. People can typically hold up to 5–7 separate pieces of information in their short-term memory (</span><a href="https://en.wikipedia.org/wiki/Working_memory#Capacity" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">source</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">); code that involves more information than that can be difficult to understand.</span></p><p dir="ltr" style="font-size: 17.3333px; line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt; text-align: center;"><span style="border: none; display: inline-block; height: 186px; overflow: hidden; width: 444px;"><img alt="Two brains displayed side-by-side.
The left brain is red with a sad face. The text below it says 'Complex code: Too much cognitive load'.;
The left brain is green with a happy face. The text below it says 'Simple code: Minimal cognitive load'." height="186" src="https://lh7-us.googleusercontent.com/WzdHDiRPb_QYc-sXvRxiM_5qb07FAfm5Jp3zin9MlU92Rpjjz0KLx7JKUwe1PDPRKZsK6Fg7hGhNN50qa5ftWFpnq7fUMrar0EOckvDZQ9HgBw7nf3IHOTVf4cFrSdMOGCVwxzw5VC-UWyBivPSclWQ" style="margin-left: 8.30025px; margin-top: -5.65394e-14px;" width="435.69975305574087" /></span></p><p dir="ltr" style="font-size: 17.3333px; line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">Cognitive load is often higher for other people reading code you wrote than it is for yourself,</span><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline;"> since readers need to understand your intentions. Think of the times you read someone else’s code and struggled to understand its behavior. One of the </span><a href="https://google.github.io/eng-practices/review/reviewer/looking-for.html#complexity" style="font-size: 17.3333px; font-weight: 400; text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">reasons for code reviews</span></a><span style="color: black; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline;"> is to allow reviewers to check if the changes to the code cause too much cognitive load. </span><span style="color: black; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 400; vertical-align: baseline;">Be kind to your co-workers: reduce their cognitive load by writing clean code.</span></span></p><p dir="ltr" style="font-size: 17.3333px; line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">The key to reducing cognitive load is to </span><span style="color: #980000; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">make code simpler</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> so it can be understood more easily by readers. This is the principle behind many code health practices. Here are some examples:</span></p><ul style="font-size: 17.3333px; margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Limit the amount of code in a function or file. </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Aim to keep the code concise enough that you can keep the whole thing in your head at once. Prefer to </span><a href="https://martinfowler.com/bliki/FunctionLength.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">keep functions small</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">, and try to limit each class to a </span><a href="https://en.wikipedia.org/wiki/Single-responsibility_principle" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">single responsibility</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">.</span></p></li><li aria-level="1" dir="ltr" style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Create abstractions to hide implementation details</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">. </span><a href="https://en.wikipedia.org/wiki/Abstraction_(computer_science)" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">Abstractions</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> such as functions and interfaces allow you to deal with simpler concepts and hide complex details. However, remember that over-engineering your code with too many abstractions also causes cognitive load.</span></p></li><li aria-level="1" dir="ltr" style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Simplify control flow.</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> Functions with too many </span><span style="font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">if</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> statements or loops can be hard to understand since it is difficult to keep the entire </span><a href="https://testing.googleblog.com/2023/10/simplify-your-control-flows.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">control flow</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> in your head. Hide complex logic in helper functions, and </span><a href="https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">reduce nesting</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> by using early returns to handle special cases.</span></p></li><li aria-level="1" dir="ltr" style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Minimize mutable state.</span><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> Stateless code is simpler to understand. For example, avoid mutable class fields when possible, and make types </span><a href="https://en.wikipedia.org/wiki/Immutable_object" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">immutable</span></a><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">.</span></p></li><li aria-level="1" dir="ltr" style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Include only relevant details in tests.</span><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> A test can be </span><a href="https://testing.googleblog.com/2023/10/include-only-relevant-details-in-tests.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">hard to follow</span></a><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> if it includes boilerplate test data that is irrelevant to the test case, or relevant test data is hidden in helper functions.</span></p></li><li aria-level="1" dir="ltr" style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Don’t overuse mocks in tests.</span><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> </span><a href="https://testing.googleblog.com/2013/05/testing-on-toilet-dont-overuse-mocks.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">Improper use of mocks</span></a><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> can lead to tests that are cluttered with calls that expose implementation details of the system under test.</span></p></li></ul><div><span id="docs-internal-guid-aa143d67-7fff-72bb-0c67-c9dcd32532e8"><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">Learn more about cognitive load in the book </span><a href="https://www.manning.com/books/the-programmers-brain" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">The Programmer’s Brain</span></a><span style="font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">, by Felienne Hermans.</span></span></div></span></span></span></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-25029167288854685792023-10-30T05:43:00.001-07:002023-10-30T06:44:16.624-07:00Include Only Relevant Details In Tests<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body">
<span style="font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space-collapse: preserve;">This article was adapted from a Google </span><a href="http://googletesting.blogspot.com/2007/01/introducing-testing-on-toilet.html" style="font-family: times, "times new roman", serif;"><span style="color: #1155cc; font-style: italic; vertical-align: baseline; white-space-collapse: preserve;">Testing on the Toilet</span></a><span style="font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space-collapse: preserve;"> (TotT) episode. You can download a </span><a href="https://docs.google.com/document/d/1huR_E1pEzNyQL__kcOBBw989H_IYAdmgYzadaG8IUgM/edit" rel="nofollow" style="font-family: times, "times new roman", serif;"><span style="color: #1155cc; font-style: italic; vertical-align: baseline; white-space-collapse: preserve;">printer-friendly version</span></a><span style="font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space-collapse: preserve;"> of this TotT episode and post it in your office.</span><div><span style="font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></div><div><span style="font-family: times, "times new roman", serif; vertical-align: baseline; white-space-collapse: preserve;">By Dagang Wei</span></div><div><span style="font-family: times, "times new roman", serif; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></div><div><span style="font-family: times, "times new roman", serif; vertical-align: baseline; white-space-collapse: preserve;"><span id="docs-internal-guid-60eea5c2-7fff-f5fa-fbe2-92dbe836ca9f"><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">What problem in the code below makes the test hard to follow?</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span id="docs-internal-guid-f3b3abed-7fff-f649-592a-6f76a08e11a7"></span></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><span id="docs-internal-guid-75caae8d-7fff-df7f-beb6-1d201e3367f7"><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="733"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">def test_get_balance(self):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> settings = BankSettings(</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">FDIC_INSURED</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">REGULATED</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">US_BASED</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> account = Account(settings, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">ID</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">BALANCE</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">ADDRESS</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">NAME</span><span style="background-color: transparent; color: #980000; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">EMAIL, PHONE</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> self.assertEqual(account.GetBalance(), </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">BALANCE</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">)</span></p></td></tr></tbody></table></div></span></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span id="docs-internal-guid-3d51ae6e-7fff-5297-ffcb-5a7cad19574d"></span></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">The problem is that there is a lot of noise in the account creation code</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">,</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> which makes it hard to tell which details are relevant to the assert statement. </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 5pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">But going from one extreme to the other can also make the test hard to follow:</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><span id="docs-internal-guid-00e4f065-7fff-8626-b886-682548701403"><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="733"></col></colgroup><tbody><tr style="height: 11.0654pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">def test_get_balance(self):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> account = _create_account()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> self.assertEqual(account.GetBalance(), </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">BALANCE</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">)</span></p></td></tr></tbody></table></div></span></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Now the problem is that critical details are hidden</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> in the </span><span style="font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">_create_account()</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> helper function, so it’s not obvious where the </span><span style="font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">BALANCE</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> field comes from. In order to understand the test, you need to switch context by diving into the helper function.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 5pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">A good test should include only details relevant to the test, while hiding noise:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 5pt;"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;"><span id="docs-internal-guid-db2b8521-7fff-9809-ec5b-b85139f371c7" style="font-weight: normal;"></span></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 554.4pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def test_get_balance():</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> account = _create_account(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.assertEqual(account.GetBalance(), </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p></td></tr></tbody></table></div><div align="left" dir="ltr" style="margin-left: 0pt;"></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">By following this advice,</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">it should be easy to see the flow of data throughout a test</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. For example:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 4pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span id="docs-internal-guid-73545355-7fff-45d0-5cfc-2f8668c54659"></span></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="370"></col><col width="370"></col></colgroup><tbody><tr style="height: 20.1987pt;"><td style="background-color: white; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 13pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">Bad (flow of data is hidden):</span></p></td><td style="background-color: white; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 13pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">Good (flow of data is clear):</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def test_bank_account_overdraw_fails(self):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> account = _create_account()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> outcome = _overdraw(account)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self._assert_withdraw_failed(</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> outcome, account)</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def _create_account():</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> settings = BankSettings(...)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Account(settings, </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">, ...)</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def _overdraw(account):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> # Boilerplate code</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 9pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> ...</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><br /><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return account.Withdraw(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE + 1</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def _assert_withdraw_failed(</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self, outcome, account):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.assertEqual(outcome, FAILED)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.assertEqual(</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> account.GetBalance(), </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p></td><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def test_bank_account_overdraw_fails(self):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> account = _create_account(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> outcome = _withdraw(account, </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE + 1</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.assertEqual(outcome, FAILED)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.assertEqual(</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> account.GetBalance(), </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">BALANCE</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><br /><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def _create_account(balance):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> settings = BankSettings(...)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Account(settings, balance</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">,</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> ...)</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def _withdraw(account, amount):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> # Boilerplate code</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 9pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> ...</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><br /><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return account.Withdraw(amount)</span></p></td></tr></tbody></table></div></span></span></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-56026401383033137532023-10-24T05:46:00.000-07:002023-10-24T05:46:17.233-07:00Simplify Your Control Flows<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body">
<span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1wi50sHboQQPhsgq9VosWwn91RrNDhOLi9C-YivZiO_c/edit">printer-friendly version</a> to display in your office.</i></span></span><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;">By Jeff Hoy</span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><span id="docs-internal-guid-c1f707f0-7fff-060e-7c40-808b329bfa39"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">When adding loops and conditionals, even simple code can become difficult to understand.</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Consider this change:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;"><span id="docs-internal-guid-eac1e3b3-7fff-2512-5e30-72fbe0a6d397" style="font-weight: normal;"></span></span></p><div align="left" dir="ltr" style="margin-left: 27.75pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="328"></col><col width="334"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: white; border-bottom: solid #ffffff 0.75pt; border-color: rgb(0, 0, 0) rgb(0, 0, 0) rgb(255, 255, 255); border-left: solid #000000 0.75pt; border-right: solid #000000 0.75pt; border-style: solid; border-top: solid #000000 0.75pt; border-width: 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 7pt 7pt 0pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">if (commode.HasPreferredCustomer()) {</span></p></td><td style="background-color: white; border-bottom: solid #ffffff 0.75pt; border-color: rgb(0, 0, 0) rgb(0, 0, 0) rgb(255, 255, 255); border-left: solid #000000 0.75pt; border-right: solid #000000 0.75pt; border-style: solid; border-top: solid #000000 0.75pt; border-width: 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 7pt 7pt 0pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">if (commode.HasPreferredCustomer()) {</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: white; border-left: 0.75pt solid rgb(0, 0, 0); border-right: 0.75pt solid rgb(0, 0, 0); border-top: 0.75pt solid rgb(255, 255, 255); overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.WarmSeat();</span></p></td><td style="background-color: white; border-bottom: solid #ffffff 0.75pt; border-color: rgb(255, 255, 255) rgb(0, 0, 0); border-left: solid #000000 0.75pt; border-right: solid #000000 0.75pt; border-style: solid; border-top: solid #ffffff 0.75pt; border-width: 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.WarmSeat();</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f3f3f3; border-left: 0.75pt solid rgb(0, 0, 0); border-right: 0.75pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt; vertical-align: top;"><br /></td><td style="background-color: #95f195; border-bottom: solid #95f195 0.75pt; border-color: rgb(255, 255, 255) rgb(0, 0, 0) rgb(149, 241, 149); border-left: solid #000000 0.75pt; border-right: solid #000000 0.75pt; border-style: solid; border-top: solid #ffffff 0.75pt; border-width: 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">} else if (commode.CustomerOnPhone()) {</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f3f3f3; border-left: 0.75pt solid rgb(0, 0, 0); border-right: 0.75pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt; vertical-align: top;"><br /></td><td style="background-color: #95f195; border-bottom: solid #ffffff 0.75pt; border-color: rgb(149, 241, 149) rgb(0, 0, 0) rgb(255, 255, 255); border-left: solid #000000 0.75pt; border-right: solid #000000 0.75pt; border-style: solid; border-top: solid #95f195 0.75pt; border-width: 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.ChillSeat();</span></p></td></tr><tr style="height: 9.75pt;"><td style="background-color: white; border-bottom: 0.75pt solid rgb(0, 0, 0); border-left: 0.75pt solid rgb(0, 0, 0); border-right: 0.75pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt 7pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p></td><td style="background-color: white; border-bottom: solid #000000 0.75pt; border-color: rgb(255, 255, 255) rgb(0, 0, 0) rgb(0, 0, 0); border-left: solid #000000 0.75pt; border-right: solid #000000 0.75pt; border-style: solid; border-top: solid #ffffff 0.75pt; border-width: 0.75pt; overflow-wrap: break-word; overflow: hidden; padding: 0pt 7pt 7pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">
While the above change may seem simple, even adding a single </span><span style="font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">else</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> statement can make the code harder to follow since </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">the complexity of code grows quickly with its size</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. Below we see the code surrounding the above snippet; the control flow on the right illustrates how much a reader needs to retain:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span id="docs-internal-guid-1c80c8df-7fff-b5e3-ea16-b2e19da4cce8"></span></span></p><div align="left" dir="ltr" style="margin-left: 0.75pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="380"></col><col width="339"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 7.2pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">while (commode.StillOccupied()) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if (commode.HasPreferredCustomer()) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.WarmSeat();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #95f195; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> } </span><span style="background-color: #95f195; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">else if (commode.CustomerOnPhone()) {</span><span face="Consolas,sans-serif" style="background-color: #95f195; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #95f195; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.ChillSeat(); </span><span face="Consolas,sans-serif" style="background-color: #95f195; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if (commode.ContainsKale()) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.PrintHealthCertificate();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> break;</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p></td><td style="background-color: white; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 7.2pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 13pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><span style="border: none; display: inline-block; height: 134px; overflow: hidden; width: 170px;"><img height="134" src="https://lh7-us.googleusercontent.com/pH4drAWtciV0pwKh2TM6Dg3rD0CeXykX2N1mHQJT02NMb__OmC8luVQiHo2PP48U-99MtQqlVlDuA4BRWgHqLr-x1PuVKhflK-PtuZz8QW0Icgp24-c0nCUXz6LgXZJ-TdObSmIHgSWvitn1c_rkivc" style="margin-left: 0px; margin-top: 0px;" width="170" /></span></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 11pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">Code Control Flow with 5 structures and 9 edges:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 11pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">challenging for a reader to retain in memory.</span></p></td></tr></tbody></table></div><div align="left" dir="ltr" style="margin-left: 0.75pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span></div><div align="left" dir="ltr" style="margin-left: 0.75pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">In order to fully understand the code, </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">the reader needs to keep the entire control flow in their head</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. However, the retention capacity of working memory is limited (</span><a href="https://en.wikipedia.org/wiki/Working_memory#Capacity" style="font-size: 17.3333px; text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">source</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">) Code path complexity will also challenge the reader, and can be measured using </span><a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity" style="font-size: 17.3333px; text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">cyclomatic complexity</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">.</span></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="color: #990000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">
To reduce cognitive overhead of complex code, </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">push implementation logic down into functions and methods</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. For example, if the </span><span style="font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">if/else</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> structure in the above code is moved into an </span><span style="font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">AdjustSeatTemp()</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"> method, the reviewer can review the two blocks independently, each having a much simpler control graph:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span id="docs-internal-guid-afe35382-7fff-e31c-9f8f-9b9adf2a5952"></span></p><div align="left" dir="ltr" style="margin-left: 0.75pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="324"></col><col width="203"></col><col width="192"></col></colgroup><tbody><tr style="height: 77pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 7.2pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">while (commode.StillOccupied()) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">commode.AdjustSeatTemp();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if (commode.ContainsKale()) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> commode.PrintHealthCertificate();</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> break;</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p></td><td style="border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 7.2pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><span style="border: none; display: inline-block; height: 71px; overflow: hidden; width: 156px;"><img height="71" src="https://lh7-us.googleusercontent.com/zVM_EP3hjr4ETeIbV-wigEQas0pdY9_XAa0wojjtK6oQHNtBkkhiBOTym_mqWl-WKviIti0IQuKV2QRh884WtVrE27KZmX4vpiGPHp2NbUQuHoW9alYrk5QIuyFhRoIosYFxrjd97HEMOcfJ-rLuUIM" style="margin-left: 0px; margin-top: 0px;" width="156" /></span></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 11pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">3 control structures and 5 edges: easier to remember</span></p></td><td style="border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 7.2pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 11pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"><span style="border: none; display: inline-block; height: 65px; overflow: hidden; width: 75px;"><img height="65" src="https://lh7-us.googleusercontent.com/BwS3I9uDSeSN8PnKlzjkQLNztOfqXfZJ5m9Jrq2KH2BIZqoKr-QOW3Aa1ER6Al6mBkdK564tS01SvGYjy4l3cAMYGokyLcxskqV2z3YR0gIYWxnLhK79S5OgKtFsKpX85fKy_4SnUlA_kgUV-KgMnQg" style="margin-left: 0px; margin-top: 0px;" width="75" /></span></span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 11pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">Commode::AdjustSeatTemp()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt; text-align: center;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 11pt; font-style: italic; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">with 2 structures and 4 edges</span></p></td></tr></tbody></table></div><div align="left" dir="ltr" style="margin-left: 0.75pt;"><span style="font-size: 13pt;"><br /></span></div><div align="left" dir="ltr" style="margin-left: 0.75pt;"><span style="font-size: 13pt;">Avoiding complexity makes code easier to follow. In addition, code reviewers are more likely to identify logic errors, and maintainers are less likely to introduce complex code.</span></div><div><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span></div></span></span></span></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-69254606388193435022023-10-17T05:50:00.003-07:002023-10-17T05:53:40.933-07:00Improve Readability With Positive Booleans<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body">
<div><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1eKfofxfQYq86Icx0FxneQ47kwss5OQX5GhGo1yGAVOE/edit">printer-friendly version</a> to display in your office.</i></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;">By </span></span><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;">Max Kanat-Alexander</span></span></div><div><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span id="docs-internal-guid-79963df3-7fff-a824-398c-886754b4ff2c"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Reading healthy code should be as easy as reading a book in your native language. You shouldn’t have to stop and puzzle over what a line of code is doing. One small trick that can assist with this is to </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">make boolean checks about something </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">positive</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> rather than about something </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">negative.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Here’s an extreme example:</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 554.4pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">if not nodisable_kryponite_shield</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> devise_clever_escape_plan()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> engage_in_epic_battle()</span></p></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">What does that code do? Sure, you </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">can </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">figure it out, but </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">healthy code is not a puzzle, it’s a </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">simple communication.</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> Let’s look at two principles we can use to simplify this code.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">1. </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Name your flags and variables in such a way that they represent the </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">positive </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">check you wish to make</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> (the presence of something, something being enabled, something being true) rather than the </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">negative</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> check you wish to make (the absence of something, something being disabled, something being false).</span></p><ol style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: white; font-family: "Times New Roman", serif; font-size: 6pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; margin-left: -18pt; vertical-align: baseline; white-space: pre;"><br /></li></ol><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 554.4pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #fff2cc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">if not</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">enable_kryponite_shield</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> devise_clever_escape_plan()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> engage_in_epic_battle()</span></p></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">That is already easier to read and understand than the first example.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">2. </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">If your conditional looks like “</span><span style="color: #980000; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">if not </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">…</span><span style="color: #980000; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> else </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">…</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">” then reverse it to put the positive case first.</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 554.4pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">if enable_kryponite_shield</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> engage_in_epic_battle()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else:</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> devise_clever_escape_plan()</span></p></td></tr></tbody></table></div><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Now the intention of the code is immediately obvious.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">There are many other contexts in which this gives improvements to readability. For example, the command </span><span style="color: #38761d; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">foo --disable_feature=False</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> is harder to read and think about than </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /></span><span style="color: #38761d; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">foo --enable_feature=True</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, particularly when you change the default to enable the feature.</span></p><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">There are some exceptions (for example, in Python, </span><span style="color: #38761d; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">if foo is not None</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> could be considered a “positive check” even though it has a “not” in it), but in general </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">checking the presence or absence of a positive is simpler for readers to understand than checking the presence or absence of a negative.</span></span></div><div><br /></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-71163976156810247132023-10-11T05:21:00.002-07:002023-10-12T09:34:38.037-07:00Shell Scripts: Stay Small & Simple<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body">
<span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1CL9sXdVJG5aAySS5CintGg1yTiZZowJ98swsoTRcRuA/edit?pli=1">printer-friendly version</a> to display in your office.</i></span></span><div><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;">By David Mandelberg</span></span></div><div><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></span></div><div><span id="docs-internal-guid-97010587-7fff-3900-52cf-f42d900a5f9d"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Shell scripts (including Bash scripts) can be convenient for automating simple command line procedures, and they are often better than keeping complicated commands in a single developer's history. However, </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">shell scripts can be hard to understand and maintain, and are typically not as well-supported as other programming languages</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. Shell scripts have less support for unit testing, and there is likely a lower chance that somebody reading one will be experienced with the language.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Python, Go, or other general-purpose languages are often better choices than shell. Shell is convenient for some simple use cases, and the </span><a href="https://google.github.io/styleguide/shellguide.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Google shell style guide</span></a><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> can help with writing better shell scripts. But it is difficult, even for experienced shell scripters, to mitigate the risks of its many surprising behaviors. So whenever possible, </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">use shell scripts only for small, simple use cases, or avoid shell entirely.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Here are some examples of mistakes that are far too easy to make when writing a shell script (see </span><a href="https://mywiki.wooledge.org/BashPitfalls" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Bash Pitfalls</span></a><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> for many more):</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"></p><ul style="text-align: left;"><li><span id="docs-internal-guid-72aa563b-7fff-3843-92d8-0714277d7f92"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Forgetting to quote something can have surprising results</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, due to shell's complicated evaluation rules. E.g., even if a wildcard is properly quoted, it can still be unexpectedly expanded elsewhere:</span></span></li></ul><p></p><div align="left" dir="ltr" style="margin-left: 18.75pt;"><table style="border-collapse: collapse; border: none; white-space: nowrap;"><colgroup><col width="714"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">$ msg='Is using bash a </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">pro?</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> Or a con?'</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">$ echo $msg </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"># Note that there's a subdirectory called 'proc' in the current directory.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Is using bash a </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">proc</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> Or a con? </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"># ? was unexpectedly treated as a wildcard.</span></p></td></tr></tbody></table></div><div><ul style="text-align: left;"><li><span id="docs-internal-guid-9fddfe8e-7fff-b0df-1d33-8acf0ed4bfd5"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Many things that would be function arguments in other languages are command line arguments in shell. </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Command line arguments are world-readable, so they can leak secrets</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">:</span></span></li></ul></div><div align="left" dir="ltr" style="margin-left: 17.25pt;"><table style="border-collapse: collapse; border: none; white-space: nowrap;"><colgroup><col width="716"></col></colgroup><tbody><tr style="height: 33.75pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">$ curl -H "Authorization: Bearer </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">${SECRET}</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">" "$URL" &</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">$ ps aux </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"># The current list of processes shows the secret.</span></p></td></tr></tbody></table></div><div style="text-align: left;"><ul style="text-align: left;"><li><span id="docs-internal-guid-271e19ec-7fff-13c3-759a-d475bdf74abb"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">By default, the shell ignores all errors from commands</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">, which can cause severe bugs if code assumes that earlier commands succeeded. The command </span><span style="color: #00931e; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">set -e</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> can appear to force termination at the first error, but </span><a href="https://mywiki.wooledge.org/BashPitfalls#errexit" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">its behavior is inconsistent</span></a><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. For example, </span><span style="color: #00931e; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">set -e</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> does not affect some commands in pipelines (like </span><span style="color: #00931e; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">false</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> in </span><span style="color: #00931e; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">false | cat</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">), nor will it affect some command substitutions (such as the </span><span style="color: #00931e; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">false</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> in </span><span style="color: #00931e; font-family: "Roboto Mono", monospace; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">export FOO="$(false)"</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">). Even worse, its behavior inside a function depends on how that function is called.</span></span></li></ul><ul style="text-align: left;"><li><span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span id="docs-internal-guid-e382aed6-7fff-3e7e-9697-3e44576cee58"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Many things run in subshells</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">, which can (often unexpectedly) hide changes to variables from the main shell. It can also make manual error handling harder, compounding the issue above:</span></span></span></span></li></ul></div><div align="left" dir="ltr" style="margin-left: 17.25pt;"><table style="border-collapse: collapse; border: none; white-space: nowrap;"><colgroup><col width="716"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">$ run_or_exit() { "$@" || exit $?; } </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"># Executes the arguments then exits on failure.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">$ foo="$(run_or_exit false)" </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"># </span><span style="background-color: transparent; color: #1155cc; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Exits the $() subshell, but the script continues.</span></p></td></tr></tbody></table></div></span><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><br /></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div></div>
</div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-74108617423070866982023-10-04T05:41:00.002-07:002023-10-04T05:47:15.898-07:00The Secret to Great Code Reviews: Respect Reviewers' Comments<style>
@media only screen and (max-width: 600px) {
.body {
overflow-x: auto;
}
}
</style>
<div class="body"><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1S8PoWYwLXXAka_lXtuQ0AgfP_Z7LbHyEyid7Pr4igjc/edit">printer-friendly version</a> to display in your office.</i></span></span><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span><div><span style="font-size: 17.3333px; white-space-collapse: preserve;">By Marius Latinis</span></div><div style="font-size: 17.3333px; font-style: italic; white-space-collapse: preserve;"><br /></div><div style="font-size: 17.3333px; font-style: italic; white-space-collapse: preserve;"><span id="docs-internal-guid-3298be58-7fff-3ffa-a65f-5a9ea4a945b3"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">You prepared a code change and asked for a review. A reviewer left a comment you disagree with. Are you going to reply that you will not address the comment? </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="color: #980000; font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">When addressing comments for your code reviewed by colleagues, find a solution that makes both you and the reviewer happy</span><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">. The fact that a reviewer left a comment suggests you may be able to improve the code further. Here are two effective ways to respond:</span></p><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">When it’s easy for you to make an improvement,</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;"> </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">update the code</span><span style="color: #38761d; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">. </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Improved code benefits future readers and maintainers. You will also avoid a potentially long and emotional debate with a reviewer.</span></p></li></ul><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">If the comment is unclear,</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">ask the reviewer to explain</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">.</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;"> </span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">To facilitate the process, talk directly with the reviewer through chat, or in person.</span></p></li></ul><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline;">Let’s demonstrate with an example code review scenario:</span></p><ol style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">You prepare a code change that modifies the following function:</span></p><div align="left" dir="ltr" style="margin-left: -0.75pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="344"></col><col width="388"></col></colgroup><tbody><tr style="height: 11.25pt;"><td style="background-color: #f3f3f3; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">3</span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> // Return the post with the most upvotes.</span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">3</span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> // Return the post with the most upvotes.</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #efefef; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #efefef; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">4</span><span style="background-color: #a0ffa0; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> // Restrict to English if englishOnly = true.</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f3f3f3; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">4</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Post findMostUpvotedPost(</span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">5</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Post findMostUpvotedPost(</span></p></td></tr><tr style="height: 12.75pt;"><td style="background-color: #f3f3f3; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">5</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> List<Post> posts) {</span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">6</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> List<Post> posts,</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #efefef; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: #d9d9d9; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">7</span><span style="background-color: #a0ffa0; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> boolean englishOnly) {</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f3f3f3; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">6</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> ... </span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">8</span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">...</span><span style="background-color: #a0ffa0; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> // Old and new logic mixed together.</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #f3f3f3; border-right: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">7</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p></td><td style="background-color: #f3f3f3; border-left: 0.5pt solid rgb(0, 0, 0); overflow-wrap: break-word; overflow: hidden; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: #d9d9d9; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">9</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p></td></tr></tbody></table></div><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"><span id="docs-internal-guid-bad1faca-7fff-21f6-c38b-85f3d8d46225"></span>
</span></p></li></ol><div align="left" dir="ltr" style="margin-left: -0.75pt;"><br /></div><ol start="2" style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">The code reviewer leaves the following comment:
</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="132"></col><col width="590"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #fff2cc; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 9pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">alertreviewer</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: #666666; font-family: 'Roboto Mono',monospace; font-size: 9pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">11:51 AM</span></p></td><td style="background-color: #fff2cc; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 9pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">The new function signature is too complex. Can we keep the signature unchanged?</span></p></td></tr><tr style="height: 0pt;"><td style="background-color: #fff2cc; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><br /></p></td><td style="background-color: #fff2cc; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 9pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Reply </span></p></td></tr></tbody></table></div><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"><span id="docs-internal-guid-709c419f-7fff-d5cb-574a-15a2dcf282eb"></span>
</span></p></li></ol><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">You disagree with the comment that one additional parameter makes the signature too complex. Nevertheless, do not reject the suggestion outright.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">There is another issue that might have prompted the comment: it is not the responsibility of this function to check the post’s language (</span><a href="https://en.wikipedia.org/wiki/Single-responsibility_principle" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline;">https://en.wikipedia.org/wiki/Single-responsibility_principle</span></a><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;">).</span></p><ol start="3" style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; margin-left: -18pt; text-wrap: nowrap; vertical-align: baseline;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">You rewrite your code to address the reviewer’s comment:</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 554.4000000000001pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 25.415999999999997pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-top: solid #b7b7b7 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt 2.88pt 2.88pt 2.88pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; white-space: pre;">ImmutableList<Post> englishPosts = selectEnglishPosts(posts); </span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; white-space: pre;">// Your new logic.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; white-space: pre;">Post mostUpvotedEnglishPost = findMostUpvotedPost(englishPosts); </span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; white-space: pre;">// No change needed.</span></p></td></tr></tbody></table></div></li></ol><div align="left" dir="ltr" style="margin-left: 0pt;"><span style="font-size: 13pt; font-style: normal;">
Now the code is improved, and both you and the reviewer are happy.</span></div><div><span style="font-size: 13pt; font-style: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><br /></span></div></span></div></span></span></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-30449906791703798902023-09-21T05:28:00.001-07:002023-09-21T05:28:47.922-07:00Communicate Design Tradeoffs Visually<span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1dNLtVwh4LivN0oB_MR4DSEufzz3s4EMDpBaaxNLT3qA/edit?resourcekey=0-qZq1Fhy6504oIICFtPhITA">printer-friendly version</a> to display in your office.</i></span></span><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div><div><span style="font-family: "Times New Roman", serif;"><span><div><span style="font-size: 17.3333px; white-space-collapse: preserve;">By Tim Lyakhovetskiy</span></div><div><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></div><div><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></div><div><span id="docs-internal-guid-431060ee-7fff-5938-2456-61a392368561"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">A goal of any written design or project proposal is to present and evaluate alternatives. However, </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">documents that include multiple solutions can be difficult to read when the qualities of each solution are not clearly expressed</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">A common approach to simplifying proposals is to use “pros and cons” for each alternative, but this leads to biased writing since the pros and cons may be weighed differently depending on the reader’s priorities.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">In this example, </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">can you quickly tell how this option would measure up against others?</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="733"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: white; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5.04pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face=""Google Sans Text", sans-serif" style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Option 1 - Optimize Shoelace Untangling Wizard in ShoeApp UI</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face=""Google Sans Text", sans-serif" style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Pros</span></p><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Google Sans Text", sans-serif; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Shoelace Untangling Wizard UI will use 10% less CPU</span></p></li><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Google Sans Text", sans-serif; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Less than one quarter to implement</span></p></li><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Google Sans Text", sans-serif; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Users will see 100ms less UI lag</span></p></li></ul><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face=""Google Sans Text", sans-serif" style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Cons</span></p><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Google Sans Text", sans-serif; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Security risk (shoelace colors exposed) until ShoeAppBackend team fixes lacing API</span></p></li><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Google Sans Text", sans-serif; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">ShoeAppBackend will be blocked for 3 months</span></p></li><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Google Sans Text", sans-serif; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">User documentation for Shoelace Untangling Wizard UI has to change</span></p></li></ul></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 15pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">This format requires the reader to remember many details in order to evaluate which option they prefer. </span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Instead, express tradeoffs using </span><span style="color: #980000; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">impact on qualities</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">.</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> There are many common </span><a href="https://en.wikipedia.org/wiki/List_of_system_quality_attributes" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">quality attributes</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> including Performance, Security, Maintainability, Usability, Testability, Scalability, and Cost.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Use colors and symbols in a table (</span><span face=""Google Sans Text", sans-serif" style="background-color: #f4cccc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">➖</span><span style="background-color: #f4cccc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> negative</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">, </span><span face=""Google Sans Text", sans-serif" style="background-color: #fff2cc; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">~</span><span style="background-color: #fff2cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> somewhat negative</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">, </span><span face=""Google Sans Text", sans-serif" style="background-color: #d9ead3; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">➕</span><span style="background-color: #d9ead3; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"> positive</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">) to make it easy for readers to parse your ideas.</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> The symbols are needed for accessibility, e.g. color-blindness and screen readers.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span id="docs-internal-guid-bd0ff027-7fff-ecab-11f8-b4ac72229579"></span></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><div style="overflow-x: auto;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 554.4pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 149.25pt;"><td style="background-color: white; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5.04pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Option 1 - Optimize Shoelace Untangling Wizard in ShoeApp UI</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="120"></col><col width="605"></col></colgroup><tbody><tr style="height: 18.75pt;"><td rowspan="2" style="background-color: #efefef; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Usability</span></p></td><td style="background-color: #d9ead3; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">➕ Users will see 100ms less UI lag</span></p></td></tr><tr style="height: 18.75pt;"><td style="background-color: #fff2cc; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">~</span><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> User documentation for Shoelace Untangling Wizard UI has to change</span></p></td></tr><tr style="height: 19.4712pt;"><td style="background-color: #efefef; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Security</span></p></td><td style="background-color: #f4cccc; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">➖ Security risk (shoelace colors exposed) until ShoeAppBackend fixes lacing API</span></p></td></tr><tr style="height: 18.75pt;"><td style="background-color: #efefef; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Partner impact</span></p></td><td style="background-color: #f4cccc; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">➖ ShoeAppBackend will be blocked for 3 months</span></p></td></tr><tr style="height: 18.75pt;"><td style="background-color: #efefef; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Performance</span></p></td><td style="background-color: #d9ead3; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">➕ Shoelace Untangling Wizard UI will use 10% less CPU</span></p></td></tr><tr style="height: 18.75pt;"><td style="background-color: #efefef; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: middle;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">Schedule/Cost</span></p></td><td style="background-color: #d9ead3; border-bottom: dotted #000000 1pt; border-color: rgb(0, 0, 0); border-left: dotted #000000 1pt; border-right: dotted #000000 1pt; border-style: dotted; border-top: dotted #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span face="'Google Sans Text',sans-serif" style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">➕ Less than one quarter to implement</span></p></td></tr></tbody></table></div></td></tr></tbody></table></div></div></span></div></span></span></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span id="docs-internal-guid-2a6182d5-7fff-9d72-8a66-f22e8331bc63"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Notice that the content uses approximately the same space but communicates more visually. The benefit is even greater when there are many alternatives/attributes, as it’s possible to evaluate the whole option at a glance.</span></span></p><div style="font-size: 17.3333px; font-style: italic; white-space-collapse: preserve;"><br /></div><div><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i><br /></i></span></span></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-13577835963954918922023-09-13T07:51:00.009-07:002023-10-03T05:45:30.930-07:00 Else Nuances<p><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: "Times New Roman", serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1V82jYkIIx5rRMBXy0Kgqel5dhyNcEk5dvxVE1iRx_No/edit?resourcekey=0-yGevquySDIxfnBkeYaLLhA">printer-friendly version</a> to display in your office.</i></span></span></p><p><i style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;">
</i><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;">By Sam Lee and Stan Chan</span></p><p><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">
If your function exits early in an </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">if</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> statement, using or not using an </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> clause is equivalent in terms of </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">behavior</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">. However, the proper use of </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> clauses and guard clauses (lack of </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">) can help emphasize the </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">intent</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> of the code to the reader.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 13pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline;">Consider the following guidelines to help you structure your functions:</span></p><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="background-color: transparent; font-family: "Times New Roman", serif; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; margin-left: -18pt; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 10pt; margin-top: 10pt;"><span id="docs-internal-guid-9040e358-7fff-5413-4fa2-148394437062"><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">Use a </span><span style="color: #980000; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">guard clause</span><span style="color: #980000; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;"> to handle special cases upfront, so that the rest of the code can focus
on the core logic.</span><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> A guard clause checks a criterion and fails fast or returns early if it is not
met, which reduces nesting (see the </span><a href="https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-decoration-line: underline; text-decoration-skip-ink: none; text-wrap: wrap; vertical-align: baseline;">Reduce Nesting</span></a><span style="font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> article).</span></span></p></li></ul><div align="left" dir="ltr" style="margin-left: 0pt; overflow-x: auto;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="348"></col><col width="388"></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def parse_path(path: str) -> Path:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">if</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> not path:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> raise ValueError(“path is empty.”)</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">else</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"># Nested logic here.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> ...</span></p></td><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def parse_path(path: str) -> Path:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">if </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">not path:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> raise ValueError(“path is empty.”)</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"># No nesting needed for the valid case.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> ...</span></p></td></tr></tbody></table></div><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="background-color: transparent; font-style: normal; font-variant: normal; font-weight: 400; list-style-type: disc; margin-left: -13.5pt; text-decoration: none; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span id="docs-internal-guid-42c30279-7fff-969b-7261-686af307bbea"><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">Use </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">else</span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;"> if it is part of the core responsibility. </span><span style="color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Prefer to keep related conditional logic
syntactically grouped together in the same </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; font-weight: 700; text-wrap: wrap; vertical-align: baseline;">if...else</span><span style="color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"> structure if each branch is relevant to the
core responsibility of the function. Grouping logic in this way emphasizes the complementary
nature of each condition. The complementary nature is emphasized explicitly by the </span><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;"><span face="Consolas, sans-serif" style="color: #188038;"><span style="font-size: 12pt;">else</span>
</span></span></span><span style="color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">statement, instead of being inferred and relying on the resulting behavior of the prior </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">return
</span><span style="color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">statement.</span></p></li></ul><div align="left" dir="ltr" style="margin-left: 0pt; overflow-x: auto;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="351"></col><col width="382"></col></colgroup><tbody><tr style="height: 112.5pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def get_favicon(self) -> Icon:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">if</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.user.id is None:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.SIGNED_OUT</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">if</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.browser.incognito:
</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.INCOGNITO</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">if</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> not self.new_inbox_items:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.EMPTY;</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.HAS_ITEMS </span></p></td><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 3.6pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">def get_favicon(self) -> Icon:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">if</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.user.id is None:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.SIGNED_OUT</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">elif</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> self.browser.incognito:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.INCOGNITO</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">elif</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> not self.new_inbox_items:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.EMPTY</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">else</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return Icon.HAS_ITEMS</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: blue; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"># No trailing return is needed or allowed.</span></p></td></tr></tbody></table></div><p><span id="docs-internal-guid-dddf1822-7fff-0922-8128-b77974b690c1"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">When it’s idiomatic, use a </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">switch</span><span face="Arial, sans-serif" style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(or similar) statement instead of </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">if...else</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> statements. (</span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">switch</span><span face="Consolas, sans-serif" style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">/</span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">when</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> in Go/Kotlin can accept boolean conditions like </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">if...else</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">.)</span></span></p><p><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Not all scenarios will be clear-cut for which pattern to use; use your best judgment to choose between these two styles. A good rule of thumb is use a guard if it's a special case, use </span><span face="Consolas, sans-serif" style="color: #188038; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">else</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"> if its core logic. Following these guidelines can improve code understandability by emphasizing the connections between different logical branches.</span></p><div><br /></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-68175244593090341082023-09-06T05:29:00.002-07:002023-09-07T05:28:26.247-07:00Use Abstraction to Improve Function Readability<span id="docs-internal-guid-51fe1366-7fff-896d-c24e-f568cf3bb354"><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: Times New Roman, serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1-B7l4c0lj4bGO8k3vSMZV_0kJEI-CcU98tLi9ytZCqo/edit?resourcekey=0-RuAgv-QvZhjfIO0tP2rwAg">printer-friendly version</a> to display in your office.</i></span></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: "Times New Roman", serif; font-size: 17.3333px; white-space-collapse: preserve;">By Palak Bansal and Mark Manley</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><br /></span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-left: -4.5pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 13pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Which version of the </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">createPizza</span><span face="Arial,sans-serif" style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Times New Roman',serif; font-size: 13pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">function below is easier to understand?</span></p><div align="left" dir="ltr" style="margin-left: -4.5pt;"><div style="overflow-x: auto;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="355"></col><col width="400"></col></colgroup><tbody><tr style="height: 114pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func createPizza(order *Order) *Pizza { </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> := &Pizza{Base: order.Size,</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Sauce: order.Sauce,</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Cheese: “Mozzarella”}</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if order.kind == “Veg” {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Toppings = vegToppings</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> } else if order.kind == “Meat” {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Toppings = meatToppings</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> oven := oven.New()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if oven.Temp != cookingTemp { </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> for (oven.Temp < cookingTemp) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> time.Sleep(checkOvenInterval)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> oven.Temp = getOvenTemp(oven)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if !</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Baked {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> oven.Insert(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> time.Sleep(cookTime)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> oven.Remove(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Baked = true</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> box := box.New()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Boxed = box.PutIn(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Sliced = box.SlicePizza(order.Size)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Ready = box.Close()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p></td><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func createPizza(order *Order) *Pizza {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> := prepare(order)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> bake(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> box(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func prepare(order *Order) *Pizza {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> := &Pizza{Base: order.Size,</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Sauce: order.Sauce,</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> Cheese: “Mozzarella”}</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> addToppings(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">, order.kind)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> return </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p><br /><p dir="ltr" style="white-space:nowrap; line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func addToppings(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> *Pizza, kind string) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> if kind == “Veg” {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Toppings = vegToppings</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> } else if kind == “Meat” {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> </span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">.Toppings = meatToppings</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> }</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func bake(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> *Pizza) {</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> oven := oven.New()</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> heatOven(oven) </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> bakePizza(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">, oven)</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">}</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func heatOven(oven *Oven) { … }</span></p><p dir="ltr" style="white-space:nowrap; line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func bakePizza(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> *Pizza, oven *Oven) { … }</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;">func box(</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre;">pizza</span><span style="background-color: transparent; color: black; font-family: 'Roboto Mono',monospace; font-size: 10pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre;"> *Pizza) { … }</span></p></td></tr></tbody></table></div></div><p dir="ltr" style="-webkit-tap-highlight-color: transparent; color: rgba(0, 0, 0, 0.87); font-family: Roboto, RobotoDraft, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="-webkit-tap-highlight-color: transparent; color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;">You probably said the right-hand side is easier, but why? </span><span style="-webkit-tap-highlight-color: transparent; color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline;">The left side mixes together several levels of abstraction</span><span style="-webkit-tap-highlight-color: transparent; color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;">: low-level implementation details (e.g., how to heat the oven), intermediate-level functions (e.g., how to bake pizza), and high-level abstractions (e.g., preparing, baking, and boxing the pizza).</span></p><p dir="ltr" style="-webkit-tap-highlight-color: transparent; color: rgba(0, 0, 0, 0.87); font-family: Roboto, RobotoDraft, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="-webkit-tap-highlight-color: transparent; color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline;">The right side is easier to follow because the functions have a consistent level of abstraction, </span><span style="-webkit-tap-highlight-color: transparent; color: #990000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline;">providing a top-down narrative of the code’s logic. </span><span face="Consolas, sans-serif" style="-webkit-tap-highlight-color: transparent; color: black; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;">createPizza</span><span style="-webkit-tap-highlight-color: transparent; color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;"> is a high-level function that delegates the preparing, baking, and boxing steps to lower-level specialized functions with intuitive names. Those functions, in turn, delegate to their own lower-level specialized functions (e.g., </span><span face="Consolas, sans-serif" style="-webkit-tap-highlight-color: transparent; color: black; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;">heatOven</span><span style="-webkit-tap-highlight-color: transparent; color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;">) until they reach a function that handles implementation details without needing to call other functions.</span><span style="-webkit-tap-highlight-color: transparent; color: #990000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline;"> </span></p><p dir="ltr" style="-webkit-tap-highlight-color: transparent; color: rgba(0, 0, 0, 0.87); font-family: Roboto, RobotoDraft, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="-webkit-tap-highlight-color: transparent; color: #990000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline;">Avoid mixing different abstraction layers into a single function.</span><span style="-webkit-tap-highlight-color: transparent; color: black; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;"> </span><span style="-webkit-tap-highlight-color: transparent; color: #444746; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;">Nest functions at equal abstraction levels to provide a narrative. This self-documenting style is simpler to follow, debug, and reuse.</span></p><p dir="ltr" style="-webkit-tap-highlight-color: transparent; line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="-webkit-tap-highlight-color: transparent; color: #444746; font-family: Times New Roman, serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;"><i>You can learn more about this topic in the book <a href="https://www.oreilly.com/library/view/clean-code-a/9780136083238/">Clean Code</a> by Robert C. Martin. </i></span></p><p dir="ltr" style="-webkit-tap-highlight-color: transparent; color: rgba(0, 0, 0, 0.87); font-family: Roboto, RobotoDraft, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="-webkit-tap-highlight-color: transparent; color: #444746; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline;"><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span id="docs-internal-guid-f97562d6-7fff-e5cd-389f-e14e4a3719c6"></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-left: -4.5pt; margin-top: 10pt;"><br /></p></span>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-32626823972204459982023-08-22T11:40:00.027-07:002023-09-01T11:44:50.461-07:00Better Feedback Makes for Faster Reviews<span id="docs-internal-guid-51fe1366-7fff-896d-c24e-f568cf3bb354"><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: Times New Roman, serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><i>This is another post in our <a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html">Code Health</a> series. A version of this post originally appeared in Google bathrooms worldwide as a Google <a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> episode. </i></span><span style="font-family: Times New Roman, serif;"><span style="font-size: 17.3333px; white-space-collapse: preserve;"><i>You can download a <a href="https://docs.google.com/document/d/1uMls75-y_MGKrCECtc2HWKQBy_2QABVCvkJOd08Y7g8/edit?resourcekey=0-cjVFpgXAjhpCXGaxLoelyQ">printer-friendly version</a> to display in your office.</i></span></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: Times New Roman, serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: Times New Roman, serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">By Felipe Sodré and Adam Bender</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-family: Times New Roman, serif; font-size: 17.3333px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /><br />
<span style="font-size: 13pt;">Have you ever received a code review with a comment like this?</span>
</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="735"></col></colgroup><tbody><tr style="height: 45pt;"><td style="background-color: #f4cccc; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">server_is_alive = </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">InitializeServer</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">()</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="453"></col></colgroup><tbody><tr style="height: 30.75pt;"><td style="background-color: #fff2cc; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Comment:</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"><br /><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">This doesn’t make any sense. Why don't you use InitializeServerWithAFewExtraSteps() instead?</span></p></td></tr></tbody></table></div><br /></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">If so, it probably left you scratching your head. Why is the reviewer asking this question? Are they testing you? Did you make a big mistake? The problem is the comment is vague (and maybe even a bit rude). It also leaves out important context as to what the reviewer is thinking, making it difficult to respond to. </span><span style="color: #980000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Here are a few simple ways to ensure your review comments are effective, helpful, and clear.</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"> </span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"></p><p style="text-align: left;"></p><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li><span style="font-family: times;">Be kind! People are more receptive to feedback if you assume competence and treat them with respect. </span></li></ul><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li><span style="font-family: times;">Focus your comments on the code, <a href="https://google.github.io/eng-practices/review/reviewer/comments.html#courtesy">not the author</a>. Avoid statements with the word ‘you’ which can give the impression that you are judging the person and not the artifact.</span></li></ul><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li><span style="font-family: times;">Explain why you are making the comment. You may be aware of alternatives that are not obvious to the author, or they may be aware of additional constraints.</span></li></ul><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li><span style="font-family: times;"><a href="https://google.github.io/eng-practices/review/reviewer/comments.html#guidance">Express the tradeoffs</a> your suggestion entails and take a pragmatic approach to working with the author to achieve the right balance.</span></li></ul><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li><span style="font-family: times;">Approach your role as a guide, not a test grader. Balance giving direct guidance with leaving some degrees of freedom for the author.</span></li></ul><ul style="margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li><span style="font-family: times;">Consider <a href="https://google.github.io/eng-practices/review/reviewer/comments.html#label-comment-severity">marking low priority comments with severity</a> like <i>Nit</i>, <i>Optional</i>, or <i>FYI</i> to help the author prioritize the important ones. </span></li></ul><p></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">Some comments can be difficult to express concisely. If you have a rewrite in mind, like a subtle API usage or structural change (e.g., split this big change into smaller changes), consider providing an example. </span><span style="color: #990000; font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">If you find you are stuck after more than two rounds of feedback, consider moving to a more direct communication channel like chat or live call to discuss next steps.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">When done well, comments can make code reviews more efficient and collaborative:</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="733"></col></colgroup><tbody><tr style="height: 84.75pt;"><td style="background-color: #d9ead3; border-bottom: solid #b7b7b7 1pt; border-color: rgb(183, 183, 183); border-left: solid #b7b7b7 1pt; border-right: solid #b7b7b7 1pt; border-style: solid; border-top: solid #b7b7b7 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 2.88pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">server_is_alive = </span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">InitializeServer</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 10pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">()</span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="536"></col></colgroup><tbody><tr style="height: 38.25pt;"><td style="background-color: #fff2cc; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">Comment:</span><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;"><br /><br /></span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: "Roboto Mono", monospace; font-size: 9pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">InitializeServerWithAFewExtraSteps() seems to achieve the same result but with built-in logging and auditing (see http://short_link_here). Would that be a better option here?</span></p></td></tr></tbody></table></div><br /></td></tr></tbody></table></div><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">The goal of reviews of any kind is to get the best possible outcome for you </span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">and</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;"> your team. Software Engineering is a team effort, and by helping each other more effectively you will deliver better, together!</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">References</span><span style="font-family: "Times New Roman", serif; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">: </span><a href="https://testing.googleblog.com/2019/11/code-health-respectful-reviews-useful.html" style="text-decoration-line: none;"><span face="Arial, sans-serif" style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Code Health: Respectful Reviews == Useful Reviews</span></a><span face="Arial, sans-serif" style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space-collapse: preserve;">, </span><a href="https://google.github.io/eng-practices/review/reviewer/comments.html" style="text-decoration-line: none;"><span face="Arial, sans-serif" style="color: #1155cc; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space-collapse: preserve;">Google's guide on Code Review Comments</span></a></p><br /></span>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-8592520766190592412023-04-28T05:24:00.000-07:002023-04-28T05:24:19.086-07:00Sensenmann: Code Deletion at Scale<span id="docs-internal-guid-142c2bb7-7fff-15d5-f0ec-97f375da1f69"><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">By Phil Norman</span></h2><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Code at Scale</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">At Google, tens of thousands of software engineers contribute to a </span><a href="https://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">multi-billion-line mono-repository</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">. This repository, stored in a system called Piper, contains the source code of shared libraries, production services, experimental programs, diagnostic and debugging tools: basically anything that's code-related.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">This open approach can be very powerful. For example, if an engineer is unsure how to use a library, they can find examples just by searching. It also allows kind-hearted individuals to perform important updates across the whole repository, be that migrating to newer APIs, or following language developments such as Python 3, or Go generics.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Code, however, doesn't come for free: it's expensive to produce, but also costs real engineering time to maintain. Such maintenance cannot easily be skipped, at least if one wants to avoid larger costs later on.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">But what if there were less code to maintain? Are </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">all</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> those lines of code </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">really necessary</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">?</span></p><br /><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Deletion at Scale</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Any large project accumulates dead code: there's always some module that is no longer needed, or a program that was used during early development but hasn't been run in years. Indeed, entire projects are created, function for a time, and then stop being useful. Sometimes they are cleaned up, but cleanups require time and effort, and it's not always easy to justify the investment.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">However, while this dead code sits around undeleted, it's still incurring a cost: the automated testing system doesn't know it should stop running dead tests; people running large-scale cleanups aren't aware that there's no point migrating this code, as it is never run anyway.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">So what if we could clean up dead code automatically? That was exactly what people started thinking several years ago, during the Zürich Engineering Productivity team's annual hackathon. The Sensenmann project, named after the German word for the embodiment of Death, has been highly successful. It submits over 1000 deletion changelists per week, and has so far deleted nearly 5% of all C++ at Google.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Its goal is simple (at least, in principle): automatically identify dead code, and send code review requests ('changelists') to delete it.</span></p><br /><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">What to Delete?</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Google's build system, Blaze (the internal version of Bazel) helps us determine this: by representing dependencies between binary targets, libraries, tests, source files and more, in a consistent and accessible way, we're able to construct a dependency graph. This allows us to find libraries that are not linked into any binary, and propose their deletion.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">That's only a small part of the problem, though: what about all those binaries? All the one-shot data migration programs, and diagnostic tools for deprecated systems? If they don't get removed, all the libraries they depend on will be kept around too.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The only real way to know if programs are useful is to check whether they're being run, so for internal binaries (programs run in Google's data centres, or on employee workstations), a log entry is written when a program runs, recording the time and which specific binary it is. By aggregating this, we get a liveness signal for every binary used in Google. If a program hasn't been used for a long time, we try sending a deletion changelist.</span></p><br /><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">What Not to Delete?</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">There are, of course, exceptions: some program code is there simply to serve as an example of how to use an API; some programs run only in places we can't get a log signal from. There are many other exceptions too, where removing the code would be deleterious. For this reason, it's important to have a blocklisting system so that exceptions can be marked, and we can avoid bothering people with spurious changelists.</span></p><br /><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The Devel's in the Details</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Consider a simple case. We have two binaries, each depending on its own library, and also on a third, shared library. Drawing this (ignoring the source files and other dependencies), we find this kind of structure:</span></p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6NZ74dGP2gDg68meVBGL16z16Ksnv6J5m_MvB4A0he-HsgSdTjRqvCrPBXlQv6xIe-tPJRXGQc4WUbCewLDSs16K6VKfOUepOu6KYoK3vTlT-Td-uYPlD4tVfu8UX8tdPlTn8UQzAAN-ZuFeXEJjnJoDnJQ29csBzZVfmc3ALs9fZAb3nWBM/s942/8NZQt9H4fg2PgLu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="284" data-original-width="942" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6NZ74dGP2gDg68meVBGL16z16Ksnv6J5m_MvB4A0he-HsgSdTjRqvCrPBXlQv6xIe-tPJRXGQc4WUbCewLDSs16K6VKfOUepOu6KYoK3vTlT-Td-uYPlD4tVfu8UX8tdPlTn8UQzAAN-ZuFeXEJjnJoDnJQ29csBzZVfmc3ALs9fZAb3nWBM/w640-h192/8NZQt9H4fg2PgLu.png" width="640" /></a></div><span><br /></span></div><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">If we see that main1 is in active use, but main2 was last used over a year ago, we can propagate the liveness signal through the build tree, marking main1 as alive along with everything it depends upon. What is left can be removed; as main2 depends on lib2, we want to delete these two targets in the same change:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3fG03iC1f5RgevQ1Z_8FpaR9kWOhPElGX-NCtFBEENQQTNuTjfUfRB7o99VHSiTWKyr4qO0vpgOCt92h0EwbDaKL2Cr8Rh-5E0BLRZ3Xppcogx0Fl-m-ksFJs8Q-aJjxh2D4ZMKsmvOSvqixnqYlt8IuhXNYHJKuB46cltmfEddjQwkMixSY/s942/8HNt7xRfwFkSRby.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="436" data-original-width="942" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3fG03iC1f5RgevQ1Z_8FpaR9kWOhPElGX-NCtFBEENQQTNuTjfUfRB7o99VHSiTWKyr4qO0vpgOCt92h0EwbDaKL2Cr8Rh-5E0BLRZ3Xppcogx0Fl-m-ksFJs8Q-aJjxh2D4ZMKsmvOSvqixnqYlt8IuhXNYHJKuB46cltmfEddjQwkMixSY/w640-h296/8HNt7xRfwFkSRby.png" width="640" /></a></div><p></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">So far so good, but real production code has unit tests, whose build targets depend upon the libraries they test. This immediately makes the graph traversal a lot more complicated:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQUsmANI3BvDrUSOSsdoHLsZ2tT8-Dcr3gQ5KDjT14r6iMc5SdUSWZrkBsMZPiDIaUkqyoG-1EeHkohXtZ0m2vK0nDBqLJeSDpnlKvoNGFvLSCWJ7cVsNQ8MLRLmEN8Jp2uSZhtxwFbvFLzYVgtnqwolLQtsHxNtftPGINooYnNsDb5eTroGg/s1048/5kyNtRsmiNEhPiE.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="344" data-original-width="1048" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQUsmANI3BvDrUSOSsdoHLsZ2tT8-Dcr3gQ5KDjT14r6iMc5SdUSWZrkBsMZPiDIaUkqyoG-1EeHkohXtZ0m2vK0nDBqLJeSDpnlKvoNGFvLSCWJ7cVsNQ8MLRLmEN8Jp2uSZhtxwFbvFLzYVgtnqwolLQtsHxNtftPGINooYnNsDb5eTroGg/w640-h210/5kyNtRsmiNEhPiE.png" width="640" /></a></div><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The testing infrastructure is going to run </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">all</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> those tests, including lib2_test, despite lib2 never being executed 'for real'. This means we cannot use test runs as a 'liveness' signal: if we did, we'd consider lib2_test to be alive, which would keep lib2 around forever. We would only be able to clean up untested code, which would severely hamper our efforts.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">What we </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">really</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> want is for each test to share the fate of the library it is testing. We can do this by making the library and its test interdependent, thus creating loops in the graph:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8mGK-XvKQRVjHPrhj6tNfDw0ov1c-Ume7oqlDC39nGykckBPXuCyRzrdw71j0NMvbOZ6qNeA9kFSOtYXOFt55ABnbi-48vo-3iVYIRLiOX8eEZTyCpJdMQysJREtwC4c-HQ6pNMGaZHElmDIdx9sajRX_tGEJY1bEeCtNMzTtoMi3FJ6bv2A/s1048/4BZfEDvrma4zupp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="352" data-original-width="1048" height="214" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8mGK-XvKQRVjHPrhj6tNfDw0ov1c-Ume7oqlDC39nGykckBPXuCyRzrdw71j0NMvbOZ6qNeA9kFSOtYXOFt55ABnbi-48vo-3iVYIRLiOX8eEZTyCpJdMQysJREtwC4c-HQ6pNMGaZHElmDIdx9sajRX_tGEJY1bEeCtNMzTtoMi3FJ6bv2A/w640-h214/4BZfEDvrma4zupp.png" width="640" /></a></div><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">This turns each library and its test into a strongly connected component. We can use the same technique as before, marking the 'live' nodes and then hunting for collections of 'dead' nodes to be deleted, but this time using </span><a href="https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Tarjan's strongly connected components algorithm</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> to deal with the loops.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Simple, right? Well, yes, if it's easy to identify the relationships between tests and the libraries they're testing. Sadly, that is not always the case. In the examples above, there's a simple naming convention which allows us to match tests to libraries, but we can't rely on that heuristic in general.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Consider the following two cases:</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFFM3beVW8HOzjfxZvzysuIznl89D5Jdq8qouRRsX22SVnLYP61U19QZxNY96i0hPdvcs00plbp0iZuPt8prbUJF3YaeuIU49d1bZB2tzcGUCl-mgSUp2unDhGmrWj78DpxHOf4R1a15DoKf8zs3_D13EVVCqsQtG-HGZ25EbMi0UkPAz1w_Q/s1000/4ckByNQ5ibUEmVo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="226" data-original-width="1000" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFFM3beVW8HOzjfxZvzysuIznl89D5Jdq8qouRRsX22SVnLYP61U19QZxNY96i0hPdvcs00plbp0iZuPt8prbUJF3YaeuIU49d1bZB2tzcGUCl-mgSUp2unDhGmrWj78DpxHOf4R1a15DoKf8zs3_D13EVVCqsQtG-HGZ25EbMi0UkPAz1w_Q/w640-h145/4ckByNQ5ibUEmVo.png" width="640" /></a></div><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">On the left, we have an implementation of the LZW compression algorithm, as separate compressor and decompressor libraries. The test is actually testing </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">both</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> of them, to ensure data isn't corrupted after being compressed and then decompressed. On the right, we have a web_test that is testing our web server library; it uses a URL encoder library for support, but isn't actually testing the URL encoder itself. On the left, we want to consider the LZW test and both LZW libraries as one connected component, but on the right, we'd want to exclude the URL encoder and consider web_test and web_lib as the connected component.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Despite requiring different treatment, these two cases have identical structures. In practice, we can encourage engineers to mark libraries like url_encoder_lib as being 'test only' (ie. only for use to support unit testing), which can help in the web-test case; otherwise our current approach is to use the </span><a href="https://en.wikipedia.org/wiki/Edit_distance" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">edit distance</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> between test and library names to pick the most likely library to match to a given test. Being able to identify cases like the LZW example, with one test and two libraries, is likely to involve processing test coverage data, and has not yet been explored.</span></p><br /><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Focus on the User...</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">While the ultimate beneficiaries of dead code deletion are the software engineers themselves, many of whom appreciate the help in keeping their projects tidy, not everyone is happy to receive automated changelists trying to delete code they wrote. This is where the </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">social</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> engineering side of the project comes in, which is every bit as important as the software engineering.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Automatic code deletion is an alien concept to many engineers, and just as with the introduction of unit testing 20 years ago, many are resistent to it. It takes time and effort to change people's minds, along with a good deal of careful communication.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">There are three main parts to Sensenmann's communication strategy. Of primary importance are the change descriptions, as they are the first thing a reviewer will see. They must be concise, but must provide enough background for </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">all</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> reviewers to be able to make a judgement. This is a difficult balance to achieve: too short, and many people will fail to find the information they need; too long, and one ends up with a wall of text no one will bother to read. Well-labelled links to supporting documentation and FAQs can really help here.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The second part is the supporting documentation. Concise and clear wording is vital here, too, as is a good navigable structure. Different people will need different information: some need reassurance that in a source control system, deletions can be rolled back; some will need guidance in how best to deal with a bad change, for example by fixing a misuse of the build system. Through careful thought, and iterations of user feedback, the supporting documentation can become a useful resource.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The third part is dealing with user feedback. This can be the hardest part at times: feedback is more frequently negative than positive, and can require a cool head and a good deal of diplomacy at times. However, accepting such feedback is the best way to improve the system in general, make users happier, and thus avoid negative feedback in the future.</span></p><br /><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 10pt;"><span style="font-family: "Trebuchet MS"; font-size: 13pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Onwards and Upwards</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Automatically deleting code may sound like a strange idea: code is expensive to write, and is generally considered to be an asset. However, unused code costs time and effort, whether in maintaining it, or cleaning it up. Once a code base reaches a certain size, it starts to make real sense to invest engineering time in </span><span style="font-family: Arial; font-size: 11pt; font-style: italic; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">automating</span><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> the clean-up process. At Google's scale, it is estimated that automatic code deletion has paid for itself tens of times over, in saved maintenance costs.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">The implementation requires solutions to problems both technical and social in nature. While a lot of progress has been made in both these areas, they are not entirely solved yet. As improvements are made, though, the rate of acceptance of the deletions increases and automatic deletion becomes more and more impactful. This kind of investment will not make sense everywhere, but if you have a huge mono-repository, maybe it'd make sense for you too. At least at Google, reducing the C++ maintenance burden by 5% is a huge win.</span></p><div><span style="font-family: Arial; font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div></span><br />Markohttp://www.blogger.com/profile/16755629501705100354noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-85833283372910324882022-02-07T09:03:00.016-08:002024-01-08T09:22:55.755-08:00Code Health: Now You're Thinking With Functions<span style="background-color: white; color: #222222; font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space: pre-wrap;">This is another post in our </span><a href="https://testing.googleblog.com/2017/04/code-health-googles-internal-code.html" style="font-family: times, "times new roman", serif; text-decoration-line: none;"><span style="background-color: white; color: #1155cc; font-style: italic; vertical-align: baseline; white-space: pre-wrap;">Code Health</span></a><span style="background-color: white; color: #222222; font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space: pre-wrap;"> series. A version of this post originally appeared in Google bathrooms worldwide as a Google </span><a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html" style="font-family: times, "times new roman", serif; text-decoration-line: none;"><span style="background-color: white; color: #1155cc; font-style: italic; vertical-align: baseline; white-space: pre-wrap;">Testing on the Toilet</span></a><span style="background-color: white; color: #222222; font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space: pre-wrap;"> episode. You can download a </span><a href="https://docs.google.com/document/d/1AqF96_mlhZO0xyW8noG5PA0B6p8uLshLfkiupxM-NzY/edit" rel="nofollow" style="font-family: times, "times new roman", serif; text-decoration-line: none;"><span style="background-color: white; color: #1155cc; font-style: italic; vertical-align: baseline; white-space: pre-wrap;">printer-friendly version</span></a><span style="background-color: white; color: #222222; font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space: pre-wrap;"> to display in your office. </span><div><span style="background-color: white; color: #222222; font-family: times, "times new roman", serif; font-style: italic; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div><div><span style="color: #222222; font-family: times, times new roman, serif;"><span style="background-color: white; white-space: pre-wrap;">by </span><span style="white-space: pre-wrap;">Cathal Weakliam</span></span></div><div><br /></div><div><span style="font-family: times, times new roman, serif;"><span style="white-space: pre-wrap;"><span style="color: #222222;">Loops are the standard way to process collections like arrays and lists. However, some loops implement the same patterns repeatedly, leading to duplicate code. </span><a href="https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99"><i><b><span style="color: #2b00fe;">Higher-order functions</span></b></i></a><span style="color: #800180; font-weight: bold;">—functions that use other functions as inputs or outputs—can reduce duplication by providing a simpler way to express common operations with collections.</span></span></span></div><div><span style="font-family: times, times new roman, serif;"><span style="white-space: pre-wrap;"><span style="color: #800180;"><b><br /></b></span></span></span></div><div><span style="color: #222222; font-family: times, times new roman, serif;"><span style="white-space: pre-wrap;">Consider these two loops in JavaScript th</span></span><span style="color: #222222; font-family: times, "times new roman", serif; white-space: pre-wrap;">at decide if every object in an array meets a condition:</span></div><div><span style="color: #222222; font-family: times, times new roman, serif;"><span style="white-space: pre-wrap;"><br /></span></span></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table">
<tbody>
<tr>
<td style="background-color: #f4cccc; vertical-align: top; width: 50%;"><pre style="background-color: #f4cccc; border: 0px; color: black; margin-bottom: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;">let everyRequestValid = true;
for (const request of requests) {
if (!<span style="font-weight: bold;">isValid</span>(request)) {
everyRequestValid = false;
break;
}
}
if (everyRequestValid) {
<span style="font-style: italic;"> // do something</span>
}</pre>
</td>
<td style="background-color: #f4cccc; vertical-align: top; width: 50%;"><pre style="background-color: #f4cccc; border: 0px; color: black; margin-bottom: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;">let everyUserEligible = true;
for (const user of users) {
if (!<span style="font-weight: bold;">isEligible</span>(user)) {
everyUserEligible = false;
break;
}
}
if (everyUserEligible) {
<span style="font-style: italic;"> // do something</span>
}</pre>
</td>
</tr>
</tbody></table><br />
</div><div style="overflow-x: auto;"><span style="color: #800180; font-weight: bold;"><br /></span></div><div style="overflow-x: auto;"><span style="color: #800180; font-weight: bold;">The high similarity between the two loops violates the <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">Don’t Repeat Yourself </a>principle</span> and creates an unnecessary burden on readers and maintainers of the code.</div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;">To reduce the maintenance burden, use the <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">every</span> method to replace each loop with a single expression. (In other languages <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">every</span> may have a different name, like <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">all</span> or <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">allMatch</span>).</div><div style="overflow-x: auto;"><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table">
<tbody>
<tr>
<td style="background-color: #d9ead3; vertical-align: top; width: 50%;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin-bottom: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;">if (requests.<span style="font-weight: bold;">every(isValid)</span>) {
<span style="font-style: italic;"> // do something</span>
}</pre>
</td>
<td style="background-color: #d9ead3; vertical-align: top; width: 50%;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin-bottom: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;">if (users.<span style="font-weight: bold;">every(isEligible)</span>) {
<span style="font-style: italic;"> // do something</span>
}</pre>
</td>
</tr>
</tbody></table><br />
</div><div style="overflow-x: auto;"><div style="overflow-x: auto;"><span style="color: #800180; font-weight: bold;"><br /></span></div><div style="overflow-x: auto;"><span style="color: #800180; font-weight: bold;">Processing collections with higher-order functions has several benefits:</span></div><div><ul style="text-align: left;"><li>It significantly reduces duplication by abstracting away the common looping code.</li><li>The resulting code is much shorter and simpler, with less opportunity for bugs.</li><li>A reader can quickly see the intent of the code as it is not obscured behind low-level control flow.</li></ul><div>Two other common higher-order functions are <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">map</span> (apply a function to each element of a collection) and <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">filter</span> (select the elements of a collection that pass a predicate). While the exact syntax varies by language, here’s how they look in JavaScript (using an anonymous function as the argument):</div></div><div><br /></div></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table">
<tbody>
<tr>
<td style="background-color: #d9ead3; vertical-align: top; width: 50%;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin-bottom: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-style: italic;">// Double each value in `ints`</span>
const doubled = ints.<span style="font-weight: bold;">map(n => n * 2);</span></pre>
</td>
<td style="background-color: #d9ead3; vertical-align: top; width: 50%;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin-bottom: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-style: italic;">// Get only the even values in `ints`</span>
const evens = ints.<span style="font-weight: bold;">filter(n => n % 2 === 0);</span></pre>
</td>
</tr>
</tbody></table>
</div><div><br /></div><div><br /></div><div>Just don’t overdo it! <span style="color: #800180; font-weight: bold;">Don’t rewrite a loop with functions if it makes it harder to understand, or if it would be considered unidiomatic in your language</span>. (For example, in Python, generator expressions are equivalent to <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">map</span> and <span style="color: #0f9d58; font-family: "consolas" , "courier new" , "courier" , monospace;">filter</span>, and are preferred in some situations).</div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-43686728836363232582022-02-01T10:11:00.000-08:002022-02-01T10:11:01.110-08:00A Tale of Two Features<h2>By George Pirocanac</h2><div><br /></div><div><div>I have often been asked, “What is the most memorable bug that you have encountered in your testing career?” For me, it is hands down a bug that happened quite a few years ago. I was leading an Engineering Productivity team that supported Google App Engine. At that time App Engine was still in its early stages, and there were many challenges associated with testing rapidly evolving features. Our testing frameworks and processes were also evolving, so it was an exciting time to be on the team. </div></div><div><br /></div><div>What makes this bug so memorable is that I spent so much time developing a comprehensive suite of test scenarios, yet a failure occurred during such an obvious use case that it left me shaking my head and wondering how I had missed it. Even with many years of testing experience it can be very humbling to construct scenarios that adequately mirror what will happen in the field.</div><div><br /></div><div>I’ll try to provide enough background for the reader to play along and see if they can determine the anomalous scenario. As a further hint, the problem resulted from the interaction of two App Engine features, so I like calling this story <i>A Tale of Two Features.</i></div><div><br /></div><h2 style="text-align: left;">Feature 1 - Datastore Admin (backup, restore, delete)</h2><div><br /></div><div>Google App Engine was released 13 years ago as Google’s first Cloud product. It allowed users to build and deploy highly scalable web applications in the Cloud. To support this, it had its own scalable database called the Datastore. An administration console allowed users to manage the application and its Datastore through a web interface. Users wrote applications that consisted of request handlers that App Engine invoked according to the URL that was specified. The handlers could call App Engine services like Datastore through a remote procedure call (RPC) mechanism. Figure 1 illustrates this flow.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh0cVtb0BQOCfEOdYOLTEzlkK6wlHKjVPjCdIUxMjMR8xH7dG2zAClwF1rdSiYZKoH9ZODmhh46bvRoZNh1m6GnBRcdO5-bJEOOBJXrH5GObECmzIKNS6w0aladuki_mY3bM1WaDl7sdTrvvbYTsJ1cY5xhWO1idwxPAnN-UrTSYyALq8JnxQ=s736" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="224" data-original-width="736" height="194" src="https://blogger.googleusercontent.com/img/a/AVvXsEh0cVtb0BQOCfEOdYOLTEzlkK6wlHKjVPjCdIUxMjMR8xH7dG2zAClwF1rdSiYZKoH9ZODmhh46bvRoZNh1m6GnBRcdO5-bJEOOBJXrH5GObECmzIKNS6w0aladuki_mY3bM1WaDl7sdTrvvbYTsJ1cY5xhWO1idwxPAnN-UrTSYyALq8JnxQ=w640-h194" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The first feature in this <i>Tale of Two Features</i> resided in the administration console, providing the ability to back up, restore, and delete selected or all of an application’s entities in the Datastore. It was implemented in a clever way that incorporated it directly into the application, rather than as an independent utility. As part of the application it could freely operate on the Datastore and incur the same billing charges as other datastore operations within the application. When the feature was invoked, traffic would be sent to its handler and the application would process it. Figure 2 illustrates this request flow.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjqOeIzD2oUVRnK14nCw8j5vxEcBvN98tBirvh3sj_P3a2E29GoZTKeSvgl2ePALbtxdVRSZ2R3tIWwPL93U3ckniR0hkDQBmKyTXqfvTzOjpLcNCVlEfLSBGUfzMD2PX6a5q4zrI8hUbJbbmPWnz5HDlWSJXQ_1LK7Hy92zVGIkx7KRZuFQQ=s736" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="471" data-original-width="736" height="410" src="https://blogger.googleusercontent.com/img/a/AVvXsEjqOeIzD2oUVRnK14nCw8j5vxEcBvN98tBirvh3sj_P3a2E29GoZTKeSvgl2ePALbtxdVRSZ2R3tIWwPL93U3ckniR0hkDQBmKyTXqfvTzOjpLcNCVlEfLSBGUfzMD2PX6a5q4zrI8hUbJbbmPWnz5HDlWSJXQ_1LK7Hy92zVGIkx7KRZuFQQ=w640-h410" width="640" /></a></div><br /><div>By the time this memorable bug occurred, this Datastore administration feature was mature, well tested, and stable. No changes were being made to it.</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Feature 2 - Utilities for Migrating to the HR - Datastore</h2><div><br /></div><div>The second feature (or more accurately, set of features) came at least a year after the first feature was released and stable. It helped users migrate their applications to a new High Replication (HR) Datastore. The HR Datastore was more reliable than its predecessor, but using it meant creating a new application and copying over all the data from the old Datastore. To support such migrations, App Engine developers added two new features to the administration console. The first copied all the data from the Datastore of one application to another, and the second redirected all traffic from one application to another. The latter was particularly useful because it meant the new application would seamlessly receive the traffic after a migration. This set of features was written by another team, and we in Engineering Productivity supported them by creating processes for testing various Datastore migrations. The migration-support features were thoroughly tested and released to developers. Figure 3 illustrates the request flow of the redirection feature.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhR9Tw44yOC48vvAARE70a2JNLAB5EcYPimIFeuGBNnm4Qbjf5d7W4NzZ-s-j5BU-b0-u2cFX1q9AL0e-QNkVabq3CpiTLZzVSA7fjrYTjHXofw0WErosSUgI_XHygJbMRIyuoPno0b4JNzgrXPXSPz2xLhZqKPUy8gkISvOZS_mjrhAKsprQ=s735" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="417" data-original-width="735" height="364" src="https://blogger.googleusercontent.com/img/a/AVvXsEhR9Tw44yOC48vvAARE70a2JNLAB5EcYPimIFeuGBNnm4Qbjf5d7W4NzZ-s-j5BU-b0-u2cFX1q9AL0e-QNkVabq3CpiTLZzVSA7fjrYTjHXofw0WErosSUgI_XHygJbMRIyuoPno0b4JNzgrXPXSPz2xLhZqKPUy8gkISvOZS_mjrhAKsprQ=w640-h364" width="640" /></a></div><br /><div><br /></div><h2 style="text-align: left;">What Could Possibly Go Wrong?</h2><div><br /></div><div><div>So this was the situation when we released these utilities for migrating to the new Datastore. We were very confident that they worked, as we had tested migrations of many different types and sizes of Datastore entities. We had also tested that a migration could be interrupted without data loss. All checked out fine. I was confident that this new feature would work, yet soon after we released it, we started getting problem reports.</div><div><br /></div><div>If you have been playing along, now is the time to ask yourself, “What could possibly go wrong?” As an added hint, the problem reports claimed that all the data in the newly migrated application was disappearing.</div></div><div><br /></div><div><br /></div><div><h2 style="text-align: left;">What Did Go Wrong</h2></div><div><div>As mentioned above, developers began to report that data was disappearing from their newly migrated applications. It wasn’t at all common, yet of course it is most disconcerting when data “just disappears.” We had to investigate how this could occur. Our standard processes ensured that we had internal backups of the data, which were promptly restored. In parallel we tried to reproduce the problem, but we couldn’t—at least until we figured out what was happening. As I mentioned earlier, once we understood it, it was quite obvious, but that only made it all the more painful that we missed it.</div><div><br /></div><div>What was happening was that, after migrating and automatically redirecting traffic to the new application, a number of customers thought they still needed to delete the data from their old application, so they used the first Datastore admin feature to do that. As expected, the feature sent traffic to that application to delete the entities from the Datastore. But that traffic was now being automatically redirected to the new application, and voila—all the data that had been copied earlier was now deleted there. Since only a handful of developers tried to delete the data from their old applications, this explained why the problem occurred only rarely. Figure 4 illustrates this request flow.</div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjRpHqaAL8XEJd2_MiESEmXnj8sqqctGvogjgRsw9Ty40zwQbmhfA9CLeECNR3dSh5PxIkN1HC3AnydKx-ef6LzkcC3E6QrKi23Y5IqZPPB9t38DIQylqsW3o-VIVhFpi0OkeX5YxkMmWk0Etiy4Php_XQWMDDeG0ZPirNEFM6Sr1Mg9Y85xg=s735" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="586" data-original-width="735" height="510" src="https://blogger.googleusercontent.com/img/a/AVvXsEjRpHqaAL8XEJd2_MiESEmXnj8sqqctGvogjgRsw9Ty40zwQbmhfA9CLeECNR3dSh5PxIkN1HC3AnydKx-ef6LzkcC3E6QrKi23Y5IqZPPB9t38DIQylqsW3o-VIVhFpi0OkeX5YxkMmWk0Etiy4Php_XQWMDDeG0ZPirNEFM6Sr1Mg9Y85xg=w640-h510" width="640" /></a></div><br /><div><br /></div><div><div>Obvious, isn’t it, once you know what is happening.</div></div><div><br /></div><div><br /></div><div><h2 style="text-align: left;">Lessons Learned</h2></div><div><br /></div><div><div>This all occurred years ago, and App Engine is based on a far different and more robust framework today. Datastore migrations are but a memory from the past, yet this experience made a great impression on me.</div><div><br /></div><div>The most important thing I learned from this experience is that, while it is important to test features for their functionality, it’s also important to think of them as part of workflows. In performing our testing we exercised a very limited number of steps in the migration process workflow and omitted a very reasonable step at the end: trying to delete the data from the old application. Our focus was in testing the variability of contents in the Datastore rather than different steps in the migration process. It was this focus that kept our eyes away from the relatively obvious failure case.</div><div><br /></div><div>Another thing I learned was that this bug might have been caught if the developer of the first feature had been in the design review for the second set of migration features (particularly the feature that automatically redirects traffic). Unfortunately, that person had already joined a new team. A key step in reducing bugs can occur at the design stage if “what-if” questions are being asked.</div><div><br /></div><div>Finally, I was enormously impressed that we were able to recover so quickly. Protecting against data loss is one of the most important aspects of Cloud management, and being able to recover from mistakes is at least as important as trying to prevent them. I have the utmost respect for my coworkers in Site Reliability Engineering (SRE).</div></div><div><br /></div><div><br /></div><div><h2 style="text-align: left;">References</h2></div><div><br /></div><div><a href="https://cloud.google.com/appengine/docs/standard/python/an-overview-of-app-engine">An Overview of Google App Engine</a></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com0tag:blogger.com,1999:blog-15045980.post-27731989697902107902021-06-15T08:04:00.001-07:002021-07-22T08:29:55.484-07:00How Much Testing is Enough?<h2 style="text-align: left;">By George Pirocanac</h2><div><br /></div><div><div><br /></div><div>A familiar question every software developer and team grapples with is, “How much testing is enough to qualify a software release?” A lot depends on the type of software, its purpose, and its target audience. One would expect a far more rigorous approach to testing commercial search engine than a simple smartphone flashlight application. Yet no matter what the application, the question of how much testing is sufficient can be hard to answer in definitive terms. A better approach is to provide considerations or rules of thumb that can be used to define a qualification process and testing strategy best suited for the case at hand. The following tips provide a helpful rubric:</div></div><div><br /></div><div><br /><ul style="text-align: left;"><li>Document your process or strategy.</li><li>Have a solid base of unit tests.</li><li>Don’t skimp on integration testing.</li><li>Perform end-to-end testing for Critical User Journeys.</li><li>Understand and implement the other tiers of testing.</li><li>Understand your coverage of code and functionality.</li><li>Use feedback from the field to improve your process.</li></ul><div><br /></div></div><h2 style="text-align: left;">Document your process or strategy</h2><div><br /></div><div>If you are already testing your product, document the entire process. This is essential for being able to both repeat the test for a later release and to analyze it for further improvement. If this is your first release, it’s a good idea to have a written test plan or strategy. In fact, having a written test plan or strategy is something that should accompany any product design.</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Have a solid base of unit tests</h2><div><br /></div><div><br /></div><div><div>A great place to start is writing unit tests that accompany the code. Unit tests test the code as it is written at the functional unit level. Dependencies on external services are either mocked or faked. </div></div><div><br /></div><div>A <i>mock</i> has the same interface as the production dependency, but only checks that the object is used according to set expectations and/or returns test-controlled values, rather than having a full implementation of its normal functionality.</div><div><br /></div><div>A <i>fake</i>, on the other hand, is a shallow implementation of the dependency but should ideally have no dependencies of it’s own. Fakes provide a wider range of functionality than mocks and should be maintained by the team providing the production version of the dependency. That way, as the dependency evolves so does the fake and the unit-test writer can be confident that the fake mirrors the functionality of the production dependency.</div><div><br /></div><div>At many companies, including Google, there are best practices of requiring any code change to have corresponding unit test cases that pass. As the code base expands, having a body of such tests that is executed before code is submitted is an important part of catching bugs before they creep into the code base. This saves time later both in writing integration tests, debugging, and verifying fixes to existing code.</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Don’t skimp on integration testing</h2><div><br /></div><div><br /></div><div>As the codebase grows and reaches a point where numbers of functional units are available to test as a group, it’s time to have a solid base of integration tests. An integration test takes a small group of units, often only two units, and tests their behavior as a whole, verifying that they coherently work together.</div><div><br /></div><div>Often developers think that integration tests can be deprioritized or even skipped in favor of full end-to-end tests. After all, the latter really tests the product as the user would exercise it. Yet, having a comprehensive set of integration tests is just as important as having a solid unit-test base (see the earlier Google Blog article, <a href="https://testing.googleblog.com/2020/11/fixing-test-hourglass.html">Fixing a test hourglass</a>).</div><div><br /></div><div>The reason lies in the fact that integration tests have less dependencies than full end-to-end tests. As a result, integration tests, with smaller environments to bring up, will be faster and more reliable than the full end-to-end tests with their full set of dependencies (see the earlier Google Blog article, <a href="https://testing.googleblog.com/2020/12/test-flakiness-one-of-main-challenges.html">Test Flakiness - One of the Main Challenges of Automated Testing</a>).</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Perform end-to-end testing for Critical User Journeys</h2><div><br /></div><div><br /></div><div>The discussion thus far covers testing the product at its component level, first as individual components (unit-testing), then as groups of components and dependencies (integration testing). Now it’s time to test the product end to end as a user would use it. This is quite important because it’s not just independent features that should be tested but entire workflows incorporating a variety of features. At Google these workflows - the combination of a critical goal and the journey of tasks a user undertakes to achieve that goal - are called Critical User Journeys (CUJs). Understanding CUJs, documenting them, and then verifying them using end-to-end testing (hopefully in an automated fashion) completes the <a href="https://docs.google.com/presentation/d/15gNk21rjer3xo-b1ZqyQVGebOp_aPvHU3YH7YnOMxtE/edit#slide=id.g437663ce1_53_98">Testing Pyramid</a>.</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Understand and implement the other tiers of testing</h2><div><br /></div><div><br /></div><div>Unit, integration, and end-to-end testing address the functional level of your product. It is important to understand the other tiers of testing, including:</div><div><br /><ul style="text-align: left;"><li>Performance testing - Measuring the latency or throughput of your application or service.</li><li>Load and scalability testing - Testing your application or service under higher and higher load.</li><li>Fault-tolerance testing - Testing your application’s behavior as different dependencies either fail or go down entirely.</li><li>Security testing - Testing for known vulnerabilities in your service or application.</li><li>Accessibility testing - Making sure the product is accessible and usable for everyone, including people with a wide range of disabilities.</li><li>Localization testing - Making sure the product can be used in a particular language or region.</li><li>Globalization testing - Making sure the product can be used by people all over the world.</li><li>Privacy testing - Assessing and mitigating privacy risks in the product.</li><li>Usability testing - Testing for user friendliness.</li></ul><br /></div><div>Again, it is important to have these testing processes occur as early as possible in your review cycle. Smaller performance tests can detect regressions earlier and save debugging time during the end-to-end tests.</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Understand your coverage of code and functionality</h2><div><br /></div><div><br /></div><div>So far, the question of how much testing is enough, from a qualitative perspective, has been examined. Different types of tests were reviewed and the argument made that smaller and earlier is better than larger or later. Now the problem will be examined from a quantitative perspective, taking code coverage techniques into account.</div><div><br /></div><div>Wikipedia has a great article on <a href="https://en.wikipedia.org/wiki/Code_coverage">code coverage</a> that outlines and discusses different types of coverage, including statement, edge, branch, and condition coverage. There are several open source tools available for measuring coverage for most of the popular programming languages such as Java, C++, Go and Python. A partial list is included in the table below:</div><div><br /></div><div><br /></div><div><div><br /></div>
<style>
table.coverage-tools {
width: 100%;
border-collapse: collapse;
}
table.coverage-tools th,
table.coverage-tools td {
width: 50%;
border: 1px solid black;
}
table.coverage-tools td {
padding: 5px;
}
</style>
<table class="coverage-tools">
<tbody><tr>
<th>Language</th>
<th>Tool</th>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>Java
</td>
<td>JaCoCo
</td>
</tr>
<tr>
<td>Java
</td>
<td>JCov
</td>
</tr>
<tr>
<td>Java
</td>
<td>OpenClover
</td>
</tr>
<tr>
<td>Python
</td>
<td>Coverage.py
</td>
</tr>
<tr>
<td>C++
</td>
<td>Bullseye
</td>
</tr>
<tr>
<td>Go
</td>
<td>Built in coverage support (go -cover)
</td>
</tr>
</tbody></table>
<div>Table 1 - Open source coverage tools for different languages</div></div><div><br /></div><div><br /></div><div>Most of these tools provide a summary in percentage terms. For example, 80% code coverage means <i>about</i> 80% of the code is covered and <i>about</i> 20% of the code is uncovered. At the same time, It is important to understand that, just because you have coverage for a particular area of code, this code can still have bugs.</div><div><br /></div><div><br /></div><div><div>Another concept in coverage is called changelist coverage. Changelist coverage measures the coverage in changed or added lines. It is useful for teams that have accumulated technical debt and have low coverage in their entire codebase. These teams can institute a policy where an increase in their incremental coverage will lead to overall improvement.</div></div><div><br /></div><div><br /></div><div><div>So far the coverage discussion has centered around coverage of the code by tests (functions, lines, etc.). Another type of coverage is feature coverage or behavior coverage. For feature coverage, the emphasis is on identifying the committed features in a particular release and creating tests for their implementation. For behavior coverage, the emphasis is on identifying the CUJs and creating the appropriate tests to track them. Again, understanding your “uncovered” features and behaviors can be a useful metric in your understanding of the risks.</div></div><div><br /></div><div><br /></div><div><br /></div><h2 style="text-align: left;">Use feedback from the field to improve your process</h2><div><br /></div><div><br /></div><div>A very important part of understanding and improving your qualification process is the feedback received from the field once the software has been released. Having a process that tracks outages and bugs and other issues, in the form of action items to improve qualification, is critical for minimizing the risks of regressions in subsequent releases. Moreover, the action items should be such that they (1) emphasize filling the testing gap as early as possible in the qualification process and (2) address strategic issues such as the lack of testing of a particular type such as load or fault tolerance testing. And again, this is why it is important to document your qualification process so that you can reevaluate it in light of the data you obtain from the field.</div><div><br /></div><div><br /></div><h2 style="text-align: left;">Summary</h2><div><br /></div><div><br /></div><div>Creating a comprehensive qualification process and testing strategy to answer the question “How much testing is enough?” can be a complex task. Hopefully the tips given here can help you with this. In summary:</div><div><br /></div><div><ul style="text-align: left;"><li>Document your process or strategy.</li><li>Have a solid base of unit tests.</li><li>Don’t skimp on integration testing.</li><li>Perform end-to-end testing for Critical User Journeys.</li><li>Understand and implement the other tiers of testing.</li><li>Understand your coverage of code and functionality.</li><li>Use feedback from the field to improve your process.</li></ul><br /></div><div><br /></div><h2 style="text-align: left;">References</h2><div><ul style="text-align: left;"><li><a href="https://testing.googleblog.com/2020/11/fixing-test-hourglass.html">Fixing a test hourglass</a></li><li><a href="https://docs.google.com/presentation/d/15gNk21rjer3xo-b1ZqyQVGebOp_aPvHU3YH7YnOMxtE/edit#slide=id.g437663ce1_53_98">Testing Pyramid</a></li><li><a href="https://testing.googleblog.com/2016/06/the-inquiry-method-for-test-planning.html">The Inquiry Method for test Planning</a></li><li><a href="https://testing.googleblog.com/2020/12/test-flakiness-one-of-main-challenges.html">Test Flakiness - One of the Main Challenges of Automated Testing</a></li><li><a href="https://testing.googleblog.com/2013/07/testing-on-toilet-know-your-test-doubles.html">Testing on the Toilet: Know Your Test Doubles</a></li><li><a href="https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html">Testing on the Toilet: Fake Your Way to Better Tests</a></li></ul><br /></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com4tag:blogger.com,1999:blog-15045980.post-36478138666603156092021-04-12T08:16:00.001-07:002021-04-13T04:29:52.199-07:00Mutation Testing<style>
.code-reserved {
color: #9c27b0;
}
.code-method,
.code-attribute {
color: #3367d6;
}
.code-string {
color: #0f9d58;
}
.code-numerical {
color: #c53929;
}
.code-changed-added {
background-color: #d9ead3;
border: 1px solid green;
}
.code-changed-removed {
background-color: #f4cccc;
border: 1px solid red;
}
</style>
By Goran Petrovic<div><br /></div><h2 style="text-align: left;">History
</h2><div><br /></div><div>It’s been a long-standing tradition of my team to organize hackathons twice a year. In weeks prior to the hackathon, the team gathers and brainstorms ideas for projects, ranging from improving the testing infrastructure or an existing process, to trying out a wild idea they’ve had for some time. Just before the hackathon, the team rates the accumulated ideas on a coolness-impact scale: how much fun does a project sound vs. how impactful could it potentially be; while impact is important, for hackathons, fun is non-negotiable. Then, engineers who are excited to work on some of the proposed projects subscribe and form teams. It was no different in the cold winter of 2013, where among the plethora of cool and wild ideas, one was to prototype <b>Mutation testing.</b></div><div><br /></div><div><br /></div><div>For those who are not familiar with it, mutation testing is a method of evaluating test quality by injecting bugs into the code and seeing whether the tests detect the fault or not. The more injected bugs the tests catch, the better they are. Here’s an example:</div><div><br /></div><div><br /></div><div>Negating the <i>if</i> condition.</div><div><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">def</span> checkout(cart):
<span class="code-reserved">if <span class="code-changed-added">not</span></span> cart.items:
<span class="code-reserved">throw</span> <span class="code-method">Error</span>(<span class="code-string">"cart empty"</span>)
<span class="code-reserved">return</span> checkout_internal(cart)</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">def</span> checkout(cart):
<span class="code-reserved">if</span> cart.items:
<span class="code-reserved">throw</span> <span class="code-method">Error</span>(<span class="code-string">"cart empty"</span>)
<span class="code-reserved">return</span> checkout_internal(cart)</span></pre></td>
</tr>
</tbody></table><br />
</div>
If a test fails, we say it kills the mutant, and if no tests fail, we say that the mutant is alive.<div><br /></div><div><br /></div><div><div>By the end of the hackathon, mutagenesis was implemented for C++ and Python, and a prototype was born: a shell script that evaluates generated mutants in a diff (pull request) and textually reports live mutants in the files in the diff. A year passed with no work done on the project, before I started to work on it in my <a href="https://en.wikipedia.org/wiki/20%25_Project">20% time</a>. I had no idea what Mutation testing was at the time, so I researched and read papers on the topic, and collected lots of ideas on what I should focus on.</div></div><div><br /></div><div><br /></div><div><h2>From Prototype To Launch</h2></div><div><br /></div><div><div>I quickly realized that the hackathon crew did not calculate the Mutation score, the ratio of mutants detected by tests, which is a prominent metric in the research literature and the holy grail of evaluating test quality, but just enumerated live mutants. My first exposure to mutants was just running the tool on the mutagenesis code itself and trying to understand the report. I was immediately overwhelmed: after a long execution time, I was facing thousands of mutants in just a handful of files. I tried going through a couple, but after a few minutes I grew tired and moved on with my main project, which happened to be on Google Shopping. In the following months, I stayed away from my 20% project, but I kept thinking about it, bugging my colleagues and friends about the ideas I had to make mutation testing work for us. After many months of brainstorming and discussions, almost a year after the original hackathon project, I was ready to design the Mutation Testing Service.</div></div><div><br /></div><div><br /></div><div><div>I faced two big problems. First, I could force myself to go through lots of mutants, and maybe find a useful one that would prompt me to write a test case, but I could not force others, not even my teammates. Second, the vast majority of mutants were simply bad. Here are some examples:</div></div>
<div><br /></div><div><br /></div><div>Replacing division with subtraction, but in a logging statement.</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">log.<span class="code-method">Infof</span>(<span class="code-string">"Found %d (%.2f %%)!"</span>, e,
float64(e)*<span class="code-numerical">100.0</span> <span class="code-changed-removed">/</span> total)</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">log.<span class="code-method">Infof</span>(<span class="code-string">"Found %d (%.2f %%)!"</span>, e,
float64(e)*<span class="code-numerical">100.0</span> <span class="code-changed-added">-</span> total)</span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div>Appending number 1 to an error message. </div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-method">Error</span>.create(((key + <span class="code-string">" disabled"</span>)));</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-method">Error</span>.create(((key + <span class="code-string">" disabled"</span>) <span class="code-changed-added">+ <span class="code-numerical">1</span></span>));</span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div><div>Replacing <i>greater than with less than</i> when comparing length of a collection to zero.</div></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">showCart := len(cart.GetItems()) <span class="code-changed-removed">></span> <span class="code-numerical">0</span></span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">showCart := len(cart.GetItems()) <span class="code-changed-added"><</span> <span class="code-numerical">0</span></span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div><div>Replacing the idiomatic python check for whether the module is imported or executed as main.</div></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">if</span> (__name__ <span class="code-changed-removed">==</span> <span class="code-string">'__main__'</span>):</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">if</span> (__name__ <span class="code-changed-added">!=</span> <span class="code-string">'__main__'</span>):</span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div>Changing python’s string concatenation (+) to string multiplication (*).</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">message = (<span class="code-string">'id '</span> <span class="code-changed-removed">+</span> run_idx)</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">message = (<span class="code-string">'id '</span> <span class="code-changed-added">*</span> run_idx)</span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div>Changing a tuning parameter.</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">slo = (<span class="code-numerical">20</span> * time.<span class="code-attribute">Second</span>)</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">slo = (<span class="code-numerical">20</span> * time.<span class="code-attribute">Second</span>) <span class="code-changed-added">+ <span class="code-numerical">1</span></span></span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div>Changing a network timeout, but the network layer is mocked in the tests.</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">_TIMEOUT = (<span class="code-numerical">60</span> <span class="code-changed-removed">*</span> <span class="code-numerical">10</span>)</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">_TIMEOUT = (<span class="code-numerical">60</span> <span class="code-changed-added">/</span> <span class="code-numerical">10</span>)</span></pre></td>
</tr>
</tbody></table><br />
</div>
<div><br /></div><div><br /></div><div>Subtracting from -∞.</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">df = df.replace(
[numpy.inf, -numpy.inf],
numpy.nan
)</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">df = df.replace(
[numpy.inf, -numpy.inf <span class="code-changed-added">- <span class="code-numerical">1</span></span>],
numpy.nan
)</span></pre></td>
</tr>
</tbody></table><br />
</div><br /><div>Yes, the tests did not detect these mutants, but we would not want such tests anyway. Many of them would produce fragile, <a href="https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html">change-detector tests</a>. We later settled on calling them <b>unproductive mutants</b>: writing tests for those mutants would make the test suite worse, not better.</div><div><br /></div>
<div><br /></div><div><div>I realized that I needed to suppress these types of mutants: if I reported them, nobody would use mutation testing, myself included. Most of the mutants were not useful, and that is a waste of developer attention. The onus was on me to create a better tool. I set out to try various heuristics by looking at the report and suppressing mutants that I found unproductive. I encoded the heuristics in AST (<a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">Abstract Syntax Tree</a>) matching rules, and I dubbed the AST nodes which contained unproductive mutants as <b>arid nodes</b>. In the beginning, there were only a few rules in the system, but that was enough to make me feel confident that my colleagues would give it a try.</div></div><div><br /></div><div><br /></div><div><div>The other big issue was the sheer number of mutants. With five or more in a line, hundreds in a file, it was a challenge to display them, and even if I managed that, nobody would go through them anyway. I quickly realized that they shouldn’t: it took a lot of time for me to go through the mutants, and, while some pointed me to a hole in my test suite, most were useless, and many of them, especially ones in the same line, redundant. I did not need every possible combination of operators changed to tell me that my test for that condition was insufficient; one was just fine. That was my first decision on mutation testing: to report at most one mutant in a line. This was a quick and easy decision to make, because, if you’ve ever used a Code review system, you know that having more makes the review noisy and harder to do. Another reason why it was such an easy decision was that it would have been computationally prohibitively expensive to calculate all mutants, and I could have thrown my 20% project down the drain. I call it limitation-driven development :)</div></div><div><br /></div><div><br /></div><div>Of course, the idea was to report live mutants during <a href="https://research.google/pubs/pub47025/">Code review</a>. Code review is the perfect time in the engineering process to surface useful findings about the code being changed, and integrating into the existing developer process has the <a href="https://research.google/pubs/pub43477/">highest chance</a> that the developers will take action. This seemed like the most normal thing in the world: we had hundreds of <a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43322.pdf">analyzers</a> and all engineers were used to receiving findings from various analyses of their code. It took an outsider to point out that this was a strange approach: mutation testing was classically run on the whole program and the mutation score calculated and used as guidance.</div><div><br /></div><div><br /></div><div>This is what a Mutant finding looks like in the Code review tool:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFQqkssBFxzWfHfdT51aECi83loojgVOQ-Xh3sTPS9qkkJLHoifI3SuZ-98ftCx4i4gn8xZD7dIyM0b9bI3lJipRuUK2ZkCbihyUkRRK6EYjCZ98j4dpMyR93jUhw0_cGD_i4H/s607/Code+Review+Image+for+Mutation+Testing+Article.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="430" data-original-width="607" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFQqkssBFxzWfHfdT51aECi83loojgVOQ-Xh3sTPS9qkkJLHoifI3SuZ-98ftCx4i4gn8xZD7dIyM0b9bI3lJipRuUK2ZkCbihyUkRRK6EYjCZ98j4dpMyR93jUhw0_cGD_i4H/s16000/Code+Review+Image+for+Mutation+Testing+Article.jpg" /></a></div><div><br /></div><div><br /></div><div><br /></div>Mutation Testing at Google is a dynamic analyzer of code changes that surfaces mutants during Code review by posting code findings. In terms of infrastructure, it consists of three main parts: the <i>change listener</i>, the <i>analyzer</i>, and many <i>mutagenesis servers</i>, one for each language.<br /><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh816h-dDe5XtCUKDTyd4xzIkKP5Gl94y9oZ-viuEUD5aMWv1av60qQoRvGEninqYS23aVu7t1p4Za5jgtzdSp0tPKexoQronquB9NXY0itBZt8z3FUGTWeCYFlwu3f39baX3dr/s711/Block+Diagram+for+Mutation+Testing+Article.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="466" data-original-width="711" height="421" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh816h-dDe5XtCUKDTyd4xzIkKP5Gl94y9oZ-viuEUD5aMWv1av60qQoRvGEninqYS23aVu7t1p4Za5jgtzdSp0tPKexoQronquB9NXY0itBZt8z3FUGTWeCYFlwu3f39baX3dr/w640-h421/Block+Diagram+for+Mutation+Testing+Article.jpg" width="640" /></a></div><br /><div><br /></div><div><br /></div><div><div>Each event during the Code review is announced using a publisher-subscriber pattern, and any interested party can listen, and react, to these events. When a change is sent for Code review, many things happen: linters are run, automated tests are evaluated, coverage is calculated, and for the users of mutation testing, mutants are generated and evaluated. Listening on all events coming from the Code review system, the <i>listener</i> schedules a mutation run on the <i>analyzer</i>. </div></div><div><br /></div><div><br /></div><div>The first thing the analyzer does is get the code coverage results for the patch in question; from it, the analyzer can extrapolate which tests cover which lines of source code. This is a very useful piece of information, because running the minimum set of tests that can kill a mutant is crucial; if we just ran all tests that were linked in, or covered the project, that would be prohibitively computationally expensive</div><div><br /></div><div><br /></div><div><div>Next, for each covered line in each file in the patch, a <i>mutagenesis server</i> for the language in question is asked to produce a mutant. The mutagenesis server parses the file, traverses its AST, and applies the mutators in the requested order (as per mutation context), ignoring arid nodes, nodes in uncovered lines and in lines that are not affected by the proposed patch.</div></div><div><br /></div><div><br /></div><div><div>When the <i>analyzer</i> assembles all mutants, it patches them one by one to a version control context and then evaluates all the tests for each mutant in parallel. For mutants for which all tests pass, the <i>analyzer</i> surfaces a finding for the code author and reviewers, and is done for the time being.</div></div><div><br /></div><div><br /></div><div>Because the Code review is a laborious and dynamic process, with many rounds of comments from reviewers and many automated findings from hundreds of different analyzers, there can be many snapshots as the patch evolves: adoption of recommendations from reviewers or accepting proposed changes from linters yields many code states. Mutation testing first runs after coverage is available, and then it runs for each subsequent snapshot: developers like to see the fruits of their labor: when they write a test to kill a mutant, they want to see the mutant killed.</div><div><br /></div><div><br /></div><div>I launched Mutation testing for the Shopping Engineering Productivity team in late 2015. Around 15 of my colleagues were subjected to Mutant findings during their Code reviews, and it was a bumpy start. Each finding has two buttons: <b>Please fix</b> and <b>Not useful</b>, as you can see on the Code review screenshot above. A reviewer can instruct the code author to fix some finding (e.g. a ClangTidy finding might point out that an object is being unnecessarily copied and suggest using a reference instead, or a Mutant finding might point out that code is not well tested). The author and all reviewers can give feedback to the author of the finding/analyzer that their finding is not useful. This is a source of valuable information, and I made use of it. For each mutant that was deemed not useful, I’d check it out and see whether I could generalize from it and add a new rule to my arid node heuristics. Slowly, I collected several hundred heuristics, many of them generally applicable, but many also tied to internal stuff, like monitoring frameworks. More and more, I noticed that just marking nodes as arid and suppressing mutants in them was not enough on its own; a more powerful mechanism was required to reduce this noise even further. Take a look at these motivating examples:</div>
<div><br /></div><div>Changing the condition of an if statement, but the body is arid (a logging statement).</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">if</span> _, err := <span class="code-method">Del</span>(req); err <span class="code-changed-removed">!=</span> <span class="code-reserved">nil</span> {
log.<span class="code-method">Errorf</span>(<span class="code-string">"cleanup failed: %v”</span>, cerr)
}</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">if</span> _, err := c.<span class="code-method">Del</span>(req); err <span class="code-changed-added">==</span> <span class="code-reserved">nil</span> {
log.<span class="code-method">Errorf</span>(<span class="code-string">"cleanup failed: %v”</span>, cerr)
}</span></pre></td>
</tr>
</tbody></table><br />
</div>
<br /><div><br /></div><div><div>Similar pattern, but in C++:</div></div><div><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span class="code-reserved">if</span> (<span class="code-changed-added">!</span>(!status.ok())) {
LOG(WARNING) << <span class="code-string">"Updating dependency graph failed"</span> << status;
}</span></pre></td>
</tr>
</tbody></table><br />
</div><br /><div><div>I settled for a transitive rule: an AST node is arid if I say it’s arid, or if it’s a compound statement and all its body is also arid. This made sense in retrospect, but it took some looking at reported examples of unproductive mutants to crystalize. Because the logging statements are arid, the whole <i>if</i> statement’s body is arid, and hence, the if statement itself is arid, including its condition. </div></div><div><br /></div><div><br /></div><div><div>In the summer of 2015, my intern, Małgorzata Salawa, and I got mutagenesis implemented for C++, Go, Python, and Java, and having transitive arid node detection and surfacing at most a single mutant per line and 7 per file, we called it a v1.0 and launched. Mutation testing was always an opt-in service, and in the beginning had a few users (93 code reviews in Q1 of 2016), but over time it ramped up to 2,500 users in February 2017, to tens of thousands today. The early days were crucial to get the users’ feedback and extend the arid node heuristics ever further. In the beginning, the <i>Not Useful</i> rate was around 80%, and this was already with some heuristics and at most a single mutant per line. With time, I got it down to around 15%. I was always aware that getting the rate to 0% was impossible, because of the nature of the mutants: sometimes, the mutant would produce an equivalent behavior as the original, and there was no way to avoid that fully.</div></div><div><br /></div><div><br /></div>
<div>Changing cached lookup by removing the cache and always recalculating yields functionally equivalent code, undetectable by conventional testing.</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">func (s *Serv) calculate(<span class="code-reserved">in</span> int32) <span class="code-reserved">int</span> {<span class="code-changed-added">
<span class="code-reserved">if</span> val, ok := <span class="code-reserved">if</span> s.cache[<span class="code-reserved">in</span>] {
<span class="code-reserved">return</span> val
}</span>
val := s.calc(<span class="code-reserved">in</span>)
s.cache[<span class="code-reserved">in</span>] = val
<span class="code-reserved">return</span> val
}</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">func (s *Serv) calculate(<span class="code-reserved">in</span> int32) <span class="code-reserved">int</span> {<span class="code-changed-removed">
</span> val := s.calc(<span class="code-reserved">in</span>)
s.cache[<span class="code-reserved">in</span>] = val
<span class="code-reserved">return</span> val
}</span></pre></td>
</tr>
</tbody></table></div><br /><div><br /></div><div><br /></div><div><div>I was both surprised and happy that I could lower the <i>Not useful</i> rate to under 50%.</div></div><div><br /></div><div><br /></div><div><h2>Mutation Context</h2></div><div><br /></div><div>As time went by, I added support for more languages. In early 2017, I implemented support for JavaScript and TypeScript, and later in the year I added support for Dart. In 2018 I added support for <a href="https://github.com/google/zetasql">ZetaSQL</a>. And finally, in early 2020, I added support for Kotlin as it became more and more popular in the Android world.</div><div><br /></div><div><br /></div><div><div>I kept track of various stats for all mutants: their survival rates and <i>Please fix/Not useful</i> ratios. </div></div><div><br /></div><div><br /></div><div>The worst performing mutator was ABS(<i>Absolute Value Mutator</i>) that would replace an <i>expression</i> with <i>±abs(expression),</i> for example:</div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">absl::<span class="code-method">Minutes</span>(<span class="code-numerical">10</span>) - elapsed;</span></pre></td>
<td style="background-color: #e0e0e0; vertical-align: top; width: 607px;"><pre style="background-color: #e0e0e0; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">absl::<span class="code-method">Minutes</span>(<span class="code-changed-added">-abs</span>(<span class="code-numerical">10</span>)) - elapsed;</span></pre></td>
</tr>
</tbody></table><br />
</div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;">Looking at the examples, I had to agree. Because the feedback was predominantly negative for this mutator, I quickly completely disabled it for all languages.</div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><div style="overflow-x: auto;">I soon noticed that the SBR (<i>Statement Block Removal</i>) mutator, which deletes statements or whole blocks, is the most common one, and that made sense: while mutating a logical or arithmetic operator required the existence of such an operator in the code to be mutated, any line of code was eligible for deletion. Mutants generated by code deletion, though, did not have the best reported usefulness, or productivity. In fact, almost all other mutators generated more productive mutants than the SBR, and that got me thinking: not all code is the same; a condition within an <i>if</i> statement that contains a <i>return</i> statement is not the same as a condition in another location.</div><div><br /></div><div><br /></div><div><div>Out of this, an idea was born: context-based mutator selection. For each line, I would randomly shuffle mutator operators and pick one by one until one generated a mutant in that line. That was not ideal, because I knew that some operators worked better than others in some contexts. Rather than just randomly picking a mutant operator, I decided to pick the one most likely to generate a surviving mutant that is then most likely to be productive when reported, based on historical data. I had millions of mutants to learn from, I just needed to define the distance between pieces of code. Finally deciding to look for AST nodes that were in similar contexts as the node being mutated, I looked at the nodes around the node under mutation, and encoded the child-parent relationships of the near-by nodes to capture the AST context. Armed with the distance measure and with the help of my returning intern Małgorzata, it was easy to find the closest AST contexts from historic mutants and to look at their fate and pick the best one. I ordered the mutators by their productivity and tried to generate a mutant in the node, in that order, since it’s quite possible that some of the mutant operators are not applicable on some piece of code.</div><div><br /></div></div><div><br /></div><div>This was quite an improvement. Both mutant survivability and usefulness improved significantly for all mutant operators and programming languages. You can read more about the findings in <a href="https://arxiv.org/pdf/2102.11378.pdf">the upcoming paper</a>.</div><div><br /></div><div><br /></div><div><div><h2>Fault Coupling</h2></div><div><br /></div></div><div><br /></div><div>Mutation testing is only valuable if the test cases we write for mutants are valuable. <a href="https://ieeexplore.ieee.org/document/6982626" rel="nofollow" target="_blank">Mutants do not resemble real bugs</a>, they are simpler than bugs found in the wild. Mutation testing relies on the coupling hypothesis: <a href="https://dl.acm.org/doi/abs/10.1145/2635868.2635929" rel="nofollow" target="_blank">mutants are coupled with real bugs</a> if a test suite that is sensitive enough to detect mutants is also sensitive enough to detect the more complex real bugs. Reporting mutants and writing tests to kill them only makes sense if they are coupled with real bugs.</div><div><br /></div><div><br /></div><div><br /></div><div><div>I instinctively thought that fault coupling was real, otherwise I would not have worked on mutation testing at all, and I’ve seen many many cases where mutants pointed to a bug; but still, I wanted to verify this hypothesis, if only for our code base. I designed an experiment: I would generate all mutants in a line for explicit bug-fixing changes, before and after the fix, and I would check whether, had mutation testing been run, it would have surfaced a mutant in the change that introduced the bug, and potentially prevented it (i.e., killed in the change that fixed the bug and added new tests cases). I ran this experiment on weekends for over a month, because we did not have the resources to run it during workdays. While I normally generate a single mutant in a line, to test the fault coupling effect, I used the classical mutation testing approach and generated all possible mutants, while still adhering to the arid node suppression. A total of 33 million test suites were executed to test hundreds of thousands of mutants, finally to conclude that, in around 70% of cases, a bug was coupled with a mutant. </div></div><div><br /></div><div><br /></div><div>While I was at it, I also checked my intuition on whether a single mutant per line was enough, and found that it was overwhelmingly so: in more than 90% of cases, either all mutants were killed in a line or none was. It’s worth keeping in mind that I still applied my arid node suppression heuristics for this experiment. It was great to finally have confirmation of my intuitions.</div><div><br /></div><div><br /></div><div>I also looked into the developer behavior changes after using mutation testing on a project for longer periods of time, and discovered that projects that use mutation testing get more tests over time, as developers get exposed to more and more mutants. Not only do developers write more test cases, but those test cases are more effective in killing mutants: less and less mutants get reported over time. I noticed this from personal experience too: when writing unit tests, I would see where I cut some corners in the tests, and anticipated the mutant. Now I just add the missing test cases, rather than facing a mutant in my Code review, and I rarely see mutants these days, as I’ve learned to anticipate and preempt them.</div><div><br /></div><div><br /></div><div>You can read more about the findings in our <a href="https://homes.cs.washington.edu/~rjust/publ/mutation_testing_practices_icse_2021.pdf">ICSE paper</a>.</div><div><br /></div><div><br /></div><div><h2>Conclusion</h2></div><div><br /></div><div><br /></div><div><div>It’s been a long road since that hackathon in the winter of 2013. Mutation testing was a lot of fun to work on. It had its challenges, and I had days where I thought that I would throw everything down the drain (I’m looking at you, clang), but I am glad I stuck with it.</div></div><div><br /></div><div><br /></div><div><br /></div><div><div>The most interesting part of the project was getting Mutation testing to scale to such a large code base, and that required redefining the problem and adapting it to the existing ecosystem that engineers were already used to. Another interesting angle was working, and learning from, the academic community, in particular Gordon Fraser (University of Passau) and René Just (University of Washington).</div></div><div><br /></div><div><br /></div><div><br /></div><div>I would like to encourage everyone to give one of the many open source mutation testing tools a try on their projects. With some tweaks here and there, it can be a great way to keep your software well tested.</div><div><br /></div><div><br /></div><div><br /></div></div>
Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com3tag:blogger.com,1999:blog-15045980.post-88599942937597899162021-03-24T10:05:00.001-07:002021-03-24T10:05:30.110-07:00Test Flakiness - One of the main challenges of automated testing (Part II)By George Pirocanac<div><br /></div><div>This is part two of a series on test flakiness. The <a href="https://testing.googleblog.com/2020/12/test-flakiness-one-of-main-challenges.html">first article</a> discussed the four components under which tests are run and the possible reasons for test flakiness. This article will discuss the triage tips and remedies for flakiness for each of these possible reasons.</div><h3 style="text-align: left;"><br /></h3><h3 style="text-align: left;">Components</h3><div><br /></div><div>To review, the four components where flakiness can occur include:</div><div><ul style="text-align: left;"><li>The tests themselves</li><li>The test-running framework</li><li>The application or system under test (SUT) and the services and libraries that the SUT and testing framework depend upon</li><li>The OS and hardware and network that the SUT and testing framework depend upon</li></ul><div><br /></div></div><div>This was captured and summarized in the following diagram.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUnN8lpHRkVYQ3G5ycEcr1HDvMHUAiCsr7nHqYYSIX0_U7A78wjhZrP5CnZLB6eqyjJk_46swSKkL835e_QtWuDZJ_65cL7mn6a_58Bes5Q_Z1_DmNZ7egieAUmlf0zu-FeYNp/s720/Copy+of+Copy+of+Test+Flakiness+Article+II+for+External+Testing+Blog+%25281%2529.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="472" data-original-width="720" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUnN8lpHRkVYQ3G5ycEcr1HDvMHUAiCsr7nHqYYSIX0_U7A78wjhZrP5CnZLB6eqyjJk_46swSKkL835e_QtWuDZJ_65cL7mn6a_58Bes5Q_Z1_DmNZ7egieAUmlf0zu-FeYNp/w608-h400/Copy+of+Copy+of+Test+Flakiness+Article+II+for+External+Testing+Blog+%25281%2529.jpg" width="608" /></a></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">The reasons, triage tips, and remedies for flakiness are discussed below, by component.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><h3 style="clear: both; text-align: left;">The tests themselves</h3><div><br /></div><div>The tests themselves can introduce flakiness. This can include test data, test workflows, initial setup of test prerequisites, and initial state of other dependencies.</div><div><br /></div><div><span id="docs-internal-guid-cc3386a8-7fff-514b-d1ff-2a7b704dddcd"><br /><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Reason for Flakiness</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Tips for Triaging</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Type of Remedy</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Improper initialization or cleanup.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Look for compiler warnings about uninitialized variables. Inspect initialization and cleanup code. Check that the environment is set up and torn down correctly. Verify that test data is correct.</span></p><br /></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Explicitly initialize all variables with proper values before their use.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Properly set up and tear down the testing environment. Consider an initial test that verifies the state of the environment.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Invalid assumptions about the state of test data.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Rerun test(s) independently.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Make tests independent of any state from other tests and previous runs.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Invalid assumptions about the state of the system, such as the system time.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Explicitly check for system dependency assumptions.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Remove or isolate the SUT dependencies on aspects of the environment that you do not control.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dependencies on execution time, expecting asynchronous events to occur in a specific order, waiting without timeouts, or race conditions between the tests and the application.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Log the times when accesses to the application are made.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">As part of debugging, introduce delays in the application to check for differences in test results.</span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /><br /></span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Add synchronization elements to the tests so that they wait for specific application states. Disable unnecessary caching to have a predictable timeline for the application responses.</span></p><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Note: </span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Do</span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">NOT </span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">add arbitrary delays as these can become flaky again over time and slow down the test unnecessarily.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Dependencies on the order in which the tests are run. (Similar to the second case above.)</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Rerun test(s) independently.</span></p></td><td style="border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Make tests independent of each other and of any state from previous runs.</span></p></td></tr></tbody></table></div></span></div>
<div><i><span style="font-size: x-small;"><br /></span></i></div><div><i><span style="font-size: x-small;">Table 1 - Reasons, triage tips, and remedies for flakiness in the tests themselves</span></i></div><div><div><br /></div><h3 style="text-align: left;">The test-running framework</h3></div><div><br /></div><div>An unreliable test-running framework can introduce flakiness. </div><div><br /></div><div><span id="docs-internal-guid-8512e193-7fff-e2f6-818d-6d28c9227926"><br /><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Reason for Flakiness</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Tips for Triaging</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Type of Remedy</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Failure to allocate enough resources for the SUT, thus preventing it from running.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Check logs to see if SUT came up.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Allocate sufficient resources.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Improper scheduling of the tests so they “collide” and cause each other to fail.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Explicitly run tests independently in different order.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Make tests runnable independently of each other.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Insufficient system resources to satisfy the test requirements. (Similar to the first case but here resources are consumed while running the workflow.)</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Check system logs to see if SUT ran out of resources.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Fix memory leaks or similar resource “bleeding.”</span></p><br /><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Allocate sufficient resources to run tests.</span></p></td></tr></tbody></table></div><br /></span></div><div><i><span style="font-size: x-small;">Table 2 - Reasons, triage tips, and remedies for flakiness in the test running framework</span></i></div><div><br /></div><div><br /></div><h3 style="text-align: left;">The application or SUT and the services and libraries that the SUT and testing framework depend upon</h3><div><div><br /></div><div>Of course, the application itself (or the SUT) could be the source of flakiness. </div><div>An application can also have numerous dependencies on other services, and each of those services can have their own dependencies. In this chain, each of the services can introduce flakiness. </div></div><div><br /></div><div><span id="docs-internal-guid-09658aab-7fff-21ca-2937-a430c1039703"><br /><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Reason for Flakiness</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Tips for Triaging</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Type of Remedy</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Race conditions.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Log accesses of shared resources.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Add synchronization elements to the tests so that they wait for specific application states. Note: </span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Do</span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">NOT </span><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">add arbitrary delays as these can become flaky again over time.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Uninitialized variables.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Look for compiler warnings about uninitialized variables.</span></p><br /></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Explicitly initialize all variables with proper values before their use.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Being slow to respond or being unresponsive to the stimuli from the tests.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Log the times when requests and responses are made.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Check and remove any causes for delays.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Memory leaks.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Look at memory consumption during test runs. Use tools such as Valgrind to detect.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Fix programming error causing memory leak. This </span><a href="https://en.wikipedia.org/wiki/Memory_leak" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Wikipedia article</span></a><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> has an excellent discussion on these types of errors.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Oversubscription of resources.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Check system logs to see if SUT ran out of resources.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Allocate sufficient resources to run tests.</span></p><br /></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Changes to the application (or dependent services) out of sync with the corresponding tests.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Examine revision history.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Institute a policy requiring code changes to be accompanied by tests.</span></p></td></tr></tbody></table></div><br /></span></div><div><span id="docs-internal-guid-ac8dffbb-7fff-2802-cd50-177a19301a76"><div align="left" dir="ltr" style="margin-left: 0pt;"><div align="left" dir="ltr" style="margin-left: 0pt;"><i><span style="font-size: x-small;">Table 3 - Reasons, triage tips, and remedies for flakiness in the application or SUT</span></i></div><div><br /></div><div><br /></div><div><h3>The OS and hardware that the SUT and testing framework depend upon</h3></div><div><br /></div><div>Finally, the underlying hardware and operating system can be sources of test flakiness. </div><div><i><span style="font-size: x-small;"><br /></span></i></div><div><span id="docs-internal-guid-0d6c36b5-7fff-566e-92ce-795efef74cfe"><br /><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Reason for Flakiness</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Tips for Triaging</span></p></td><td style="background-color: #cfe2f3; border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: transparent; font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Type of Remedy</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Networking failures or instability.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Check for hardware errors in system logs.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Fix hardware errors or run tests on different hardware.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Disk errors.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Check for hardware errors in system logs.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Fix hardware errors or run tests on different hardware.</span></p></td></tr><tr style="height: 0pt;"><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Resources being consumed by other tasks/services not related to the tests being run.</span></p><br /></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Examine system process activity.</span></p></td><td style="border-bottom: solid #000000 1pt; border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-top: solid #000000 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt 5pt 5pt 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Reduce activity of other processes on test system(s).</span></p></td></tr></tbody></table></div><br /></span></div><div><i><span style="font-size: x-small;">Table 4 - Reasons, triage tips, and remedies for flakiness in the OS and hardware of the SUT</span></i></div><div><br /></div><div><br /></div><h3>Conclusion</h3><div>As can be seen from the wide variety of failures, having low flakiness in automated testing can be quite a challenge. This article has outlined both the components under which tests are run and the types of flakiness that can occur, and thus can serve as a cheat sheet when triaging and fixing flaky tests.</div><div><br /></div><div><br /></div><div><h3>References</h3></div><div><ul><li><a href="https://testing.googleblog.com/2017/04/where-do-our-flaky-tests-come-from.html">Where do our flaky tests come from?</a></li><li><a href="https://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html">Flaky Tests at Google and How We Mitigate Them</a></li><li><a href="https://testing.googleblog.com/2009/06/my-selenium-tests-arent-stable.html">My Selenium Tests Aren't Stable!</a></li><li><a href="https://testing.googleblog.com/2008/04/tott-avoiding-flakey-tests.html">TotT: Avoiding Flakey Tests</a></li><li><a href="https://testing.googleblog.com/2020/12/test-flakiness-one-of-main-challenges.html">Test Flakiness - One of the main challenges of automated testing</a></li></ul></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div></div></span></div></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com3tag:blogger.com,1999:blog-15045980.post-70867358226678390512020-12-16T09:32:00.001-08:002020-12-16T09:32:43.777-08:00Test Flakiness - One of the main challenges of automated testingBy George Pirocanac<div><br /></div><div><br /></div><div>Dealing with test flakiness is a critical skill in testing because automated tests that do not provide a consistent signal will slow down the entire development process. If you haven’t encountered flaky tests, this article is a must-read as it first tries to systematically outline the causes for flaky tests. If you have encountered flaky tests, see how many fall into the areas listed.</div><div><br /></div><div><br /></div><div><div>A follow-up article will talk about dealing with each of the causes.</div></div><div><br /></div><div><br /></div><div>Over the years I’ve seen a lot of reasons for flaky tests, but rather than review them one by one, let’s group the sources of flakiness by the components under which tests are run:</div><div><ul style="text-align: left;"><li>The tests themselves</li><li>The test-running framework</li><li>The application or system under Test (SUT) and the services and libraries that the SUT and testing framework depend upon</li><li>The OS and hardware that the SUT and testing framework depend upon</li></ul><div><br /></div></div><div><div>This is illustrated below. Figure 1 first shows the hardware/software stack that supports an application or system under test. At the lowest level is the hardware. The next level up is the operating system followed by the libraries that provide an interface to the system. At the highest level, is the middleware, the layer that provides application specific interfaces.</div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL7-sd5koCPql8mJeq-5b4Vjo8Ex1nWHlihjQ2w-0AHXbIZLWb0cL9zCuqzkoV0RspcxQTtXE_JSmJHk6nStrW7NUxQ8lvHEzCBw5dchj1Jowg4EHYb9G6U-ZyiOGsQIE3rzW9/s460/Test+Flakiness+-+Figure+1+%25281%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="460" data-original-width="439" height="346" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL7-sd5koCPql8mJeq-5b4Vjo8Ex1nWHlihjQ2w-0AHXbIZLWb0cL9zCuqzkoV0RspcxQTtXE_JSmJHk6nStrW7NUxQ8lvHEzCBw5dchj1Jowg4EHYb9G6U-ZyiOGsQIE3rzW9/w330-h346/Test+Flakiness+-+Figure+1+%25281%2529.jpg" width="330" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /><br /></div><div>In a distributed system, however, each of the services of the application and the services it depends upon can reside on a different hardware / software stack as can the test running service. This is illustrated in Figure 2 as the full test running environment.</div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQLc8lZ3LethyhQkmibpBwkEqNMVvjznwN7b5MhDimx1AlC-QKtuvpfaGs6tUhuQJ715klFCvqzCjbT8juhTQT0ccn-eSfS2dCuekKvxK-bXBVv7w7PA2VCY1X0hfgAVbXn6k3/s726/Test+Flakiness+-+Figure+2+%25281%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="520" data-original-width="726" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQLc8lZ3LethyhQkmibpBwkEqNMVvjznwN7b5MhDimx1AlC-QKtuvpfaGs6tUhuQJ715klFCvqzCjbT8juhTQT0ccn-eSfS2dCuekKvxK-bXBVv7w7PA2VCY1X0hfgAVbXn6k3/w548-h392/Test+Flakiness+-+Figure+2+%25281%2529.jpg" width="548" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div><br /></div><div><div>As discussed above, each of these components is a potential area for flakiness.</div></div><div><br /></div><div><br /></div><div><h3 style="text-align: left;">The tests themselves</h3></div><div><br /></div><div><div>The tests themselves can introduce flakiness. Typical causes include:</div></div><div><ul style="text-align: left;"><li>Improper initialization or cleanup.</li><li>Invalid assumptions about the state of test data.</li><li>Invalid assumptions about the state of the system. An example can be the system time.</li><li>Dependencies on the timing of the application.</li><li>Dependencies on the order in which the tests are run. (Similar to the first case above.)</li></ul></div><div><br /></div><div><br /></div><h3 style="text-align: left;">The test-running framework</h3><div><br /></div><div>An unreliable test-running framework can introduce flakiness. Typical causes include:</div><div><br /></div><div><ul style="text-align: left;"><li>Failure to allocate enough resources for the system under test thus causing it to fail coming up. </li><li>Improper scheduling of the tests so they “collide” and cause each other to fail.</li><li>Insufficient system resources to satisfy the test requirements.</li></ul></div><div><br /></div><h3 style="text-align: left;">The application or system under test and the services and libraries that the SUT and testing framework depend upon</h3><div><br /></div><div><div>Of course, the application itself (or the system under test) could be the source of flakiness. An application can also have numerous dependencies on other services, and each of those services can have their own dependencies. In this chain, each of the services can introduce flakiness. Typical causes include:</div></div><div><ul style="text-align: left;"><li>Race conditions.</li><li>Uninitialized variables.</li><li>Being slow to respond or being unresponsive to the stimuli from the tests.</li><li>Memory leaks.</li><li>Oversubscription of resources.</li><li>Changes to the application (or dependent services) happening at a different pace than those to the corresponding tests.</li></ul></div><div><br /></div><div>Testing environments are called <i><a href="https://testing.googleblog.com/2012/10/hermetic-servers.html">hermetic</a></i> when they contain everything that is needed to run the tests (i.e. no external dependencies like servers running in production). Hermetic environments, in general, are less likely to be flaky.</div><div><br /></div><h3 style="text-align: left;">The OS and hardware that the SUT and testing framework depend upon</h3><div><br /></div><div><br /></div><div>Finally, the underlying hardware and operating system can be the source of test flakiness. Typical causes include:</div><div><ul style="text-align: left;"><li>Networking failures or instability.</li><li>Disk errors.</li><li>Resources being consumed by other tasks/services not related to the tests being run.</li></ul></div><div><br /></div><div><div>As can be seen from the wide variety of failures, having low flakiness in automated testing can be quite a challenge. This article has both outlined the areas and the types of flakiness that can occur in those areas, so it can serve as a cheat sheet when triaging flaky tests.</div><div><br /></div><div><br /></div><div>In the follow-up of this blog we’ll look at ways of addressing these issues.</div></div><div><br /></div><h3 style="text-align: left;"><br /></h3><h3 style="text-align: left;">References</h3><div><div><ul style="text-align: left;"><li><a href="https://testing.googleblog.com/2017/04/where-do-our-flaky-tests-come-from.html">Where do our flaky tests come from?</a></li><li><a href="https://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html">Flaky Tests at Google and How We Mitigate Them</a></li><li><a href="https://testing.googleblog.com/2009/06/my-selenium-tests-arent-stable.html">My Selenium Tests Aren't Stable!</a></li><li><a href="https://testing.googleblog.com/2008/04/tott-avoiding-flakey-tests.html">TotT: Avoiding Flakey Tests</a></li></ul></div></div><div><br /></div><div><br /></div><div><br /></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com9tag:blogger.com,1999:blog-15045980.post-56192827285730057512020-12-09T11:05:00.002-08:002020-12-09T12:35:18.810-08:00Testing on the Toilet: Separation of Concerns? That's a Wrap!<p><i><span style="font-family: times;">This article was adapted from a Google <a href="http://googletesting.blogspot.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> (TotT) episode. You can download a <a href="https://docs.google.com/document/d/16865gKSr3QXh6kRyKw8g9B6Q13L48t3lNNWfWdg21Ng/edit?usp=sharing" rel="nofollow">printer-friendly version</a> of this TotT episode and post it in your office.</span></i></p><div><i><span style="font-family: times;"><br /></span></i></div><div><span style="font-family: times;">By Stefan Kennedy</span></div><div><span style="font-family: times;"><br /></span></div><div><span style="font-family: times;"><br /></span></div><div><span style="font-family: times;"><div>The following function decodes a byte array as an image using an API named SpeedyImg. What maintenance problems might arise due to referencing an API owned by a different team?</div><div><br /></div></span></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #f4cccc; vertical-align: top; width: 607px;"><pre style="background-color: #f4cccc; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span style="font-weight: bold;">SpeedyImgImage</span> decodeImage(List<<span style="font-weight: bold;">SpeedyImgDecoder</span>> <span style="font-weight: bold;">decoders</span>, byte[] data) {
<span style="font-weight: bold;">SpeedyImgOptions options</span> = getDefaultConvertOptions();
for (<span style="font-weight: bold;">SpeedyImgDecoder decoder</span> : <span style="font-weight: bold;">decoders</span>) {
<span style="font-weight: bold;">SpeedyImgResult decodeResult</span> = <span style="font-weight: bold;">decoder</span>.decode(<span style="font-weight: bold;">decoder</span>.formatBytes(data));
<span style="font-weight: bold;">SpeedyImgImage image</span> = <span style="font-weight: bold;">decodeResult</span>.getImage(<span style="font-weight: bold;">options</span>);
if (validateGoodImage(<span style="font-weight: bold;">image</span>)) { return <span style="font-weight: bold;">image</span>; }
}
throw new RuntimeException();
}</span></pre></td></tr>
</tbody></table><br />
</div>
<div style="overflow-x: auto;"><span style="font-family: times;"><span style="color: #800180; font-weight: bold;"><br /></span></span></div><div style="overflow-x: auto;"><span style="font-family: times;"><span style="color: #800180; font-weight: bold;"><br /></span></span></div><div style="overflow-x: auto;"><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Details about how to call the API are mixed with domain logic</span>, which can make the code harder to understand. For example, the call to <span style="color: #0f9d58; font-family: "courier new" , "courier" , monospace;">decoder.formatBytes()</span> is required by the API, but how the bytes are formatted isn’t relevant to the domain logic.</span></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><div style="overflow-x: auto;"><span style="font-family: times;"><br /></span></div><div style="overflow-x: auto;"><span style="font-family: times;">Additionally, if this API is used in many places across a codebase, then <span style="color: #800180; font-weight: bold;">all usages may need to change if the way the API is used changes.</span> For example, if the return type of this function is changed to the more generic <span style="color: #0f9d58; font-family: "courier new" , "courier" , monospace;">SpeedyImgResult</span> type, usages of <span style="color: #0f9d58; font-family: "courier new" , "courier" , monospace;">SpeedyImgImage</span> would need to be updated.</span></div><div><br /></div><div><br /></div><div><div><span style="font-family: times;">To avoid these maintenance problems, <span style="color: #800180; font-weight: bold;">create <i>wrapper types</i> to hide API details behind an abstraction</span>:</span></div></div><div><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #d9ead3; vertical-align: top; width: 607px;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><span style="font-weight: bold;">Image</span> decodeImage(List<<span style="font-weight: bold;">ImageDecoder</span>> <span style="font-weight: bold;">decoders</span>, byte[] data) {
for (<span style="font-weight: bold;">ImageDecoder</span> decoder : decoders) {
<span style="font-weight: bold;">Image decodedImage</span> = decoder.decode(data);
if (validateGoodImage(<span style="font-weight: bold;">decodedImage</span>)) { return <span style="font-weight: bold;">decodedImage</span>; }
}
throw new RuntimeException();
}</span></pre></td></tr>
</tbody></table>
<div><br /></div><div><br /></div><div><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Wrapping an external API follows the <a href="https://en.wikipedia.org/wiki/Separation_of_concerns">Separation of Concerns</a></span> principle, since the logic for how the API is called is separated from the domain logic. This has many benefits, including:</span></div><div><ul style="text-align: left;"><li><span style="font-family: times;">If the way the API is used changes, encapsulating the API in a wrapper insulates how far those changes can propagate across your codebase.</span></li><li><span style="font-family: times;">You can modify the interface or the implementation of types you own, but you can’t for API types.</span></li><li><span style="font-family: times;">It is easier to switch or add another API, since they can still be represented by the introduced types (e.g. <span style="color: #0f9d58; font-family: "courier new" , "courier" , monospace;">ImageDecoder/Image</span>).</span></li><li><span style="font-family: times;">Readability can improve as you don’t need to sift through API code to understand core logic.</span></li></ul><br /></div><div><div><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Not all external APIs need to be wrapped</span>. For example, if an API would take a huge effort to separate or is simple enough that it doesn't pollute the codebase, it may be better not to introduce wrapper types (e.g. library types like <span style="color: #0f9d58; font-family: "courier new" , "courier" , monospace;">List</span> in Java or <span style="color: #0f9d58; font-family: "courier new" , "courier" , monospace;">std::vector</span> in C++). When in doubt, keep in mind that a wrapper should only be added if it will clearly improve the code (see the <a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">YAGNI</a> principle).</span></div></div><div><br /></div><div><br /></div></div><div style="overflow-x: auto;"><p dir="ltr" style="font-family: times; line-height: 1.2; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: "Times New Roman"; font-size: 13pt; font-style: italic; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">“Separation of Concerns” in the context of external APIs is also described by Martin Fowler in his blog post, </span><a href="https://martinfowler.com/articles/refactoring-external-service.html" style="text-decoration-line: none;"><span style="color: #1155cc; font-family: "Times New Roman"; font-size: 13pt; font-style: italic; font-variant-east-asian: normal; font-variant-numeric: normal; text-decoration-line: underline; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Refactoring code that accesses external services</span></a><span style="font-family: "Times New Roman"; font-size: 13pt; font-style: italic; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">. </span></p><div><span style="font-family: "Times New Roman"; font-size: 13pt; font-style: italic; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div></div>
</div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com2tag:blogger.com,1999:blog-15045980.post-58342305786224725772020-11-09T09:23:00.001-08:002020-11-09T09:23:56.785-08:00Fixing a Test HourglassBy Alan Myrvold<div><br /></div><div><br /></div><div><div>Automated tests make it safer and faster to create new features, fix bugs, and refactor code. When planning the automated tests, we envision a pyramid with a strong foundation of small unit tests, some well designed integration tests, and a few large end-to-end tests. From <a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html">Just Say No to More End-to-End Tests</a>, tests should be fast, reliable, and specific; end-to-end tests, however, are often slow, unreliable, and difficult to debug.</div></div><div><br /></div><div><br /></div><div><div>As software projects grow, often the shape of our test distribution becomes undesirable, either top heavy (no unit or medium integration tests), or like an hourglass.</div></div><div><br /></div><div><br /></div><div><div>The hourglass test distribution has a large set of unit tests, a large set of end-to-end tests, and few or no medium integration tests.</div></div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="0" data-original-height="400" data-original-width="330" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBcjRL4H5PZyx9By6YuWOfdQ7HcMmBI69vEC9R-4SaiB0uX1Nu5bek8i8ZE04ybm3gs_0S64CTMPsXnHDVKbfxhOD3ePr-qpMRqJnqNvextQFWz6so3tSlwfubtTVxBUbx0oD5/s320/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+-+Edited.jpg" /> <img border="0" data-original-height="400" data-original-width="288" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7vt0oUgXE0OdsGMA3gUSZUe1qxs1wULZjR1IGMHHIiHC_ljDFlWotoNBIoyc-YnreyQMiA5QEAguEFuYjjnXJy6bUY-aAEY-Hqv1H5hONrg1vy5m-de8ETQpfJO97w1iZjO9B/s320/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25281%2529+-+Edited.jpg" /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">To transform the hourglass back into a pyramid — so that you can test the integration of components in a reliable, sustainable way — you need to figure out how to architect the system under test and test infrastructure and make system testability improvements and test-code improvements.</div><div><br /></div><div><br /></div><div><div>I worked on a project with a web UI, a server, and many backends. There were unit tests at all levels with good coverage and a quickly increasing set of end-to-end tests.</div></div><div><br /></div><div><br /></div><div><div>The end-to-end tests found issues that the unit tests missed, but they ran slowly, and environmental issues caused spurious failures, including test data corruption. In addition, some functional areas were difficult to test because they covered more than the unit but required state within the system that was hard to set up.</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD_aw7EpvaXrpQ8jX4O86OXMknitbhaepcE4qhKHIyV8OfujFnI0neJcz2mqEtZ0gdB-BMA8NoBRAF3471L4jPD4nZ9OBsMqwS0-DLwpJW7JMYmB-Xi6Q_u8-BBoSahq2c30TK/s637/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25282%2529+-+Edited.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="1" data-original-height="400" data-original-width="637" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD_aw7EpvaXrpQ8jX4O86OXMknitbhaepcE4qhKHIyV8OfujFnI0neJcz2mqEtZ0gdB-BMA8NoBRAF3471L4jPD4nZ9OBsMqwS0-DLwpJW7JMYmB-Xi6Q_u8-BBoSahq2c30TK/s16000/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25282%2529+-+Edited.jpg" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div>We eventually found a good test architecture for faster, more reliable integration tests, but with some missteps along the way.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">An example UI-level end-to-end test, written in <a href="https://protractor.angular.io/">protractor</a>, looked something like this:</div><div><br /></div><div><br /></div></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="vertical-align: top; width: 607px;"><pre style="border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">describe('Terms of service are handled', () => {
it('accepts terms of service', async () => {
const user = getUser('termsNotAccepted');
await login(user);
await see(termsOfServiceDialog());
await click('Accept')
await logoff();
await login(user);
await not.see(termsOfServiceDialog());
});
});</span></pre>
</td></tr>
</tbody></table></div>
<div style="overflow-x: auto;"><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;">This test logs on as a user, sees the terms of service dialog that the user needs to accept, accepts it, then logs off and logs back on to ensure the user is not prompted again.</div><div><br /></div><div><br /></div><div><div>This terms of service test was a challenge to run reliably, because once an agreement was accepted, the backend server had no RPC method to reverse the operation and “un-accept” the TOS. We could create a new user with each test, but that was time consuming and hard to clean up.</div></div><div><br /></div><div><br /></div><div><div>The first attempt to make the terms of service feature testable without end-to-end testing was to hook the server RPC method and set the expectations within the test. The hook intercepts the RPC call and provides expected results instead of calling the backend API.</div></div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><img border="1" data-original-height="389" data-original-width="595" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc-iEOvsO4Bbl0583gNzI5HMXrwbyWrI-I_G1vexQGmaNghR2OruzsdI9qRmWU3gk4B-hMLQEmyR5Jpc8lbdVav3gHRO5Ri6bCdj0Sh3BKaCIrArGiSqzfQfz_gi7nSNZMec0l/s16000/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25283%2529+-+Edited.jpg" /></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><br /></div>This approach worked. The test interacted with the backend RPC without really calling it, but it cluttered the test with extra logic.<br /><div><br /></div></div><div style="overflow-x: auto;"><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="vertical-align: top; width: 607px;"><pre style="border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">describe('Terms of service are handled', () => {
it('accepts terms of service', async () => {
const user = getUser('someUser');
await hook('TermsOfService.Get()', true);
await login(user);
await see(termsOfServiceDialog());
await click('Accept')
await logoff();
await hook('TermsOfService.Get()', false);
await login(user);
await not.see(termsOfServiceDialog());
});
});</span></pre>
</td></tr>
</tbody></table></div>
<div style="overflow-x: auto;"><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;">The test met the goal of testing the integration of the web UI and server, but it was unreliable. As the system scaled under load, there were several server processes and no guarantee that the UI would access the same server for all RPC calls, so the hook might be set in one server process and the UI accessed in another. </div><div><br /></div><div><br /></div><div><div>The hook also wasn't at a natural system boundary, which made it require more maintenance as the system evolved and code was refactored.</div></div><div><br /></div><div><br /></div><div>The next design of the test architecture was to fake the backend that eventually processes the terms of service call.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigDTMCMC8KK_h0RX75gcfCVpmVoEsz3hke9tvQos8_gNE1wje0BogKepp8uo0gBQSWFs7nVueQjfsV493Oa9b9EDSlp8IjqLeYprTGedasqEfxpQGaUmegCjg0zkDONus6u6Wn/s616/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25284%2529+-+Edited.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="1" data-original-height="400" data-original-width="616" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigDTMCMC8KK_h0RX75gcfCVpmVoEsz3hke9tvQos8_gNE1wje0BogKepp8uo0gBQSWFs7nVueQjfsV493Oa9b9EDSlp8IjqLeYprTGedasqEfxpQGaUmegCjg0zkDONus6u6Wn/s16000/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25284%2529+-+Edited.jpg" /></a></div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;">The fake implementation can be quite simple:</div><div><br /></div></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="vertical-align: top; width: 607px;"><pre style="border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">public class FakeTermsOfService implements TermsOfService.Service {
private static final Map<String, Boolean> accepted = new ConcurrentHashMap<>();
@Override
public TosGetResponse get(TosGetRequest req) {
return accepted.getOrDefault(req.UserID(), Boolean.FALSE);
}
@Override
public void accept(TosAcceptRequest req) {
accepted.put(req.UserID(), Boolean.TRUE);
}
}</span></pre></td></tr>
</tbody></table></div><div><br /></div><div><br /></div><div><br /></div>
And the test is now uncluttered by the expectations:<div><br /><div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="vertical-align: top; width: 607px;"><pre style="border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">describe('Terms of service are handled', () => {
</span></pre><pre style="border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"> it('accepts terms of service', async () => {
const user = getUser('termsNotAccepted');
await login(user);
await see(termsOfServiceDialog());
await click('Accept')
await logoff();
await login(user);
await not.see(termsOfServiceDialog());
});
});</span></pre></td></tr>
</tbody></table></div><div><br /></div><div><br /></div><div>Because the fake stores the accepted state in memory, there is no need to reset the state for the next test iteration; it is enough just to restart the fake server.</div><div><br /></div><div><br /></div><div><div>This worked but was problematic when there was a mix of fake and real backends. This was because there was state between the real backends that was now out of sync with the fake backend.</div></div><div><br /></div><div><br /></div><div><div>Our final, successful integration test architecture was to provide fake implementations for all except one of the backends, all sharing the same in-memory state. One real backend was included in the system under test because it was tightly coupled with the Web UI. Its dependencies were all wired to fake backends. These are integration tests over the entire system under test, but they remove the backend dependencies. These tests expand the medium size tests in the test hourglass, allowing us to have fewer end-to-end tests with real backends.</div></div><div><br /></div><div><br /></div><div><div>Note that these integration tests are not only the option. For logic in the Web UI, we can write page level unit tests, which allow the tests to run faster and more reliably. For the terms of service feature, however, we want to test the Web UI and server logic together, so integration tests are a good solution.</div></div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSFD-ohxvljx_jhnybtZFd5g05lzR-UWZReDuVVABDNE5FDp1vcMn32DHXuKtohryYVd_aIW5pk5NKNGYgy_ODCr_TbVuZF4PCtqnyFg2Hdc0LGH9XEAFCT7L4QgpHMxCrZewZ/s594/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25285%2529+-+Edited.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="1" data-original-height="461" data-original-width="594" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSFD-ohxvljx_jhnybtZFd5g05lzR-UWZReDuVVABDNE5FDp1vcMn32DHXuKtohryYVd_aIW5pk5NKNGYgy_ODCr_TbVuZF4PCtqnyFg2Hdc0LGH9XEAFCT7L4QgpHMxCrZewZ/s16000/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25285%2529+-+Edited.jpg" /></a></div><br /><div><br /></div><div><br /></div><div>This resulted in UI tests that ran, unmodified, on both the real and fake backend systems. </div><div><br /></div><div><br /></div><div><div>When run with fake backends the tests were faster and more reliable. This made it easier to add test scenarios that would have been more challenging to set up with the real backends. We also deleted end-to-end tests that were well duplicated by the integration tests, resulting in more integration tests than end-to-end tests.</div></div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCwdTcQbDCI7sArfHDKxxXqEl2KkI16ejx9U-NvP0gMpr0V5tzmw0_3C5afj12ZEVFpGUMik8CQo7nj_CspteU0GGT9WwfrPmh7ZZu2MHNQwSoWWENSNW6yFS0VPxFNdQzz8bX/s463/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25286%2529+-+Edited.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="463" data-original-width="380" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCwdTcQbDCI7sArfHDKxxXqEl2KkI16ejx9U-NvP0gMpr0V5tzmw0_3C5afj12ZEVFpGUMik8CQo7nj_CspteU0GGT9WwfrPmh7ZZu2MHNQwSoWWENSNW6yFS0VPxFNdQzz8bX/s320/Copy+of+Google+Testing+Blog_+Fixing+a+Test+Hourglass+%25286%2529+-+Edited.jpg" /></a></div><div><div><br /></div><div>By iterating, we arrived at a sustainable test architecture for the integration tests.</div></div><div><br /></div><div><br /></div><div>If you're facing a test hourglass the test architecture to devise medium tests may not be obvious. I'd recommend experimenting, dividing the system on well defined interfaces, and making sure the new tests are providing value by running faster and more reliably or by unlocking hard to test areas.</div><div><br /></div><div style="text-align: left;"><br /></div><h3 style="text-align: left;">References</h3><div><ul style="text-align: left;"><li>Just Say No to More End-to-End Tests, Mike Wacker, <a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html">https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html</a></li><li>Test Pyramid & Antipatterns, Khushi, <a href="https://khushiy.com/2019/02/07/test-pyramid-antipatterns/">https://khushiy.com/2019/02/07/test-pyramid-antipatterns/</a></li><li>Testing on the Toilet: Fake Your Way to Better Tests, Jonathan Rockway and Andrew Trenk, <a href="https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html">https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html</a></li><li>Testing on the Toilet: Know Your Test Doubles, Andrew Trenk, <a href="https://testing.googleblog.com/2013/07/testing-on-toilet-know-your-test-doubles.html">https://testing.googleblog.com/2013/07/testing-on-toilet-know-your-test-doubles.html</a></li><li>Hermetic Servers, Chaitali Narla and Diego Salas, <a href="https://testing.googleblog.com/2012/10/hermetic-servers.html">https://testing.googleblog.com/2012/10/hermetic-servers.html</a></li><li>Software Engineering at Google, Titus Winters, Tom Manshreck, Hyrum Wright, <a href="https://www.oreilly.com/library/view/software-engineering-at/9781492082781/">https://www.oreilly.com/library/view/software-engineering-at/9781492082781/</a></li></ul></div><div><br /></div><div><br /></div></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com4tag:blogger.com,1999:blog-15045980.post-75917987873178829052020-10-26T10:05:00.000-07:002023-12-12T08:34:00.191-08:00Testing on the Toilet: Testing UI Logic? Follow the User!<i><span style="font-family: times;">This article was adapted from a Google <a href="http://googletesting.blogspot.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> (TotT) episode. You can download a <a href="https://docs.google.com/document/d/1j4Z_Pvr5CxqRb3kRG3hxepIoZkXN81fjDVHGZEqkhxk/edit?usp=sharing">printer-friendly version</a> of this TotT episode and post it in your office.</span></i><div><i><span style="font-family: times;"><br /></span></i></div><div><span style="font-family: times;">By Carlos Israel Ortiz García</span></div><div><span style="font-family: times;"><br /></span></div><div><span style="font-family: times;"><br /></span></div><div><span style="font-family: times;"><div>After years of anticipation, you're finally able to purchase Google's hottest new product, gShoe*. But after clicking the "Buy" button, nothing happened! Inspecting the HTML, you notice the problem:</div><div><br /></div></span></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #c9daf8; vertical-align: top; width: 607px;"><pre style="background-color: #c9daf8; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;"><button <span style="font-weight: bold;">disabled</span> click=”$handleBuyClick(data)”>Buy</button></span></pre>
</td></tr>
</tbody></table>
</div><div><br /></div><div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4Af_A9ulTbVtNuo8OtiUnbMdKtfdPPa7w9AmP3K2bXISk0JRmkPfUQ84Jjbv5o-Heqa3hQlMaqsNIib8YMpyLpljsLdQle1N3Qeq9RB-_ch4f0w8R3-cLujyXYWgSpxi51kOM/s435/image1.png" style="clear: right; float: right; font-family: times; margin-bottom: 1em; margin-left: 1em; text-align: center;"><img border="0" data-original-height="435" data-original-width="349" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4Af_A9ulTbVtNuo8OtiUnbMdKtfdPPa7w9AmP3K2bXISk0JRmkPfUQ84Jjbv5o-Heqa3hQlMaqsNIib8YMpyLpljsLdQle1N3Qeq9RB-_ch4f0w8R3-cLujyXYWgSpxi51kOM/w258-h320/image1.png" width="258" /></a></div><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Users couldn’t buy their gShoes because the “Buy” button was disabled.</span> The problem was due to the unit test for <span style="font-family: "courier new" , "courier" , monospace;">handleBuyClick</span>, which passed <i>even though the user interface had a bug:</i></span><div><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #f4cccc; vertical-align: top; width: 607px;"><pre style="background-color: #f4cccc; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">it('submits purchase request', () => {
controller = new PurchasePage();
<span style="color: #1155cc;">// Call the method that handles the "Buy" button click</span>
<span style="font-weight: bold;">controller.handleBuyClick(data);</span>
expect(service).toHaveBeenCalledWith(expectedData);
});
</span></pre>
</td></tr>
</tbody></table>
</div><div><br /></div><span style="font-family: times;">
In the above example, the test failed to detect the bug because it <i>bypassed</i> the UI element and instead directly invoked the "Buy" button click handler. <span style="color: #800180; font-weight: bold;">To be effective, tests for UI logic should interact with the <i>components</i> on the page as a browser would</span>, which allows testing the behavior that the end user experiences. <span style="color: #800180; font-weight: bold;">Writing tests against <i>UI components</i> rather than calling handlers directly faithfully simulates user interactions</span> (e.g., add items to a shopping cart, click a purchase button, or verify an element is visible on the page), making the tests more comprehensive.</span><div><br /></div><div><br /></div><div><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">The test for the “Buy” button should instead exercise the entire UI component by interacting with the HTML element,</span> which would have caught the disabled button issue:</span></div><div><br /></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #d9ead3; vertical-align: top; width: 607px;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span style="font-family: consolas, "courier new", courier, monospace;">it('submits purchase request', () => {
<span style="color: #1155cc;">// Renders the page with the “Buy” button and its associated code.</span>
render(PurchasePage);
<span style="color: #1155cc;">// Tries to click the button, fails the test, and catches the bug!</span>
<span style="font-weight: bold;">buttonWithText('Buy').dispatchEvent(new Event(‘click’));</span>
expect(service).toHaveBeenCalledWith(expectedData);
});
</span></pre>
</td></tr>
</tbody></table><br />
</div><br /><div><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Why should tests be written this way?</span> Unlike end-to-end tests, tests for individual UI components don’t require a backend server or the entire app to be rendered. Instead, these tests run in the same self-contained environment and take a similar amount of time to execute as unit tests that just execute the underlying event handlers directl</span><span style="font-family: times;">y. Therefore, the UI acts as the public API, leaving the business logic as an implementation detail (also known as the </span><a href="http://xunitpatterns.com/Principles%20of%20Test%20Automation.html#Use%20the%20Front%20Door%20First" style="font-family: times;">"Use the Front Door First"</a><span style="font-family: times;"> principle), resulting in better coverage of a feature.</span></div><div><span style="font-family: times;"><br /></span></div><div><span style="font-family: times;"><i><span style="font-weight: bold;">Disclaimer:</span> “gShoe” is not a real Google product. Unfortunately you can’t buy a pair even if the bug is fixed!</i></span></div><div><br /></div>Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com6tag:blogger.com,1999:blog-15045980.post-24407334332736893352020-08-19T09:04:00.000-07:002020-08-19T09:04:17.279-07:00Testing on the Toilet: Avoid Hardcoding Values for Better Libraries<i><span style="font-family: times;">This article was adapted from a Google <a href="http://googletesting.blogspot.com/2007/01/introducing-testing-on-toilet.html">Testing on the Toilet</a> (TotT) episode. You can download a <a href="https://docs.google.com/document/d/1YPg8KK1w-fsKCYupdVkE9MvDezlaSYSfASGKW46GKrw/edit?usp=sharing">printer-friendly version</a> of this TotT episode and post it in your office.</span></i><div><br /></div><div><span style="font-family: times;">By Adel Saoud</span></div><div><br /></div><div><br /></div><div><span style="font-family: times;">You may have been in a situation where you're using a value that always remains the same, so you define a constant. This can be a good practice as it removes magic values and improves code readability. But be mindful that <span style="color: #800180; font-weight: bold;">hardcoding values can make usability and potential refactoring significantly harder</span>.</span></div><div><br /></div><div><span style="font-family: times;">Consider the following function that relies on hardcoded values:</span></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #f4cccc; vertical-align: top; width: 607px;"><pre style="background-color: #f4cccc; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span face="" style="font-family: consolas, "courier new", courier, monospace;"><span style="color: #1155cc;">// Declared in the module.</span>
constexpr int <span style="font-weight: bold;">kThumbnailSizes</span>[] = {480, 576, 720};
<span style="color: #1155cc;">// Returns thumbnails of various sizes for the given image.</span>
std::vector<Image> GetThumbnails(const Image& image) {
std::vector<Image> thumbnails;
for (const int size : <span style="font-weight: bold;">kThumbnailSizes</span>) {
thumbnails.push_back(ResizeImage(image, size));
}
return thumbnails;
}</span></pre>
</td></tr>
</tbody></table>
</div>
<br />
<br />
<div><span style="color: #800180; font-family: times; font-weight: bold;">Using hardcoded values can make your code:</span></div><div><ul style="text-align: left;"><li><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Less predictable:</span> The caller might not expect the function to be relying on hardcoded values outside its parameters; a user of the function shouldn’t need to read the function’s code to know that. Also, it is difficult to predict the product/resource/performance implications of changing these hardcoded values.</span></li><li><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">Less reusable:</span> The caller is not able to call the function with different values and is stuck with the hardcoded values. If the caller doesn’t need all these sizes or needs a different size, the function has to be forked or refactored to avoid aforementioned complications with existing callers.</span></li></ul><div><br /></div></div><div><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">When designing a library, prefer to pass required values, such as through a function call or a constructor</span>. The code above can be improved as follows:</span></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #d9ead3; vertical-align: top; width: 607px;"><pre style="background-color: #d9ead3; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span face="" style="font-family: consolas, "courier new", courier, monospace;">std::vector<Image> GetThumbnails(const Image& image, <span style="font-weight: bold;">absl::Span<const int> sizes</span>) {
std::vector<Image> thumbnails;
for (const int size : <span style="font-weight: bold;">sizes</span>) {
thumbnails.push_back(ResizeImage(image, size));
}
return thumbnails;
}</span></pre>
</td></tr>
</tbody></table><br />
</div><div style="overflow-x: auto;"><br /></div><div style="overflow-x: auto;"><span style="font-family: times;"><span style="color: #800180; font-weight: bold;">If most of the callers use the same value for a certain parameter, make your code configurable so that this value doesn't need to be duplicated by each caller.</span> For example, you can define a public constant that contains a commonly used value, or use default arguments in languages that support this feature (e.g. C++ or Python).</span></div>
<div style="overflow-x: auto;">
<table class="my-bordered-table" style="width: 613px;"><tbody>
<tr><td style="background-color: #c9daf8; vertical-align: top; width: 607px;"><pre style="background-color: #c9daf8; border: 0px; color: black; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-top: 0px;"><span face="" style="font-family: consolas, "courier new", courier, monospace;"><span style="color: #0f9d58;">// Declared in the public header.</span>
inline constexpr int <span style="font-weight: bold;">kDefaultThumbnailSizes</span>[] = {480, 576, 720};
<span style="color: #0f9d58;">// Default argument allows the function to be used without specifying a size.</span>
std::vector<Image> GetThumbnails(const Image& image,
absl::Span<const int> sizes = <span style="font-weight: bold;">kDefaultThumbnailSizes</span>);</span></pre>
</td></tr>
</tbody></table>
</div>
Google Testing Bloggershttp://www.blogger.com/profile/03153388556673050910noreply@blogger.com7