CINXE.COM
Google Testing Blog: 2009
<!DOCTYPE html> <html class='v2 list-page' dir='ltr' itemscope='' itemtype='http://schema.org/Blog' lang='en' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'> <head> <link href='https://www.blogger.com/static/v1/widgets/3566091532-css_bundle_v2.css' rel='stylesheet' type='text/css'/> <title> Google Testing Blog: 2009 </title> <meta content='width=device-width, height=device-height, minimum-scale=1.0, initial-scale=1.0, user-scalable=0' name='viewport'/> <meta content='IE=Edge' http-equiv='X-UA-Compatible'/> <meta content='Google Testing Blog' property='og:title'/> <meta content='en_US' property='og:locale'/> <meta content='https://testing.googleblog.com/2009/' property='og:url'/> <meta content='Google Testing Blog' property='og:site_name'/> <!-- Twitter Card properties --> <meta content='Google Testing Blog' property='og:title'/> <meta content='summary' name='twitter:card'/> <meta content='@googletesting' name='twitter:creator'/> <link href='https://fonts.googleapis.com/css?family=Roboto:400italic,400,500,500italic,700,700italic' rel='stylesheet' type='text/css'/> <link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet'/> <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js' type='text/javascript'></script> <!-- End --> <style id='page-skin-1' type='text/css'><!-- /* <Group description="Header Color" selector="header"> <Variable name="header.background.color" description="Header Background" type="color" default="#ffffff"/> </Group> */ .header-outer { border-bottom: 1px solid #e0e0e0; background: #ffffff; } html, .Label h2, #sidebar .rss a, .BlogArchive h2, .FollowByEmail h2.title, .widget .post h2 { font-family: Roboto, sans-serif; } .plusfollowers h2.title, .post h2.title, .widget h2.title { font-family: Roboto, sans-serif; } .widget-item-control { height: 100%; } .widget.Header, #header { position: relative; height: 100%; width: 100%; } } .widget.Header .header-logo1 { float: left; margin-right: 15px; padding-right: 15px; border-right: 1px solid #ddd; } .header-title h2 { color: rgba(0,0,0,.54); display: inline-block; font-size: 40px; font-family: Roboto, sans-serif; font-weight: normal; line-height: 48px; vertical-align: top; } .header-inner { background-repeat: no-repeat; background-position: right 0px; } .post-author, .byline-author { font-size: 14px; font-weight: normal; color: #757575; color: rgba(0,0,0,.54); } .post-content .img-border { border: 1px solid rgb(235, 235, 235); padding: 4px; } .header-title a { text-decoration: none !important; } pre { border: 1px solid #bbbbbb; margin-top: 1em 0 0 0; padding: 0.99em; overflow-x: auto; overflow-y: auto; } pre, code { font-size: 9pt; background-color: #fafafa; line-height: 125%; font-family: monospace; } pre, code { color: #060; font: 13px/1.54 "courier new",courier,monospace; } .header-left .header-logo1 { width: 128px !important; } .header-desc { line-height: 20px; margin-top: 8px; } .fb-custom img, .twitter-custom img, .gplus-share img { cursor: pointer; opacity: 0.54; } .fb-custom img:hover, .twitter-custom img:hover, .gplus-share img:hover { opacity: 0.87; } .fb-like { width: 80px; } .post .share { float: right; } #twitter-share{ border: #CCC solid 1px; border-radius: 3px; background-image: -webkit-linear-gradient(top,#ffffff,#dedede); } .twitter-follow { background: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSPSElBOH0lQ-yG1UMSLWe_pq9gZ9DT-0xhhjwadPeQt1WAHCnDYvERUcHBadwN6NcdtZCJu-F0fLqO6_PnV2iWDxnun1R_ii8saOztKUI6GA6zc2FNkjkcfTDlPzaINdbYhPw7Q/s1600/twitter-bird.png) no-repeat left center; padding-left: 18px; font: normal normal normal 11px/18px 'Helvetica Neue',Arial,sans-serif; font-weight: bold; text-shadow: 0 1px 0 rgba(255,255,255,.5); cursor: pointer; margin-bottom: 10px; } .twitter-fb { padding-top: 2px; } .fb-follow-button { background: -webkit-linear-gradient(#4c69ba, #3b55a0); background: -moz-linear-gradient(#4c69ba, #3b55a0); background: linear-gradient(#4c69ba, #3b55a0); border-radius: 2px; height: 18px; padding: 4px 0 0 3px; width: 57px; border: #4c69ba solid 1px; } .fb-follow-button a { text-decoration: none !important; text-shadow: 0 -1px 0 #354c8c; text-align: center; white-space: nowrap; font-size: 11px; color: white; vertical-align: top; } .fb-follow-button a:visited { color: white; } .fb-follow { padding: 0px 5px 3px 0px; width: 14px; vertical-align: bottom; } .gplus-wrapper { margin-top: 3px; display: inline-block; vertical-align: top; } .twitter-custom, .gplus-share { margin-right: 12px; } .fb-follow-button{ margin: 10px auto; } /** CUSTOM CODE **/ /* Make the page title smaller */ .header-inner { height: 120px !important; } /* Make the post titles look like the links that they are */ .post .title a { color: #4184F3 !important; } /* Set a normal line height in post text */ .post .post-content { line-height: 1.4 !important; } .post .post-content li { line-height: 1.4 !important; } /* Custom table class used in some posts */ .my-bordered-table { border-collapse: collapse; border: 1px solid black; } .my-bordered-table th, .my-bordered-table td { border: 1px solid black; padding: 5px; } --></style> <style id='template-skin-1' type='text/css'><!-- .header-outer { clear: both; } .header-inner { margin: auto; padding: 0px; } .footer-outer { background: #f5f5f5; clear: both; margin: 0; } .footer-inner { margin: auto; padding: 0px; } .footer-inner-2 { /* Account for right hand column elasticity. */ max-width: calc(100% - 248px); } .google-footer-outer { clear: both; } .cols-wrapper, .google-footer-outer, .footer-inner, .header-inner { max-width: 978px; margin-left: auto; margin-right: auto; } .cols-wrapper { margin: auto; clear: both; margin-top: 60px; margin-bottom: 60px; overflow: hidden; } .col-main-wrapper { float: left; width: 100%; } .col-main { margin-right: 278px; max-width: 660px; } .col-right { float: right; width: 248px; margin-left: -278px; } /* Tweaks for layout mode. */ body#layout .google-footer-outer { display: none; } body#layout .header-outer, body#layout .footer-outer { background: none; } body#layout .header-inner { height: initial; } body#layout .cols-wrapper { margin-top: initial; margin-bottom: initial; } --></style> <!-- start all head --> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='blogger' name='generator'/> <link href='https://testing.googleblog.com/favicon.ico' rel='icon' type='image/x-icon'/> <link href='https://testing.googleblog.com/2009/' rel='canonical'/> <link rel="alternate" type="application/atom+xml" title="Google Testing Blog - Atom" href="https://testing.googleblog.com/feeds/posts/default" /> <link rel="alternate" type="application/rss+xml" title="Google Testing Blog - RSS" href="https://testing.googleblog.com/feeds/posts/default?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="Google Testing Blog - Atom" href="https://www.blogger.com/feeds/15045980/posts/default" /> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <meta content='https://testing.googleblog.com/2009/' property='og:url'/> <meta content='Google Testing Blog' property='og:title'/> <meta content='' property='og:description'/> <!-- end all head --> <base target='_self'/> <style> html { font-family: Roboto, sans-serif; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } body { padding: 0; /* This ensures that the scroll bar is always present, which is needed */ /* because content render happens after page load; otherwise the header */ /* would "bounce" in-between states. */ min-height: 150%; } h2 { font-size: 20px; } h1, h2, h3, h4, h5 { line-height: 2em; } html, h4, h5, h6 { font-size: 17px; } h3 { font-size: 18px !important; } a, a:visited { color: #4184F3; text-decoration: none; } a:focus, a:hover, a:active { text-decoration: none; } .Header { margin-top: 15px; } .Header h1 { font-size: 32px; font-weight: 300; line-height: 32px; height: 42px; } .header-inner .Header .titlewrapper { padding: 0; margin-top: 30px; } .header-inner .Header .descriptionwrapper { padding: 0; margin: 0; } .cols-wrapper { margin-top: 56px; } .header-outer, .cols-wrapper, .footer-outer, .google-footer-outer { padding: 0 60px; } .header-inner { height: 256px; position: relative; } html, .header-inner a { color: #212121; color: rgba(0,0,0,.87); } .header-inner .google-logo { display: inline-block; background-size: contain; z-index: 1; height: 46px; overflow: hidden; margin-top: 4px; margin-right: 8px; } .header-left { position: absolute; top: 50%; -webkit-transform: translateY(-50%); transform: translateY(-50%); margin-top: 12px; width: 100%; } .google-logo { margin-left: -4px; } #google-footer { position: relative; font-size: 13px; list-style: none; text-align: right; } #google-footer a { color: #444; } #google-footer ul { margin: 0; padding: 0; height: 144px; line-height: 144px; } #google-footer ul li { display: inline; } #google-footer ul li:before { color: #999; content: "\00b7"; font-weight: bold; margin: 5px; } #google-footer ul li:first-child:before { content: ''; } #google-footer .google-logo-dark { left: 0; margin-top: -16px; position: absolute; top: 50%; } /** Sitemap links. **/ .footer-inner-2 { font-size: 14px; padding-top: 42px; padding-bottom: 74px; } .footer-inner-2 .HTML h2 { color: #212121; color: rgba(0,0,0,.87); font-size: 14px; font-weight: 500; padding-left: 0; margin: 10px 0; } .footer-inner-2 .HTML ul { font-weight: normal; list-style: none; padding-left: 0; } .footer-inner-2 .HTML li { line-height: 24px; padding: 0; } .footer-inner-2 li a { color: rgba(65,132,243,.87); } /** Archive widget. **/ .BlogArchive { font-size: 13px; font-weight: normal; } .BlogArchive .widget-content { display: none; } .BlogArchive h2, .Label h2 { color: #4184F3; text-decoration: none; } .BlogArchive .hierarchy li { display: inline-block; } /* Specificity needed here to override widget CSS defaults. */ .BlogArchive #ArchiveList ul li, .BlogArchive #ArchiveList ul ul li { padding-left: 0; text-indent: 0; } .BlogArchive .intervalToggle { cursor: pointer; } .BlogArchive .expanded .intervalToggle .new-toggle { -ms-transform: rotate(180deg); transform: rotate(180deg); } .BlogArchive .new-toggle { float: right; padding-top: 3px; opacity: 0.87; } #ArchiveList { text-transform: uppercase; } #ArchiveList .expanded > ul:last-child { margin-bottom: 16px; } #ArchiveList .archivedate { width: 100%; } /* Months */ .BlogArchive .items { max-width: 150px; margin-left: -4px; } .BlogArchive .expanded .items { margin-bottom: 10px; overflow: hidden; } .BlogArchive .items > ul { float: left; height: 32px; } .BlogArchive .items a { padding: 0 4px; } .Label { font-size: 13px; font-weight: normal; } .sidebar-icon { display: inline-block; width: 24px; height: 24px; vertical-align: middle; margin-right: 12px; margin-top: -1px } .Label a { margin-right: 4px; } .Label .widget-content { display: none; } .FollowByEmail { font-size: 13px; font-weight: normal; } .FollowByEmail h2 { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAALCAYAAACZIGYHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUBJREFUeNrMkSGLAlEUhb+ZB4JFi8mx2cz+ApvhRUGTcUCrNqNJDYIi+DO0GUwmQXDK2DSIoGgZcSaIjDrzwrK4ssvChj1w0733O+fdp+m6PozH4yQSCfb7Pa7r8pOi0SjJZBLP8zgej4gAIMvlMuPxmADIYrHger1+C6lUKmo+NJ/NZojb7SZDWiwWo1qtks1msW2bw+HwZdkwDHq9HvV6nel0SqvVYrvdIh6Ph3Qch+VyqRYLhQJSSjRNw7IsfN9XgGKxSLfbJZfL0e/3aTabrFYr7vc7IujLcOh8PqunrNdr0uk0pVKJVCpFJBJRgEajweVyod1uMxgM2O12BAGUgRbU8DV2JpOhVquRz+cRQii3+XxOp9NRN3jVR5LPOp1OjEYjlSL8hclkgmmabDabt4d+m+S30vkD/R/IU4ABAPTZgnZdmG/PAAAAAElFTkSuQmCC"); background-repeat: no-repeat; background-position: 0 50%; text-indent: 30px; } .FollowByEmail .widget-content { display: none; } .searchBox input { border: 1px solid #eee; color: #212121; color: rgba(0,0,0,.87); font-size: 14px; padding: 8px 8px 8px 40px; width: 164px; font-family: Roboto, sans-serif; background: url("https://www.gstatic.com/images/icons/material/system/1x/search_grey600_24dp.png") 8px center no-repeat; } .searchBox ::-webkit-input-placeholder { /* WebKit, Blink, Edge */ color: rgba(0,0,0,.54); } .searchBox :-moz-placeholder { /* Mozilla Firefox 4 to 18 */ color: #000; opacity: 0.54; } .searchBox ::-moz-placeholder { /* Mozilla Firefox 19+ */ color: #000; opacity: 0.54; } .searchBox :-ms-input-placeholder { /* Internet Explorer 10-11 */ color: #757575; } .widget-item-control { margin-top: 0px; } .section { margin: 0; padding: 0; } #sidebar-top { border: 1px solid #eee; } #sidebar-top > div { margin: 16px 0; } .widget ul { line-height: 1.6; } /*main post*/ .post { margin-bottom:30px; } #main .post .title { margin: 0; } #main .post .title a { color: #212121; color: rgba(0,0,0,.87); font-weight: normal; font-size: 24px; } #main .post .title a:hover { text-decoration:none; color:#4184F3; } .message, #main .post .post-header { margin: 0; padding: 0; } #main .post .post-header .caption, #main .post .post-header .labels-caption, #main .post .post-footer .caption, #main .post .post-footer .labels-caption { color: #444; font-weight: 500; } #main .tr-caption-container td { text-align: left; } #main .post .tr-caption { color: #757575; color: rgba(0,0,0,.54); display: block; max-width: 560px; padding-bottom: 20px; } #main .post .tr-caption-container { line-height: 24px; margin: -1px 0 0 0 !important; padding: 4px 0; text-align: left; } #main .post .post-header .published{ font-size:11px; font-weight:bold; } .post-header .publishdate { font-size: 17px; font-weight:normal; color: #757575; color: rgba(0,0,0,.54); } #main .post .post-footer{ font-size:12px; padding-bottom: 21px; } .label-footer { margin-bottom: 12px; margin-top: 12px; } .comment-img { margin-right: 16px; opacity: 0.54; vertical-align: middle; } #main .post .post-header .published { margin-bottom: 40px; margin-top: -2px; } .post .post-content { color: #212121; color: rgba(0,0,0,.87); font-size: 17px; margin: 25px 0 36px 0; line-height: 32px; word-wrap: break-word; } .post-body .post-content ul, .post-body .post-content ol { margin: 16px 0; padding: 0 48px; } .post-summary { display: none; } /* Capture section headers. */ .post-content br + br + b, .post-content .space + .space + b, .post-content .separator + b { display: inline-block; margin-bottom: 8px; margin-top: 24px; } .post-content li { line-height: 32px; } /* Override all post images/videos to left align. */ .post-content .separator, .post-content > div { text-align: left; } .post-content .separator > a, .post-content .separator > span { margin-left: 0 !important; } .post-content img { max-width: 100%; } .post-content .tr-caption-container img { margin-bottom: 12px; } .post-content iframe, .post-content embed { max-width: 100%; } .post-content .carousel-container { margin-bottom: 48px; } #main .post-content b { font-weight: 500; } /* These are the main paragraph spacing tweaks. */ #main .post-content br { content: ' '; display: block; padding: 4px; } .post-content .space { display: block; height: 8px; } .post-content iframe + .space, .post-content iframe + br { padding: 0 !important; } #main .post .jump-link { margin-bottom:10px; } .post-content img, .post-content iframe { margin: 30px 0 20px 0; } .post-content > img:first-child, .post-content > iframe:first-child { margin-top: 0; } .col-right .section { padding: 0 16px; } #aside { background:#fff; border:1px solid #eee; border-top: 0; } #aside .widget { margin:0; } #aside .widget h2, #ArchiveList .toggle + a.post-count-link { color: #212121; color: rgba(0,0,0,.87); font-weight: 400 !important; margin: 0; } #ArchiveList .toggle { float: right; } #ArchiveList .toggle .material-icons { padding-top: 4px; } #sidebar .tab { cursor: pointer; } #sidebar .tab .arrow { display: inline-block; float: right; } #sidebar .tab .icon { display: inline-block; vertical-align: top; height: 24px; width: 24px; margin-right: 13px; margin-left: -1px; margin-top: 1px; color: #757575; color: rgba(0,0,0,.54); } #sidebar .widget-content > :first-child { padding-top: 8px; } #sidebar .active .tab .arrow { -ms-transform: rotate(180deg); transform: rotate(180deg); } #sidebar .arrow { color: #757575; color: rgba(0,0,0,.54); } #sidebar .widget h2 { font-size: 14px; line-height: 24px; display: inline-block; } #sidebar .widget .BlogArchive { padding-bottom: 8px; } #sidebar .widget { border-bottom: 1px solid #eee; box-shadow: 0px 1px 0 white; margin-bottom: 0; padding: 14px 0; min-height: 20px; } #sidebar .widget:last-child { border-bottom: none; box-shadow: none; margin-bottom: 0; } #sidebar ul { margin: 0; padding: 0; } #sidebar ul li { list-style:none; padding:0; } #sidebar ul li a { line-height: 32px; } #sidebar .archive { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAYAAADzoH0MAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAE1JREFUeNpiNDY23s9AAWBioBCwYBM8c+YMVsUmJibEGYBNMS5DaeMFfDYSZQA2v9I3FrB5AZeriI4FmnrBccCT8mhmGs1MwyAzAQQYAKEWG9zm9QFEAAAAAElFTkSuQmCC"); height: 24px; line-height: 24px; padding-left: 30px; } #sidebar .labels { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUxJREFUeNpiNDY23s9AAMycOfM7UF05kHkZmzwTMkdSUhKrIcXFxZy3bt3qBjIN8RrS09PDsHnzZjCNDr58+cKQlpbGDjSoHcg1w2oIyAUODg5gARCNzUVIBrUCuVYYhjx//pzhwIEDYAEQDeJjA1CDWIAGNQK59jBxRuSABbkAlwHIgIeHh2HWrFn/1NTU2oDcvSgBS4wBSC5iArqoCsj1YGIgEyAZVMoEchqlBjEB/cZAiUHg2AEGznpKDAImxOeM////B4VLKtBvEUCngZ1ILKivr3/u6+ubBzJAGZQ9gC5aQoqLgAY8BhkAZL4BuQQkxgXE34A4BuiiZEIuAhrwEGhAEZD5DpzYoIaA2UAM4kQADUrHZRDUgAIg8wO2XAwzbQXQa5OweQ1owB10AyA6gS7BgX1u3ry5397eHow3bdo0EyjGi00tQIABANPgyAH1q1eaAAAAAElFTkSuQmCC"); height: 20px; line-height: 20px; padding-left: 30px; } #sidebar .rss a { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX5JREFUeNqsVDGSgkAQHL2rIiIikohIc/EBRkbwAIwuwgfwAXiAD9AHSI7kEkECRCb6AIyINDLx7K0aa6kT7uq0q7YYtnZ7umdnt7darXbr9Zpegeu61DNNc0dvwCcH4/GYJpMJnc9nOhwOVJbl/4hAAokMECZJQtvt9k+kH7qufyEYDAakqqqYxFdRFBqNRmTbNg2HQ0rTlK7XayvR0xqBdDqdkuM4dE/0ULhYLOh4PHYrknG5XGi/31MYhuL/nkwonM1mlGUZ1XXdrsiyLGEDhY7juJEZ1u5tIixDGdYhmYw+B7CAzPP5nDabjdgIAgCksMX1832/3drtdqPT6SQWapomiGEFNkDEdpDMMAzK81ys/7XYy+XyoQgq2WoURSIJ2iIIgp/WZCCTvFm2wgeAU31aI3Q2GhIDMeB53qPYPIcm5VrxXIOIOxsDMStjVawAc1VViRgN22lNBiuQN3GR+SY07hpOoStmFQAKXRRFY93bnpG+fONfedi+BRgAbkS8Fxp7QQIAAAAASUVORK5CYII="); } #sidebar .subscription a { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAALCAYAAACZIGYHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUBJREFUeNrMkSGLAlEUhb+ZB4JFi8mx2cz+ApvhRUGTcUCrNqNJDYIi+DO0GUwmQXDK2DSIoGgZcSaIjDrzwrK4ssvChj1w0733O+fdp+m6PozH4yQSCfb7Pa7r8pOi0SjJZBLP8zgej4gAIMvlMuPxmADIYrHger1+C6lUKmo+NJ/NZojb7SZDWiwWo1qtks1msW2bw+HwZdkwDHq9HvV6nel0SqvVYrvdIh6Ph3Qch+VyqRYLhQJSSjRNw7IsfN9XgGKxSLfbJZfL0e/3aTabrFYr7vc7IujLcOh8PqunrNdr0uk0pVKJVCpFJBJRgEajweVyod1uMxgM2O12BAGUgRbU8DV2JpOhVquRz+cRQii3+XxOp9NRN3jVR5LPOp1OjEYjlSL8hclkgmmabDabt4d+m+S30vkD/R/IU4ABAPTZgnZdmG/PAAAAAElFTkSuQmCC"); } #sidebar-bottom { background: #f5f5f5; border-top:1px solid #eee; } #sidebar-bottom .widget { border-bottom: 1px solid #e0e0e0; padding: 15px 0; text-align: center; } #sidebar-bottom > div:last-child { border-bottom: 0; } #sidebar-bottom .text { line-height: 20px; } /* Home, forward, and backward pagination. */ .blog-pager { border-top : 1px #e0e0e0 solid; padding-top: 10px; margin-top: 15px; text-align: right !important; } #blog-pager { margin-botom: 0; margin-top: -14px; padding: 16px 0 0 0; } #blog-pager a { display: inline-block; } .blog-pager i.disabled { opacity: 0.2 !important; } .blog-pager i { color: black; margin-left: 16px; opacity: 0.54; } .blog-pager i:hover, .blog-pager i:active { opacity: 0.87; } #blog-pager-older-link, #blog-pager-newer-link { float: none; } .gplus-profile { background-color: #fafafa; border: 1px solid #eee; overflow: hidden; width: 212px; } .gplus-profile-inner { margin-left: -1px; margin-top: -1px; } /* Sidebar follow buttons. */ .followgooglewrapper { padding: 12px 0 0 0; } .loading { visibility: hidden; } .detail-page .post-footer .cmt_iframe_holder { padding-top: 40px !important; } /** Desktop **/ @media (max-width: 900px) { .col-right { display: none; } .col-main { margin-right: 0; min-width: initial; } .footer-outer { display: none; } .cols-wrapper { min-width: initial; } .google-footer-outer { background-color: #f5f5f5; } } /** Tablet **/ @media (max-width: 712px) { .header-outer, .cols-wrapper, .footer-outer, .google-footer-outer { padding: 0 40px; } } /* An extra breakpoint accommodating for long blog titles. */ @media (max-width: 600px) { .header-left { height: 100%; position: initial; top: inherit; margin-top: 0; -webkit-transform: initial; transform: initial; } .header-title { margin-top: 18px; } .header-inner { height: auto; margin-bottom: 32px; margin-top: 32px; } .header-desc { margin-top: 12px; } .header-inner .google-logo { height: 40px; margin-top: 3px; } .header-inner .google-logo img { height: 42px; } .header-title h2 { font-size: 32px; line-height: 40px; } } /** Mobile/small desktop window; also landscape. **/ @media (max-width: 480px), (max-height: 480px) { .header-outer, .cols-wrapper, .footer-outer, .google-footer-outer { padding: 0 16px; } .cols-wrapper { margin-top: 0; } .post-header .publishdate, .post .post-content { font-size: 16px; } .post .post-content { line-height: 28px; margin-bottom: 30px; } .post { margin-top: 30px; } .byline-author { display: block; font-size: 12px; line-height: 24px; margin-top: 6px; } #main .post .title a { font-weight: 500; color: #4c4c4c; color: rgba(0,0,0,.70); } #main .post .post-header { padding-bottom: 12px; } #main .post .post-header .published { margin-bottom: -8px; margin-top: 3px; } .post .read-more { display: block; margin-top: 14px; } .post .tr-caption { font-size: 12px; } #main .post .title a { font-size: 20px; line-height: 30px; } .post-content iframe { /* iframe won't keep aspect ratio when scaled down. */ max-height: 240px; } .post-content .separator img, .post-content .tr-caption-container img, .post-content iframe { margin-left: -16px; max-width: inherit; width: calc(100% + 32px); } .post-content table, .post-content td { width: 100%; } #blog-pager { margin: 0; padding: 16px 0; } /** List page tweaks. **/ .list-page .post-original { display: none; } .list-page .post-summary { display: block; } .list-page .comment-container { display: none; } .list-page #blog-pager { padding-top: 0; border: 0; margin-top: -8px; } .list-page .label-footer { display: none; } .list-page #main .post .post-footer { border-bottom: 1px solid #eee; margin: -16px 0 0 0; padding: 0 0 20px 0; } .list-page .post .share { display: none; } /** Detail page tweaks. **/ .detail-page .post-footer .cmt_iframe_holder { padding-top: 32px !important; } .detail-page .label-footer { margin-bottom: 0; } .detail-page #main .post .post-footer { padding-bottom: 0; } .detail-page #comments { display: none; } } [data-about-pullquote], [data-is-preview], [data-about-syndication] { display: none; } </style> <noscript> <style> .loading { visibility: visible }</style> </noscript> <!-- Google tag (gtag.js) --> <script async='true' src='https://www.googletagmanager.com/gtag/js?id=G-838ZCPQWM6'></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-838ZCPQWM6'); </script> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=15045980&zx=8efcf30c-2eb6-4b0e-a577-cfb7e3bcd454' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=15045980&zx=8efcf30c-2eb6-4b0e-a577-cfb7e3bcd454' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> </head> <body> <script type='text/javascript'> //<![CDATA[ var axel = Math.random() + ""; var a = axel * 10000000000000; document.write('<iframe src="https://2542116.fls.doubleclick.net/activityi;src=2542116;type=gblog;cat=googl0;ord=ord=' + a + '?" width="1" height="1" frameborder="0" style="display:none"></iframe>'); //]]> </script> <noscript> <img alt='' height='1' src='https://ad.doubleclick.net/ddm/activity/src=2542116;type=gblog;cat=googl0;ord=1?' width='1'/> </noscript> <!-- Header --> <div class='header-outer'> <div class='header-inner'> <div class='section' id='header'><div class='widget Header' data-version='1' id='Header1'> <div class='header-left'> <div class='header-title'> <a class='google-logo' href='https://testing.googleblog.com/'> <img height='50' src='https://www.gstatic.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png'/> </a> <a href='/.'> <h2> Testing Blog </h2> </a> </div> <div class='header-desc'> </div> </div> </div></div> </div> </div> <!-- all content wrapper start --> <div class='cols-wrapper loading'> <div class='col-main-wrapper'> <div class='col-main'> <div class='section' id='main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='post' data-id='4423389607868514209' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/12/httptwittercomgoogletesting.html' itemprop='url' title='http://twitter.com/googletesting'> http://twitter.com/googletesting </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Monday, December 14, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> Google Testing Blog is now live on twitter. Follow us here: <a href="http://twitter.com/googletesting">http://twitter.com/googletesting</a><div><br /></div><div>By Patrick Copeland</div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> Google Testing Blog is now live on twitter. Follow us here: <a href="http://twitter.com/googletesting">http://twitter.com/googletesting</a><div><br /></div><div>By Patrick Copeland</div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:http://twitter.com/googletesting&url=https://testing.googleblog.com/2009/12/httptwittercomgoogletesting.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/12/httptwittercomgoogletesting.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/12/httptwittercomgoogletesting.html#comments' style='font-weight: 500; text-decoration: underline;'>1 comment</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/12/httptwittercomgoogletesting.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Patrick%20Copeland' rel='tag'> Patrick Copeland </a> </span> </div> </div> </div> <div class='post' data-id='2594170400126695993' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager-cont.html' itemprop='url' title='"If you were a brand new QA manager ..." (cont)'> "If you were a brand new QA manager ..." (cont) </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Friday, December 04, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By James A. Whittaker<br /><br /><div>More thoughts:</div><div><br /></div><div><b>Understand your orgs release process and priorities</b></div><div>Late cycle pre-release testing is the most nerve racking part of the entire development cycle. Test managers have to strike a balance between doing the right testing and ensuring a harmonious release. I suggest attending all the dev meetings, but certainly as release approaches you shouldn't miss a single one. Pay close attention to their worries and concerns. Nightmare scenarios have a tendency to surface late in the process. Add test cases to your verification suite to ensure these scenarios won't happen. </div><div><br /></div><div>The key here is to get late cycle pre-release testing right without any surprises. Developers can get skittish so make sure they understand your test plan going into the final push. The trick isn't to defer to development as to how to perform release testing but to make sure they are on-board with your plan. I find that at Google increasing the team's focus on manual testing is wholeheartedly welcomed by the dev team. Find your dev team's comfort zone and strike a balance between doing the right testing and making the final hours/days as wrinkle-free as possible. </div><div><br /></div><div><b>Question your testing process</b></div><div>Start by reading every test case and reviewing all automation. Can you map these test cases back to the test plan? How many tests do you have per component? Per feature? If a bug is found outside the testing process did you create a test case for it? Do you have a process to fix or deprecate broken or outdated test cases? </div><div><br /></div><div>As a test manager the completeness and thoroughness of the set of tests is your job. You may not be writing or running a lot of tests, but you should have them all in your head and be the first to spot gaps. It should be something a new manager tackles early and stays on top of at all times. </div><div><br /></div><div><b>Look for ways to innovate</b></div><div>The easiest way to look good in the eyes of developers is to maintain the status quo. Many development managers appreciate a docile and subservient test team. Many of them like a predictable and easily understood testing practice. It's one less thing to worry about (even in the face of obvious inefficiencies the familiar path is often the most well worn). </div><div><br /></div><div>As a new manager it is your job not to let them off so easy! You should make a list of the parts of the process that concern you and the parts that seem overly hard or inefficient. These are the places to apply innovation. Prepare for nervousness from the developer ranks, but do yourself and the industry a favor and place some bets for the long term. </div><div><br /></div><div>There is no advice I have found universally applicable concerning how to best foster innovation. What works for me is to find the stars on your team and make sure they are working on something they can be passionate about. As a manager this is the single most important thing you can do to increase productivity and foster innovation. </div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By James A. Whittaker<br /><br /><div>More thoughts:</div><div><br /></div><div><b>Understand your orgs release process and priorities</b></div><div>Late cycle pre-release testing is the most nerve racking part of the entire development cycle. Test managers have to strike a balance between doing the right testing and ensuring a harmonious release. I suggest attending all the dev meetings, but certainly as release approaches you shouldn't miss a single one. Pay close attention to their worries and concerns. Nightmare scenarios have a tendency to surface late in the process. Add test cases to your verification suite to ensure these scenarios won't happen. </div><div><br /></div><div>The key here is to get late cycle pre-release testing right without any surprises. Developers can get skittish so make sure they understand your test plan going into the final push. The trick isn't to defer to development as to how to perform release testing but to make sure they are on-board with your plan. I find that at Google increasing the team's focus on manual testing is wholeheartedly welcomed by the dev team. Find your dev team's comfort zone and strike a balance between doing the right testing and making the final hours/days as wrinkle-free as possible. </div><div><br /></div><div><b>Question your testing process</b></div><div>Start by reading every test case and reviewing all automation. Can you map these test cases back to the test plan? How many tests do you have per component? Per feature? If a bug is found outside the testing process did you create a test case for it? Do you have a process to fix or deprecate broken or outdated test cases? </div><div><br /></div><div>As a test manager the completeness and thoroughness of the set of tests is your job. You may not be writing or running a lot of tests, but you should have them all in your head and be the first to spot gaps. It should be something a new manager tackles early and stays on top of at all times. </div><div><br /></div><div><b>Look for ways to innovate</b></div><div>The easiest way to look good in the eyes of developers is to maintain the status quo. Many development managers appreciate a docile and subservient test team. Many of them like a predictable and easily understood testing practice. It's one less thing to worry about (even in the face of obvious inefficiencies the familiar path is often the most well worn). </div><div><br /></div><div>As a new manager it is your job not to let them off so easy! You should make a list of the parts of the process that concern you and the parts that seem overly hard or inefficient. These are the places to apply innovation. Prepare for nervousness from the developer ranks, but do yourself and the industry a favor and place some bets for the long term. </div><div><br /></div><div>There is no advice I have found universally applicable concerning how to best foster innovation. What works for me is to find the stars on your team and make sure they are working on something they can be passionate about. As a manager this is the single most important thing you can do to increase productivity and foster innovation. </div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:"If you were a brand new QA manager ..." (cont)&url=https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager-cont.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager-cont.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager-cont.html#comments' style='font-weight: 500; text-decoration: underline;'>5 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager-cont.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/James%20Whittaker' rel='tag'> James Whittaker </a> </span> </div> </div> </div> <div class='post' data-id='5603157247607195678' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager.html' itemprop='url' title='"If you were a brand new QA manager ..."'> "If you were a brand new QA manager ..." </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, December 02, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By James A. Whittaker<div><br /></div><div>I got this question in email this morning from a reader:<div><br /></div><div>"<span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; border-collapse: collapse; ">I am a test supervisor at --- and was promoted to a QA management position yesterday. I'm excited and terrified, so I have been thinking about how to organize the thought in my mind. After attending StarWest and following your blog for a while now, I am very interested in your opinion.</span></div><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; border-collapse: collapse; "><br />If you were a brand new QA Manager, and you knew what you know now, what are the top 5-10 things you would focus on?<span class="Apple-style-span" style="border-collapse: separate; font-family: Georgia, serif; font-size: 16px; ">"</span></span><div><br /></div><div>I am flattered by the confidence but in the event it is misplaced I wanted to answer this question publicly and invite readers to chime in with their own experiences. Besides, I am curious as to other opinions because I live with this same excitement and terror every day and could use a little advice myself. Here's my first couple and I'll add some more in future posts (unless of course you guys beat me to it).</div><div><br /></div><div><b>Start living with your product, get passionate about it</b></div><div>Drink your product's kool-aid, memorize the sales pitch, understand it's competitive advantages but retain your skepticism. Test/QA managers should be as passionate about the product as dev managers but we need to temper our passion with <i>proof</i>. Make sure the test team never stops testing the functionality represented by the sales pitch. </div><div><br /></div><div>Furthermore, part of living with your product is being a user yourself. I now live without a laptop and exclusively use my Chrome OS Netbook for my day to day work. As people see me with it in the hallways, I get to recite its sales pitch many times every day. Great practice. I also get to live with its inadequacies and take note of the things it has yet to master. This is great discussion fodder with devs and other stakeholders and also forces me to consider competitive products. When I can't do something important on my Chrome OS Netbook, I have to use a competing product and this spawns healthy discussions about how users will perceive our product's downside and how we can truthfully communicate the pros and cons of our product to customers. Every day becomes a deep dive into my product <i>as an actual user</i>. </div><div><br /></div><div>This is a great way to start off on a new product. </div><div><br /></div><div><b>Really focus on the test plan, make it an early priority</b></div><div>If you are taking over an existing role as test manager for an existing product chances are that a test plan already exists and chances are that test plan is inadequate. I'm not being unkind to your predecessor here, I am just being truthful. Most test plans are transitory docs. </div><div><br /></div><div>Now let me explain what I mean by that. Testers are quick to complain about inadequate design docs: that devs throw together a quick design doc or diagram but once they start coding, that design stagnates as the code takes on a life of its own. Soon the code does not match the design and the documentation is unreliable. If this is not your experience, congratulations but I find it far more the norm than design docs that are continually updated. </div><div><br /></div><div>Testers love to complain about this. "How can I test a product without a full description of what the product does?" But don't we often do the same thing with respect to our test plans? We throw together a quick test plan but as we start writing test cases (automated or manual) they take on a life of their own. Soon the test cases diverge from the test plan as we chase new development and our experience develops new testing insight. The test plan has just become like the design docs: a has-been document. </div><div><br /></div><div>You're a new test manager now, make fixing these documents one of your first priorities. You'll get to know your product's functionality and you'll see holes in the current test infrastructure that will need plugging. Plus, you'll have a basis to communicate with dev managers and show them you are taking quality seriously. Dev managers at Google love a good test plan, it gives them confidence you know what you are doing. </div><div><br /></div><div>Coming up next:</div><div><br /></div><div>Understand your orgs release process and priorities</div><div>Question your testing process</div><div>Look for ways to innovate</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By James A. Whittaker<div><br /></div><div>I got this question in email this morning from a reader:<div><br /></div><div>"<span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; border-collapse: collapse; ">I am a test supervisor at --- and was promoted to a QA management position yesterday. I'm excited and terrified, so I have been thinking about how to organize the thought in my mind. After attending StarWest and following your blog for a while now, I am very interested in your opinion.</span></div><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; border-collapse: collapse; "><br />If you were a brand new QA Manager, and you knew what you know now, what are the top 5-10 things you would focus on?<span class="Apple-style-span" style="border-collapse: separate; font-family: Georgia, serif; font-size: 16px; ">"</span></span><div><br /></div><div>I am flattered by the confidence but in the event it is misplaced I wanted to answer this question publicly and invite readers to chime in with their own experiences. Besides, I am curious as to other opinions because I live with this same excitement and terror every day and could use a little advice myself. Here's my first couple and I'll add some more in future posts (unless of course you guys beat me to it).</div><div><br /></div><div><b>Start living with your product, get passionate about it</b></div><div>Drink your product's kool-aid, memorize the sales pitch, understand it's competitive advantages but retain your skepticism. Test/QA managers should be as passionate about the product as dev managers but we need to temper our passion with <i>proof</i>. Make sure the test team never stops testing the functionality represented by the sales pitch. </div><div><br /></div><div>Furthermore, part of living with your product is being a user yourself. I now live without a laptop and exclusively use my Chrome OS Netbook for my day to day work. As people see me with it in the hallways, I get to recite its sales pitch many times every day. Great practice. I also get to live with its inadequacies and take note of the things it has yet to master. This is great discussion fodder with devs and other stakeholders and also forces me to consider competitive products. When I can't do something important on my Chrome OS Netbook, I have to use a competing product and this spawns healthy discussions about how users will perceive our product's downside and how we can truthfully communicate the pros and cons of our product to customers. Every day becomes a deep dive into my product <i>as an actual user</i>. </div><div><br /></div><div>This is a great way to start off on a new product. </div><div><br /></div><div><b>Really focus on the test plan, make it an early priority</b></div><div>If you are taking over an existing role as test manager for an existing product chances are that a test plan already exists and chances are that test plan is inadequate. I'm not being unkind to your predecessor here, I am just being truthful. Most test plans are transitory docs. </div><div><br /></div><div>Now let me explain what I mean by that. Testers are quick to complain about inadequate design docs: that devs throw together a quick design doc or diagram but once they start coding, that design stagnates as the code takes on a life of its own. Soon the code does not match the design and the documentation is unreliable. If this is not your experience, congratulations but I find it far more the norm than design docs that are continually updated. </div><div><br /></div><div>Testers love to complain about this. "How can I test a product without a full description of what the product does?" But don't we often do the same thing with respect to our test plans? We throw together a quick test plan but as we start writing test cases (automated or manual) they take on a life of their own. Soon the test cases diverge from the test plan as we chase new development and our experience develops new testing insight. The test plan has just become like the design docs: a has-been document. </div><div><br /></div><div>You're a new test manager now, make fixing these documents one of your first priorities. You'll get to know your product's functionality and you'll see holes in the current test infrastructure that will need plugging. Plus, you'll have a basis to communicate with dev managers and show them you are taking quality seriously. Dev managers at Google love a good test plan, it gives them confidence you know what you are doing. </div><div><br /></div><div>Coming up next:</div><div><br /></div><div>Understand your orgs release process and priorities</div><div>Question your testing process</div><div>Look for ways to innovate</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:"If you were a brand new QA manager ..."&url=https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager.html#comments' style='font-weight: 500; text-decoration: underline;'>19 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/James%20Whittaker' rel='tag'> James Whittaker </a> </span> </div> </div> </div> <div class='post' data-id='5455685021463221202' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/11/testing-chrome-os.html' itemprop='url' title='Testing Chrome OS'> Testing Chrome OS </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Thursday, November 19, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By Julian Harty<br /> <div> <br /></div> <div> The open-source launch of Chrome OS was announced today, and the source is available to download and build <a href="http://www.chromium.org/chromium-os">http://www.chromium.org/chromium-os</a>. The entire project, including testing, is being open-sourced and made available for scrutiny and to help others to both contribute and learn from our experiences.<br /> <div> <a href="http://www.chromium.org/chromium-os"></a><br /> The test engineering team haven't been idle - we're a small, international team and as a result we're having to be innovative in terms of our testing so we maximize our contribution to the project. We had two goals: to take care of short-term release quality and to plan an automation infrastructure that will serve the operating system for many years in the future. </div> <div> <br /></div> <div> Currently we're combining manual and automated testing to achieve these goals. The manual testing provides fast feedback while we're extending the use of test automation to optimize future testing. In terms of test automation, we're using a collection of open-source tools such as:</div> <div> <ul> <li>autotest <a href="http://autotest.kernel.org/">http://autotest.kernel.org/</a> for complete end-to-end testing of builds </li> <li>webdriver and selenium <a href="http://code.google.com/p/selenium/">http://code.google.com/p/selenium/</a> to test through the Chrome web browser</li> </ul> <div> There are some interesting plans and ideas afoot on how to significantly increase the testability and accessibility of Chrome OS - watch for future blog posts on these topics in the coming months!</div> <div> <br /></div> <div> We have used various approaches to design our tests, including 'tours' (mentioned in various posts on this blog). We are also applying the concept of 'attack surface' used in security testing more generally to determine what to test, from both technical and functional perspectives.</div> <div> <br /></div> </div> <div> For the launch we devised the 'early-adopters tour'; where we validated the open source build and installation instructions on a collection of netbooks purchased from local stores (we expect many of you will want to build and run Chrome OS on similar machines).</div> <div> <br /></div> <div> <div> If you're one of the early adopters - have fun building, installing and running Chrome OS and post your comments and ideas here. We hope you enjoy using Chrome OS as much as we're enjoying testing it!</div> <div> <br /></div> <div> <br /></div> </div> </div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By Julian Harty<br /> <div> <br /></div> <div> The open-source launch of Chrome OS was announced today, and the source is available to download and build <a href="http://www.chromium.org/chromium-os">http://www.chromium.org/chromium-os</a>. The entire project, including testing, is being open-sourced and made available for scrutiny and to help others to both contribute and learn from our experiences.<br /> <div> <a href="http://www.chromium.org/chromium-os"></a><br /> The test engineering team haven't been idle - we're a small, international team and as a result we're having to be innovative in terms of our testing so we maximize our contribution to the project. We had two goals: to take care of short-term release quality and to plan an automation infrastructure that will serve the operating system for many years in the future. </div> <div> <br /></div> <div> Currently we're combining manual and automated testing to achieve these goals. The manual testing provides fast feedback while we're extending the use of test automation to optimize future testing. In terms of test automation, we're using a collection of open-source tools such as:</div> <div> <ul> <li>autotest <a href="http://autotest.kernel.org/">http://autotest.kernel.org/</a> for complete end-to-end testing of builds </li> <li>webdriver and selenium <a href="http://code.google.com/p/selenium/">http://code.google.com/p/selenium/</a> to test through the Chrome web browser</li> </ul> <div> There are some interesting plans and ideas afoot on how to significantly increase the testability and accessibility of Chrome OS - watch for future blog posts on these topics in the coming months!</div> <div> <br /></div> <div> We have used various approaches to design our tests, including 'tours' (mentioned in various posts on this blog). We are also applying the concept of 'attack surface' used in security testing more generally to determine what to test, from both technical and functional perspectives.</div> <div> <br /></div> </div> <div> For the launch we devised the 'early-adopters tour'; where we validated the open source build and installation instructions on a collection of netbooks purchased from local stores (we expect many of you will want to build and run Chrome OS on similar machines).</div> <div> <br /></div> <div> <div> If you're one of the early adopters - have fun building, installing and running Chrome OS and post your comments and ideas here. We hope you enjoy using Chrome OS as much as we're enjoying testing it!</div> <div> <br /></div> <div> <br /></div> </div> </div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Testing Chrome OS&url=https://testing.googleblog.com/2009/11/testing-chrome-os.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/11/testing-chrome-os.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/11/testing-chrome-os.html#comments' style='font-weight: 500; text-decoration: underline;'>12 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/11/testing-chrome-os.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Chrome%20OS' rel='tag'> Chrome OS </a> , <a class='label' href='https://testing.googleblog.com/search/label/Julian%20Harty' rel='tag'> Julian Harty </a> </span> </div> </div> </div> <div class='post' data-id='9018312402946084820' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/11/how-to-get-started-with-tdd.html' itemprop='url' title='How to get Started with TDD'> How to get Started with TDD </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Tuesday, November 17, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By <a href="http://misko.hevery.com/">Miško Hevery</a><br /> <br /> Best way to learn TDD is to have someone show you while pairing with you. Short of that, I have set up an eclipse project for you where you can give it a try:<br /> <ol><br /> <li><span style="font-family: monospace; white-space: pre;">hg clone <a href="https://bitbucket.org/misko/misko-hevery-blog/">https://bitbucket.org/misko/misko-hevery-blog/</a></span></li> <br /> <li>Open project <span style="font-family: monospace, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="white-space: pre;">blog/tdd/01_Calculator</span></span> in Eclipse.</li> <br /> <li>It should be set up to run all tests every time you modify a file.<br /><ul><br /> <li>You may have to change the path to java if you are not an Mac OS.</li> <br /> <li>Project -> Properties -> Builders -> Test -> Edit</li> <br /> <li>Change location to your java</li> </ul> </li> <br /> <li>Right-click on Calculator.java -> Run As -> Java Application to run the calculator</li> </ol> <br /> Your mission is to make the calculator work using TDD. This is the simplest form of TDD where you don't have to mock classes or create complex interactions, so it should be a good start for beginners.<br /> <br /> TDD means:<br /> <ol><br /> <li>write a simple test, and assert something interesting in it</li> <br /> <li> implement just enough to make that tests green (nothing more, or you will get ahead of your tests)</li> <br /> <li>then write another test, rinse, and repeat.</li> </ol> <br /> I have already done all of the work of separating the behavior from the UI, so that the code is testable and properly Dependency Injected, so you don't have to worry about running into testability issues.<br /> <ul><br /> <li>Calculator.java: This is the main method and it is where all of the wiring is happening.</li> <br /> <li>CalculatorView.java: This is a view and we don't usually bother unit testing it has cyclomatic complexity of one, hence there is no logic. It either works or does not. Views are usually good candidates for end-to-end testing, which is not part of this exercise.</li> <br /> <li>CalculatorModel.java: is just a PoJo which marshals data from the Controller to the View, not much to test here.</li> <br /> <li>CalculatorController.java: is where all of your if statements will reside, and we need good tests for it.</li> </ul> <br /> I have started you off with first 'testItShouldInitializeToZero' test. Here are some ideas for next tests you may want to write.<br /> <ul><br /> <li>testItShouldConcatinateNumberPresses</li> <br /> <li>testItShouldSupportDecimalPoint</li> <br /> <li>testItShouldIgnoreSecondDecimalPoint</li> <br /> <li>testItShouldAddTwoIntegers</li> </ul> <br /> I would love to see what you will come up with and what your thoughts are, after you get the whole calculator working. I would also encourage you to post interesting corner case tests here for others to incorporate. If you want to share your code with others, I would be happy to post your solutions.<br /> <br /> Good luck!<br /> <br /> PS: I know it is trivial example, but you need to start someplace. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By <a href="http://misko.hevery.com/">Miško Hevery</a><br /> <br /> Best way to learn TDD is to have someone show you while pairing with you. Short of that, I have set up an eclipse project for you where you can give it a try:<br /> <ol><br /> <li><span style="font-family: monospace; white-space: pre;">hg clone <a href="https://bitbucket.org/misko/misko-hevery-blog/">https://bitbucket.org/misko/misko-hevery-blog/</a></span></li> <br /> <li>Open project <span style="font-family: monospace, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="white-space: pre;">blog/tdd/01_Calculator</span></span> in Eclipse.</li> <br /> <li>It should be set up to run all tests every time you modify a file.<br /><ul><br /> <li>You may have to change the path to java if you are not an Mac OS.</li> <br /> <li>Project -> Properties -> Builders -> Test -> Edit</li> <br /> <li>Change location to your java</li> </ul> </li> <br /> <li>Right-click on Calculator.java -> Run As -> Java Application to run the calculator</li> </ol> <br /> Your mission is to make the calculator work using TDD. This is the simplest form of TDD where you don't have to mock classes or create complex interactions, so it should be a good start for beginners.<br /> <br /> TDD means:<br /> <ol><br /> <li>write a simple test, and assert something interesting in it</li> <br /> <li> implement just enough to make that tests green (nothing more, or you will get ahead of your tests)</li> <br /> <li>then write another test, rinse, and repeat.</li> </ol> <br /> I have already done all of the work of separating the behavior from the UI, so that the code is testable and properly Dependency Injected, so you don't have to worry about running into testability issues.<br /> <ul><br /> <li>Calculator.java: This is the main method and it is where all of the wiring is happening.</li> <br /> <li>CalculatorView.java: This is a view and we don't usually bother unit testing it has cyclomatic complexity of one, hence there is no logic. It either works or does not. Views are usually good candidates for end-to-end testing, which is not part of this exercise.</li> <br /> <li>CalculatorModel.java: is just a PoJo which marshals data from the Controller to the View, not much to test here.</li> <br /> <li>CalculatorController.java: is where all of your if statements will reside, and we need good tests for it.</li> </ul> <br /> I have started you off with first 'testItShouldInitializeToZero' test. Here are some ideas for next tests you may want to write.<br /> <ul><br /> <li>testItShouldConcatinateNumberPresses</li> <br /> <li>testItShouldSupportDecimalPoint</li> <br /> <li>testItShouldIgnoreSecondDecimalPoint</li> <br /> <li>testItShouldAddTwoIntegers</li> </ul> <br /> I would love to see what you will come up with and what your thoughts are, after you get the whole calculator working. I would also encourage you to post interesting corner case tests here for others to incorporate. If you want to share your code with others, I would be happy to post your solutions.<br /> <br /> Good luck!<br /> <br /> PS: I know it is trivial example, but you need to start someplace. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:How to get Started with TDD&url=https://testing.googleblog.com/2009/11/how-to-get-started-with-tdd.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/11/how-to-get-started-with-tdd.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/11/how-to-get-started-with-tdd.html#comments' style='font-weight: 500; text-decoration: underline;'>10 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/11/how-to-get-started-with-tdd.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Misko%20Hevery' rel='tag'> Misko Hevery </a> </span> </div> </div> </div> <div class='post' data-id='1000184391974582058' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/10/fedex-tour.html' itemprop='url' title='The FedEx Tour'> The FedEx Tour </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Tuesday, October 20, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <span class="Apple-style-span" style="font-family: arial;"><span class="Apple-style-span" style="font-size: small;">By Rajat Dewan</span></span><br /> <div> <span class="Apple-style-span" style="font-family: verdana;"><br /></span></div> <div> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"></span><br /> <div style="background-color: white; counter-reset: __goog_page__ 0; font-family: Verdana; font-size: 10pt; line-height: normal; margin-bottom: 6px; margin-left: 6px; margin-right: 6px; margin-top: 6px; min-height: 1100px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">I appreciate James' offer to talk about how I have used the FedEx tour in Mobile Ads. Good timing too as I just found two more priority 0 bugs with the automation that the FedEx tour inspired! It was fun presenting this at STAR and I am pleased so many people attended.</span><br /> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">Mobile has been a hard problem space for testing: a humongous browser, phone, capability combination which is changing fast as the underlying technology evolves. Add to this poor tool support for the mobile platform and the rapid evolution of the device and you'll understand why I am so interested in advice on how to do better test design. We've literally tried everything, from checking screenshots of Google's properties on mobile phones to treating the phone like a collection of client apps and automating them in the UI button-clicking traditional way.</span><br /> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">Soon after James joined Google in May 2009, he started introducing the concept of tours, essentially making a point of "structured" exploratory testing. Tours presented a way for me to look at the testing problem in a radical new way. <i>Traditionally, the strategy is simple, focus on the end user interaction, and verify the expected outputs from the system under test. </i>Tours (at least for me) change this formula. They force the tester to focus on what the software does, isolating the different moving parts of software in execution, and isolating the different parts of the software at the component (and composition) level. Tours tell me to focus on testing the parts that drive the car, rather than on whether or not the car drives. This is somewhat counter intuitive I admit, that's why it is so important. The real value add of the tours comes from the fact that they guide me in testing those different parts and help me analyze how different capabilities inter-operate. Cars will always drive you off the lot, which part will break first is the real question.</span><br /> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">I think testing a car is a good analogy. As a system it's devilishly complicated, hard to automate and hard to find the right combination of factors to make it fail. However, testing the dashboard can be automated; so can testing the flow of gasoline from the fuel tank to the engine and from there to the exhaust, so can lots of other capabilities. These automated point solutions can also be combined to test a bigger piece of the whole system. It's exactly what a mechanic does when trying to diagnose a problem: he employs different strategies for testing/checking each mechanical subsystem.</span><br /> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">At STAR West, I spoke about evolving a good test strategy with the help of tours, specifically the FedEx tour. Briefly, the FedEx tour talks about tracking the movement of data and how it gets consumed and transformed by the system. It focuses on a very specific moving part, and as it turns out a crucial one for mobile. </span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">James' FedEx tour tells me to identify and track data through my system. Identifying it is the easy part: the data comes from the Ads Database and is basically the information a user sees when the ad is rendered. When I followed it through the system, I noted three (and only three) places where the data is used (either manipulated or rendered for display). I found this to be true for all 10 local versions of the Mobile Ads application. The eureka moment for me was realizing that if I validated the data at those three points, I had little else to do in order to verify any specific localized version of an ad. Add all the languages you want, I'll be ready!</span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">I was able to hook verification modules at each one of these three data inflection points. This basically meant validating data for the new Click-to-Call Ad parameters and locale specific phone number format. I was tracking how code is affecting the data at each stage, which also helps in localizing a bug better than other conventional means...I knew exactly where the failure was! For overcoming the location dependency, I mocked the GPS location parameters of the phone. As soon as I finished with the automation, I ran each ad in our database through each of the language versions verifying the integrity of the data. The only thing that was left was to visually verify rendering of the ads on the three platforms, reducing the manual tests to three (one each for Android, iPhone and Palm Pre).</span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">The FedEx tour guided me to build a succinct piece of automation and turned what could have been a huge and error prone manual test into a reusable piece of automation that will find and localize bugs quickly. We're now looking at applying the FedEx tour across ads and in other client and cloud areas in the company. Hopefully there will be more experience reports from others who have found it useful.</span></div> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">Exploratory Testing ... it's not just for manual testers anymore!</span></div> <div> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> </div> </div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <span class="Apple-style-span" style="font-family: arial;"><span class="Apple-style-span" style="font-size: small;">By Rajat Dewan</span></span><br /> <div> <span class="Apple-style-span" style="font-family: verdana;"><br /></span></div> <div> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"></span><br /> <div style="background-color: white; counter-reset: __goog_page__ 0; font-family: Verdana; font-size: 10pt; line-height: normal; margin-bottom: 6px; margin-left: 6px; margin-right: 6px; margin-top: 6px; min-height: 1100px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">I appreciate James' offer to talk about how I have used the FedEx tour in Mobile Ads. Good timing too as I just found two more priority 0 bugs with the automation that the FedEx tour inspired! It was fun presenting this at STAR and I am pleased so many people attended.</span><br /> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">Mobile has been a hard problem space for testing: a humongous browser, phone, capability combination which is changing fast as the underlying technology evolves. Add to this poor tool support for the mobile platform and the rapid evolution of the device and you'll understand why I am so interested in advice on how to do better test design. We've literally tried everything, from checking screenshots of Google's properties on mobile phones to treating the phone like a collection of client apps and automating them in the UI button-clicking traditional way.</span><br /> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">Soon after James joined Google in May 2009, he started introducing the concept of tours, essentially making a point of "structured" exploratory testing. Tours presented a way for me to look at the testing problem in a radical new way. <i>Traditionally, the strategy is simple, focus on the end user interaction, and verify the expected outputs from the system under test. </i>Tours (at least for me) change this formula. They force the tester to focus on what the software does, isolating the different moving parts of software in execution, and isolating the different parts of the software at the component (and composition) level. Tours tell me to focus on testing the parts that drive the car, rather than on whether or not the car drives. This is somewhat counter intuitive I admit, that's why it is so important. The real value add of the tours comes from the fact that they guide me in testing those different parts and help me analyze how different capabilities inter-operate. Cars will always drive you off the lot, which part will break first is the real question.</span><br /> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">I think testing a car is a good analogy. As a system it's devilishly complicated, hard to automate and hard to find the right combination of factors to make it fail. However, testing the dashboard can be automated; so can testing the flow of gasoline from the fuel tank to the engine and from there to the exhaust, so can lots of other capabilities. These automated point solutions can also be combined to test a bigger piece of the whole system. It's exactly what a mechanic does when trying to diagnose a problem: he employs different strategies for testing/checking each mechanical subsystem.</span><br /> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">At STAR West, I spoke about evolving a good test strategy with the help of tours, specifically the FedEx tour. Briefly, the FedEx tour talks about tracking the movement of data and how it gets consumed and transformed by the system. It focuses on a very specific moving part, and as it turns out a crucial one for mobile. </span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">James' FedEx tour tells me to identify and track data through my system. Identifying it is the easy part: the data comes from the Ads Database and is basically the information a user sees when the ad is rendered. When I followed it through the system, I noted three (and only three) places where the data is used (either manipulated or rendered for display). I found this to be true for all 10 local versions of the Mobile Ads application. The eureka moment for me was realizing that if I validated the data at those three points, I had little else to do in order to verify any specific localized version of an ad. Add all the languages you want, I'll be ready!</span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">I was able to hook verification modules at each one of these three data inflection points. This basically meant validating data for the new Click-to-Call Ad parameters and locale specific phone number format. I was tracking how code is affecting the data at each stage, which also helps in localizing a bug better than other conventional means...I knew exactly where the failure was! For overcoming the location dependency, I mocked the GPS location parameters of the phone. As soon as I finished with the automation, I ran each ad in our database through each of the language versions verifying the integrity of the data. The only thing that was left was to visually verify rendering of the ads on the three platforms, reducing the manual tests to three (one each for Android, iPhone and Palm Pre).</span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">The FedEx tour guided me to build a succinct piece of automation and turned what could have been a huge and error prone manual test into a reusable piece of automation that will find and localize bugs quickly. We're now looking at applying the FedEx tour across ads and in other client and cloud areas in the company. Hopefully there will be more experience reports from others who have found it useful.</span></div> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span> <div style="margin-bottom: 0px; margin-top: 0px;"> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;">Exploratory Testing ... it's not just for manual testers anymore!</span></div> <div> <span class="Apple-style-span" style="font-family: 'Times New Roman'; font-size: medium;"><br /></span></div> </div> </div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:The FedEx Tour&url=https://testing.googleblog.com/2009/10/fedex-tour.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/10/fedex-tour.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/10/fedex-tour.html#comments' style='font-weight: 500; text-decoration: underline;'>4 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/10/fedex-tour.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Rajat%20Dewan' rel='tag'> Rajat Dewan </a> </span> </div> </div> </div> <div class='post' data-id='1204733198786401529' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/10/tott-making-perfect-matcher.html' itemprop='url' title='TotT: Making a Perfect Matcher'> TotT: Making a Perfect Matcher </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Monday, October 05, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <span style="font-size:78%;">by Zhanyong G. Mock Wan in Google Kirkland<br /></span><br />In the previous episode, we showed how <strong><span style="color:#990000;">Google C++ Mocking Framework matchers can make both your test code and your test output readable</span></strong>. What if you cannot find the right matcher for the task?<br /><br />Don't settle for anything less than perfect. <span style="color:#990000;"><strong>It's easy to create a matcher that does exactly what you want, either by composing from existing matchers or by writing one from scratch</strong></span>.<br /><br />The simplest <em>composite matcher</em> is <strong>Not(m)</strong>, which negates matcher <em>m</em> as you may have guessed. We also have <strong>AnyOf(m1, ..., mn)</strong> for <strong>OR</strong>-ing and <strong>AllOf(m1, ..., mn)</strong> for <strong>AND</strong>-ing. Combining them wisely and you can get a lot done. For example,<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;">EXPECT_THAT(new_code, <b>AnyOf</b>(StartsWith(“// Tests”)),<br /> <b>Not</b>(ContainsRegex(“TODO.*intern”))));<br /></span></p>could generate a message like:<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;"><b>Expected:</b> (starts with “// Tests”) or<br /> (doesn't contain regular expression “TODO.*intern”)<br /><b>Actual:</b> “/* TODO: hire an intern. */ int main() {}”<br /></span></p>If the matcher expression gets too complex, or your matcher logic cannot be expressed in terms of existing matchers, you can use plain C++. <span style="color:#990000;"><strong>The MATCHER macro lets you define a named matcher</strong></span>:<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;"><b>MATCHER</b>(<b>IsEven</b>, “”) { return (<b>arg</b> % 2) == 0; }<br /></span></p>allows you to write <strong>EXPECT_THAT(paren_num, IsEven())</strong> to verify that paren_num is divisible by two. The special variable arg refers to the value being validated (paren_num in this case) – it is <em>not</em> a global variable.<br /><br />You can put <em>any code</em> between {} to validate arg, as long as it returns a bool value.<br /><br />The empty string “” tells Google C++ Mocking Framework to <em>automatically generate</em> the matcher's description from its name (therefore you'll see “<span style="font-family:courier new;">Expected: is even</span>” when the match fails). As long as you pick a descriptive name, you get a good description for free.<br /><br /><span style="color:#990000;"><strong>You can also give multiple parameters to a matcher, or customize its description</strong></span>. The code:<br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;">// P2 means the matcher has 2 parameters. Their names are low and high.<br /><br /><b>MATCHER_P2</b>(<b>InClosedRange</b>, <b>low</b>, <b>high</b>, “is in range [<b>%(low)s</b>, <b>%(high)s</b>]”) {<br /> return low <= arg && arg <= high;<br />}<br />...<br />EXPECT_THAT(my_age, <b>InClosedRange</b>(adult_min, penalty_to_withdraw_401k)); </span></p>may print:<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;"><b>Expected:</b> is in range [18, 60]<br /> <b>Actual:</b> 2</span><br /></p>(No, that's not my real age.) Note how you can use Python-style interpolation in the description string to print the matcher parameters.<br />You may wonder why we haven't seen any types in the examples. Rest assured that all the code we showed you is type-safe. <strong><span style="color:#990000;">Google C++ Mocking Framework uses compiler type inference to “write” the matcher parameter types for you</span></strong>, so that you can spend the time on actually writing tests – or finding your perfect match.<br /><br /><a href="http://code.google.com/intl/de-CH/testing/TotT-2009-10-05.pdf">Toilet-Friendly Version</a> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <span style="font-size:78%;">by Zhanyong G. Mock Wan in Google Kirkland<br /></span><br />In the previous episode, we showed how <strong><span style="color:#990000;">Google C++ Mocking Framework matchers can make both your test code and your test output readable</span></strong>. What if you cannot find the right matcher for the task?<br /><br />Don't settle for anything less than perfect. <span style="color:#990000;"><strong>It's easy to create a matcher that does exactly what you want, either by composing from existing matchers or by writing one from scratch</strong></span>.<br /><br />The simplest <em>composite matcher</em> is <strong>Not(m)</strong>, which negates matcher <em>m</em> as you may have guessed. We also have <strong>AnyOf(m1, ..., mn)</strong> for <strong>OR</strong>-ing and <strong>AllOf(m1, ..., mn)</strong> for <strong>AND</strong>-ing. Combining them wisely and you can get a lot done. For example,<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;">EXPECT_THAT(new_code, <b>AnyOf</b>(StartsWith(“// Tests”)),<br /> <b>Not</b>(ContainsRegex(“TODO.*intern”))));<br /></span></p>could generate a message like:<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;"><b>Expected:</b> (starts with “// Tests”) or<br /> (doesn't contain regular expression “TODO.*intern”)<br /><b>Actual:</b> “/* TODO: hire an intern. */ int main() {}”<br /></span></p>If the matcher expression gets too complex, or your matcher logic cannot be expressed in terms of existing matchers, you can use plain C++. <span style="color:#990000;"><strong>The MATCHER macro lets you define a named matcher</strong></span>:<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;"><b>MATCHER</b>(<b>IsEven</b>, “”) { return (<b>arg</b> % 2) == 0; }<br /></span></p>allows you to write <strong>EXPECT_THAT(paren_num, IsEven())</strong> to verify that paren_num is divisible by two. The special variable arg refers to the value being validated (paren_num in this case) – it is <em>not</em> a global variable.<br /><br />You can put <em>any code</em> between {} to validate arg, as long as it returns a bool value.<br /><br />The empty string “” tells Google C++ Mocking Framework to <em>automatically generate</em> the matcher's description from its name (therefore you'll see “<span style="font-family:courier new;">Expected: is even</span>” when the match fails). As long as you pick a descriptive name, you get a good description for free.<br /><br /><span style="color:#990000;"><strong>You can also give multiple parameters to a matcher, or customize its description</strong></span>. The code:<br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;">// P2 means the matcher has 2 parameters. Their names are low and high.<br /><br /><b>MATCHER_P2</b>(<b>InClosedRange</b>, <b>low</b>, <b>high</b>, “is in range [<b>%(low)s</b>, <b>%(high)s</b>]”) {<br /> return low <= arg && arg <= high;<br />}<br />...<br />EXPECT_THAT(my_age, <b>InClosedRange</b>(adult_min, penalty_to_withdraw_401k)); </span></p>may print:<br /><br /><p style="BORDER-BOTTOM: rgb(128,128,128) 1px solid; BORDER-LEFT: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-TOP: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-RIGHT: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:courier new;"><b>Expected:</b> is in range [18, 60]<br /> <b>Actual:</b> 2</span><br /></p>(No, that's not my real age.) Note how you can use Python-style interpolation in the description string to print the matcher parameters.<br />You may wonder why we haven't seen any types in the examples. Rest assured that all the code we showed you is type-safe. <strong><span style="color:#990000;">Google C++ Mocking Framework uses compiler type inference to “write” the matcher parameter types for you</span></strong>, so that you can spend the time on actually writing tests – or finding your perfect match.<br /><br /><a href="http://code.google.com/intl/de-CH/testing/TotT-2009-10-05.pdf">Toilet-Friendly Version</a> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:TotT: Making a Perfect Matcher&url=https://testing.googleblog.com/2009/10/tott-making-perfect-matcher.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/10/tott-making-perfect-matcher.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/10/tott-making-perfect-matcher.html#comments' style='font-weight: 500; text-decoration: underline;'>2 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/10/tott-making-perfect-matcher.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/C%2B%2B' rel='tag'> C++ </a> , <a class='label' href='https://testing.googleblog.com/search/label/TotT' rel='tag'> TotT </a> , <a class='label' href='https://testing.googleblog.com/search/label/Zhanyong%20Wan' rel='tag'> Zhanyong Wan </a> </span> </div> </div> </div> <div class='post' data-id='1974718471053623145' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/10/cost-of-testing.html' itemprop='url' title='Cost of Testing'> Cost of Testing </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Friday, October 02, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By <a href="http://misko.hevery.com/">Miško Hevery</a><br /> <br /> A lot of people have been asking me lately, what is the cost of testing, so I decided, that I will try to measure it, to dispel the myth that testing takes twice as long.<br /> <br /> For the last two weeks I have been keeping track of the amount of time I spent writing tests versus the time writing production code. The number surprised even me, but after I thought about it, it makes a lot of sense. The magic number is about 10% of time spent on writing tests. Now before, you think I am nuts, let me back it up with some real numbers from a personal project I have been working on.<br /> <br /> <table border="0"><tbody></tbody><tbody> <tr><th></th><th>Total</th><th>Production</th><th>Test</th><th>Ratio</th></tr> <tr><th>Commits</th><td>1,347</td><td>1,347</td><td>1,347</td><td></td></tr> <tr><th>LOC</th><td>14,709</td><td>8,711</td><td>5,988</td><td>40.78%</td></tr> <tr><th>JavaScript LOC</th><td>10,077</td><td>6,819</td><td>3,258</td><td>32.33%</td></tr> <tr><th>Ruby LOC</th><td>4,632</td><td>1,892</td><td>2,740</td><td>59.15%</td></tr> <tr><th>Lines/Commit</th><td>10.92</td><td>6.47</td><td>4.45</td><td>40.78%</td></tr> <tr><th>Hours(estimate)</th><td>1,200</td><td>1,080</td><td>120</td><td>10.00%</td></tr> <tr><th>Hours/Commit</th><td>0.89</td><td>0.80</td><td>0.09</td><td></td></tr> <tr><th>Mins/Commit</th><td>53</td><td>48</td><td>5</td><td></td></tr> </tbody></table> <br /> Commits refers to the number of commits I have made to the repository. LOC is lines of code which is broken down by language. The ratio shows the typical breakdown between the production and test code when you test drive and it is about half, give or take a language. It is interesting to note that on average I commit about 11 lines out of which 6.5 are production and 4.5 are test. Now, keep in mind this is average, a lot of commits are large where you add a lot of code, but then there are a lot of commits where you are tweaking stuff, so the average is quite low.<br /> <br /> The number of hours spent on the project is my best estimate, as I have not kept track of these numbers. Also, the 10% breakdown comes from keeping track of my coding habits for the last two weeks of coding. But, these are my best guesses.<br /> <br /> Now when I test drive, I start with writing a test which usually takes me few minutes (about 5 minutes) to write. The test represents my scenario. I then start implementing the code to make the scenario pass, and the implementation usually takes me a lot longer (about 50 minutes). The ratio is highly asymmetrical! Why does it take me so much less time to write the scenario than it does to write the implementation given that they are about the same length? Well look at a typical test and implementation:<br /> <br /> Here is a typical test for a feature:<br /> <pre>ArrayTest.prototype.testFilter = function() { var items = ["MIsKO", {name:"john"}, ["mary"], 1234]; assertEquals(4, items.filter("").length); assertEquals(4, items.filter(undefined).length); assertEquals(1, items.filter('iSk').length); assertEquals("MIsKO", items.filter('isk')[0]); assertEquals(1, items.filter('ohn').length); assertEquals(items[1], items.filter('ohn')[0]); assertEquals(1, items.filter('ar').length); assertEquals(items[2], items.filter('ar')[0]); assertEquals(1, items.filter('34').length); assertEquals(1234, items.filter('34')[0]); assertEquals(0, items.filter("I don't exist").length); }; ArrayTest.prototype.testShouldNotFilterOnSystemData = function() { assertEquals("", "".charAt(0)); // assumption var items = [{$name:"misko"}]; assertEquals(0, items.filter("misko").length); }; ArrayTest.prototype.testFilterOnSpecificProperty = function() { var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}]; assertEquals(2, items.filter({}).length); assertEquals(2, items.filter({name:'a'}).length); assertEquals(1, items.filter({name:'b'}).length); assertEquals("abc", items.filter({name:'b'})[0].name); }; ArrayTest.prototype.testFilterOnFunction = function() { var items = [{name:"a"}, {name:"abc", done:true}]; assertEquals(1, items.filter(function(i){return i.done;}).length); }; ArrayTest.prototype.testFilterIsAndFunction = function() { var items = [{first:"misko", last:"hevery"}, {first:"mike", last:"smith"}]; assertEquals(2, items.filter({first:'', last:''}).length); assertEquals(1, items.filter({first:'', last:'hevery'}).length); assertEquals(0, items.filter({first:'mike', last:'hevery'}).length); assertEquals(1, items.filter({first:'misko', last:'hevery'}).length); assertEquals(items[0], items.filter({first:'misko', last:'hevery'})[0]); }; ArrayTest.prototype.testFilterNot = function() { var items = ["misko", "mike"]; assertEquals(1, items.filter('!isk').length); assertEquals(items[1], items.filter('!isk')[0]); };</pre> <br /> Now here is code which implements this scenario tests above:<br /> <pre>Array.prototype.filter = function(expression) { var predicates = []; predicates.check = function(value) { for (var j = 0; j < predicates.length; j++) { if(!predicates[j](value)) { return false; } } return true; }; var getter = Scope.getter; var search = function(obj, text){ if (text.charAt(0) === '!') { return !search(obj, text.substr(1)); } switch (typeof obj) { case "bolean": case "number": case "string": return ('' + obj).toLowerCase().indexOf(text) > -1; case "object": for ( var objKey in obj) { if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { return true; } } return false; case "array": for ( var i = 0; i < obj.length; i++) { if (search(obj[i], text)) { return true; } } return false; default: return false; } }; switch (typeof expression) { case "bolean": case "number": case "string": expression = {$:expression}; case "object": for (var key in expression) { if (key == '$') { (function(){ var text = (''+expression[key]).toLowerCase(); if (!text) return; predicates.push(function(value) { return search(value, text); }); })(); } else { (function(){ var path = key; var text = (''+expression[key]).toLowerCase(); if (!text) return; predicates.push(function(value) { return search(getter(value, path), text); }); })(); } } break; case "function": predicates.push(expression); break; default: return this; } var filtered = []; for ( var j = 0; j < this.length; j++) { var value = this[j]; if (predicates.check(value)) { filtered.push(value); } } return filtered; };</pre> <br /> Now, I think that if you look at these two chunks of code, it is easy to see that even though they are about the same length, one is much harder to write. The reason, why tests take so little time to write is that they are linear in nature. No loops, ifs or interdependencies with other tests. Production code is a different story, I have to create complex ifs, loops and have to make sure that the implementation works not just for one test, but all test. This is why it takes you so much longer to write production than test code. In this particular case, I remember rewriting this function three times, before I got it to work as expected. :-)<br /> <br /> So a naive answer is that writing test carries a 10% tax. But, we pay taxes in order to get something in return. Here is what I get for 10% which pays me back:<br /> <ul><br /> <li>When I implement a feature I don't have to start up the whole application and click several pages until I get to page to verify that a feature works. In this case it means that I don't have to refreshing the browser, waiting for it to load a dataset and then typing some test data and manually asserting that I got what I expected. This is immediate payback in time saved!</li> <br /> <li>Regression is almost nil. Whenever you are adding new feature you are running the risk of breaking something other then what you are working on immediately (since you are not working on it you are not actively testing it). At least once a day I have a what the @#$% moment when a change suddenly breaks a test at the opposite end of the codebase which I did not expect, and I count my lucky stars. This is worth a lot of time spent when you discover that a feature you thought was working no longer is, and by this time you have forgotten how the feature is implemented.</li> <br /> <li>Cognitive load is greatly reduced since I don't have to keep all of the assumptions about the software in my head, this makes it really easy to switch tasks or to come back to a task after a meeting, good night sleep or a weekend.</li> <br /> <li>I can refactor the code at will, keeping it from becoming stagnant, and hard to understand. This is a huge problem on large projects, where the code works, but it is really ugly and everyone is afraid to touch it. This is worth money tomorrow to keep you going.</li> </ul> <br /> These benefits translate to real value today as well as tomorrow. I write tests, because the additional benefits I get more than offset the additional cost of 10%. Even if I don't include the long term benefits, the value I get from test today are well worth it. I am faster in developing code with test. How much, well that depends on the complexity of the code. The more complex the thing you are trying to build is (more ifs/loops/dependencies) the greater the benefit of tests are.<br /> <br /> So now you understand my puzzled look when people ask me how much slower/costlier the development with tests is. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By <a href="http://misko.hevery.com/">Miško Hevery</a><br /> <br /> A lot of people have been asking me lately, what is the cost of testing, so I decided, that I will try to measure it, to dispel the myth that testing takes twice as long.<br /> <br /> For the last two weeks I have been keeping track of the amount of time I spent writing tests versus the time writing production code. The number surprised even me, but after I thought about it, it makes a lot of sense. The magic number is about 10% of time spent on writing tests. Now before, you think I am nuts, let me back it up with some real numbers from a personal project I have been working on.<br /> <br /> <table border="0"><tbody></tbody><tbody> <tr><th></th><th>Total</th><th>Production</th><th>Test</th><th>Ratio</th></tr> <tr><th>Commits</th><td>1,347</td><td>1,347</td><td>1,347</td><td></td></tr> <tr><th>LOC</th><td>14,709</td><td>8,711</td><td>5,988</td><td>40.78%</td></tr> <tr><th>JavaScript LOC</th><td>10,077</td><td>6,819</td><td>3,258</td><td>32.33%</td></tr> <tr><th>Ruby LOC</th><td>4,632</td><td>1,892</td><td>2,740</td><td>59.15%</td></tr> <tr><th>Lines/Commit</th><td>10.92</td><td>6.47</td><td>4.45</td><td>40.78%</td></tr> <tr><th>Hours(estimate)</th><td>1,200</td><td>1,080</td><td>120</td><td>10.00%</td></tr> <tr><th>Hours/Commit</th><td>0.89</td><td>0.80</td><td>0.09</td><td></td></tr> <tr><th>Mins/Commit</th><td>53</td><td>48</td><td>5</td><td></td></tr> </tbody></table> <br /> Commits refers to the number of commits I have made to the repository. LOC is lines of code which is broken down by language. The ratio shows the typical breakdown between the production and test code when you test drive and it is about half, give or take a language. It is interesting to note that on average I commit about 11 lines out of which 6.5 are production and 4.5 are test. Now, keep in mind this is average, a lot of commits are large where you add a lot of code, but then there are a lot of commits where you are tweaking stuff, so the average is quite low.<br /> <br /> The number of hours spent on the project is my best estimate, as I have not kept track of these numbers. Also, the 10% breakdown comes from keeping track of my coding habits for the last two weeks of coding. But, these are my best guesses.<br /> <br /> Now when I test drive, I start with writing a test which usually takes me few minutes (about 5 minutes) to write. The test represents my scenario. I then start implementing the code to make the scenario pass, and the implementation usually takes me a lot longer (about 50 minutes). The ratio is highly asymmetrical! Why does it take me so much less time to write the scenario than it does to write the implementation given that they are about the same length? Well look at a typical test and implementation:<br /> <br /> Here is a typical test for a feature:<br /> <pre>ArrayTest.prototype.testFilter = function() { var items = ["MIsKO", {name:"john"}, ["mary"], 1234]; assertEquals(4, items.filter("").length); assertEquals(4, items.filter(undefined).length); assertEquals(1, items.filter('iSk').length); assertEquals("MIsKO", items.filter('isk')[0]); assertEquals(1, items.filter('ohn').length); assertEquals(items[1], items.filter('ohn')[0]); assertEquals(1, items.filter('ar').length); assertEquals(items[2], items.filter('ar')[0]); assertEquals(1, items.filter('34').length); assertEquals(1234, items.filter('34')[0]); assertEquals(0, items.filter("I don't exist").length); }; ArrayTest.prototype.testShouldNotFilterOnSystemData = function() { assertEquals("", "".charAt(0)); // assumption var items = [{$name:"misko"}]; assertEquals(0, items.filter("misko").length); }; ArrayTest.prototype.testFilterOnSpecificProperty = function() { var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}]; assertEquals(2, items.filter({}).length); assertEquals(2, items.filter({name:'a'}).length); assertEquals(1, items.filter({name:'b'}).length); assertEquals("abc", items.filter({name:'b'})[0].name); }; ArrayTest.prototype.testFilterOnFunction = function() { var items = [{name:"a"}, {name:"abc", done:true}]; assertEquals(1, items.filter(function(i){return i.done;}).length); }; ArrayTest.prototype.testFilterIsAndFunction = function() { var items = [{first:"misko", last:"hevery"}, {first:"mike", last:"smith"}]; assertEquals(2, items.filter({first:'', last:''}).length); assertEquals(1, items.filter({first:'', last:'hevery'}).length); assertEquals(0, items.filter({first:'mike', last:'hevery'}).length); assertEquals(1, items.filter({first:'misko', last:'hevery'}).length); assertEquals(items[0], items.filter({first:'misko', last:'hevery'})[0]); }; ArrayTest.prototype.testFilterNot = function() { var items = ["misko", "mike"]; assertEquals(1, items.filter('!isk').length); assertEquals(items[1], items.filter('!isk')[0]); };</pre> <br /> Now here is code which implements this scenario tests above:<br /> <pre>Array.prototype.filter = function(expression) { var predicates = []; predicates.check = function(value) { for (var j = 0; j < predicates.length; j++) { if(!predicates[j](value)) { return false; } } return true; }; var getter = Scope.getter; var search = function(obj, text){ if (text.charAt(0) === '!') { return !search(obj, text.substr(1)); } switch (typeof obj) { case "bolean": case "number": case "string": return ('' + obj).toLowerCase().indexOf(text) > -1; case "object": for ( var objKey in obj) { if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { return true; } } return false; case "array": for ( var i = 0; i < obj.length; i++) { if (search(obj[i], text)) { return true; } } return false; default: return false; } }; switch (typeof expression) { case "bolean": case "number": case "string": expression = {$:expression}; case "object": for (var key in expression) { if (key == '$') { (function(){ var text = (''+expression[key]).toLowerCase(); if (!text) return; predicates.push(function(value) { return search(value, text); }); })(); } else { (function(){ var path = key; var text = (''+expression[key]).toLowerCase(); if (!text) return; predicates.push(function(value) { return search(getter(value, path), text); }); })(); } } break; case "function": predicates.push(expression); break; default: return this; } var filtered = []; for ( var j = 0; j < this.length; j++) { var value = this[j]; if (predicates.check(value)) { filtered.push(value); } } return filtered; };</pre> <br /> Now, I think that if you look at these two chunks of code, it is easy to see that even though they are about the same length, one is much harder to write. The reason, why tests take so little time to write is that they are linear in nature. No loops, ifs or interdependencies with other tests. Production code is a different story, I have to create complex ifs, loops and have to make sure that the implementation works not just for one test, but all test. This is why it takes you so much longer to write production than test code. In this particular case, I remember rewriting this function three times, before I got it to work as expected. :-)<br /> <br /> So a naive answer is that writing test carries a 10% tax. But, we pay taxes in order to get something in return. Here is what I get for 10% which pays me back:<br /> <ul><br /> <li>When I implement a feature I don't have to start up the whole application and click several pages until I get to page to verify that a feature works. In this case it means that I don't have to refreshing the browser, waiting for it to load a dataset and then typing some test data and manually asserting that I got what I expected. This is immediate payback in time saved!</li> <br /> <li>Regression is almost nil. Whenever you are adding new feature you are running the risk of breaking something other then what you are working on immediately (since you are not working on it you are not actively testing it). At least once a day I have a what the @#$% moment when a change suddenly breaks a test at the opposite end of the codebase which I did not expect, and I count my lucky stars. This is worth a lot of time spent when you discover that a feature you thought was working no longer is, and by this time you have forgotten how the feature is implemented.</li> <br /> <li>Cognitive load is greatly reduced since I don't have to keep all of the assumptions about the software in my head, this makes it really easy to switch tasks or to come back to a task after a meeting, good night sleep or a weekend.</li> <br /> <li>I can refactor the code at will, keeping it from becoming stagnant, and hard to understand. This is a huge problem on large projects, where the code works, but it is really ugly and everyone is afraid to touch it. This is worth money tomorrow to keep you going.</li> </ul> <br /> These benefits translate to real value today as well as tomorrow. I write tests, because the additional benefits I get more than offset the additional cost of 10%. Even if I don't include the long term benefits, the value I get from test today are well worth it. I am faster in developing code with test. How much, well that depends on the complexity of the code. The more complex the thing you are trying to build is (more ifs/loops/dependencies) the greater the benefit of tests are.<br /> <br /> So now you understand my puzzled look when people ask me how much slower/costlier the development with tests is. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Cost of Testing&url=https://testing.googleblog.com/2009/10/cost-of-testing.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/10/cost-of-testing.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/10/cost-of-testing.html#comments' style='font-weight: 500; text-decoration: underline;'>6 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/10/cost-of-testing.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Misko%20Hevery' rel='tag'> Misko Hevery </a> </span> </div> </div> </div> <div class='post' data-id='8559119593676951275' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/09/tott-literate-testing-with-matchers.html' itemprop='url' title='TotT: Literate Testing With Matchers'> TotT: Literate Testing With Matchers </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Tuesday, September 29, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <em><span style="font-size: 85%;">By Zhanyong G. Mock Wan in Google Kirkland</span></em><br /> <br /> Alright, it sounds like a good idea to verify that matchmakers can read and write. How does this concern us programmers, though?<br /> Actually, we are talking about a way of writing tests here – a way that makes both the test code and its output read like English (hence “literate”). The key to this technique is <strong><span style="color: #cc0000;">matchers</span></strong>, which <strong><span style="color: #cc0000;">are predicates that know how to describe themselves</span></strong>. For example, in Google C++ Mocking Framework, <strong>ContainsRegex</strong>(<span style="font-family: courier new;">"Ahcho+!"</span>) is a matcher that matches any string that has the regular expression <span style="font-family: courier new;">"Ahcho+!"</span> in it. Therefore, it matches <span style="font-family: courier new;">"Ahchoo!"</span> and <span style="font-family: courier new;">"Ahchoooo! Sorry."</span>, but not <span style="font-family: courier new;">"Aha!"</span>.<br /> What's this to do with test readability, anyway? It turns out that <strong><span style="color: #cc0000;">matchers</span></strong>, whose names are usually verb phrases, <strong><span style="color: #cc0000;">lend themselves easily to an assertion style that resembles natural languages</span></strong>. Namely, the assertion<br /> <br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(230,245,255); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: black; font-family: courier new;"><strong>EXPECT_THAT</strong>(value, matcher);</span></div> <br /> succeeds if <i>value</i> matches <i>matcher</i>. For example,<br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(230,245,255); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: black;">#include <gmock/gmock.h><br />using ::testing::Contains;<br />...<br />EXPECT_THAT(GetUserList(), Contains(admin_id));</span></div> <br /> verifies that the result of <span style="font-family: courier new;">GetUserList()</span> contains the administrator.<br /> <br /> Now, pretend the punctuations aren't there in the last C++ statement and read it. See what I mean?<br /> <br /> Better yet, <strong><span style="color: #cc0000;">when an EXPECT_THAT assertion fails, it will print an informative message that includes the expression being validated, its value, and the property we expect it to have</span></strong> – thanks to a matcher's ability to describe itself in human-friendly language. Therefore, not only is the test code readable, the test output it generates is readable too. For instance, the above example might produce:<br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(255,255,50); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: #660000; font-family: courier new;"><strong>Value of:</strong> GetUserList()<br /><strong>Expected:</strong> contains "yoko"<br /><strong> Actual:</strong> { "john", "paul", "george", "ringo" }</span></div> <br /> This message contains relevant information for diagnosing the problem, often without having to use a debugger.<br /> To get the same effect without using a matcher, you'd have to write something like:<br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(230,245,255); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: black;">std::vector<std::string> users = GetUserList();<br />EXPECT_TRUE(VectorContains(users, admin_id))<br /> << " GetUserList() returns " << users<br /> << " and admin_id is " << admin_id;</span></div> <br /> which is harder to write and less clear than the one-liner we saw earlier.<br /> <br /> Google C++ Mocking Framework (<a href="http://code.google.com/p/googlemock/">http://code.google.com/p/googlemock/</a>) provides dozens of matchers for validating many kinds of values: numbers, strings, STL containers, structs, etc. They all produce friendly and informative messages. See <a href="http://code.google.com/p/googlemock/wiki/CheatSheet">http://code.google.com/p/googlemock/wiki/CheatSheet</a> to learn more. If you cannot<br /> find one that matches (pun intended) your need, you can either combine existing matchers, or define your own from scratch. Both are quite easy to do. We'll show you how in another episode. Stay tuned!<br /> <br /> <a href="https://drive.google.com/file/d/0B_EQS8-2f7m5bzBmZlJ0ZEZKVzg/edit?usp=sharing">Toilet-friendly version</a> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <em><span style="font-size: 85%;">By Zhanyong G. Mock Wan in Google Kirkland</span></em><br /> <br /> Alright, it sounds like a good idea to verify that matchmakers can read and write. How does this concern us programmers, though?<br /> Actually, we are talking about a way of writing tests here – a way that makes both the test code and its output read like English (hence “literate”). The key to this technique is <strong><span style="color: #cc0000;">matchers</span></strong>, which <strong><span style="color: #cc0000;">are predicates that know how to describe themselves</span></strong>. For example, in Google C++ Mocking Framework, <strong>ContainsRegex</strong>(<span style="font-family: courier new;">"Ahcho+!"</span>) is a matcher that matches any string that has the regular expression <span style="font-family: courier new;">"Ahcho+!"</span> in it. Therefore, it matches <span style="font-family: courier new;">"Ahchoo!"</span> and <span style="font-family: courier new;">"Ahchoooo! Sorry."</span>, but not <span style="font-family: courier new;">"Aha!"</span>.<br /> What's this to do with test readability, anyway? It turns out that <strong><span style="color: #cc0000;">matchers</span></strong>, whose names are usually verb phrases, <strong><span style="color: #cc0000;">lend themselves easily to an assertion style that resembles natural languages</span></strong>. Namely, the assertion<br /> <br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(230,245,255); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: black; font-family: courier new;"><strong>EXPECT_THAT</strong>(value, matcher);</span></div> <br /> succeeds if <i>value</i> matches <i>matcher</i>. For example,<br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(230,245,255); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: black;">#include <gmock/gmock.h><br />using ::testing::Contains;<br />...<br />EXPECT_THAT(GetUserList(), Contains(admin_id));</span></div> <br /> verifies that the result of <span style="font-family: courier new;">GetUserList()</span> contains the administrator.<br /> <br /> Now, pretend the punctuations aren't there in the last C++ statement and read it. See what I mean?<br /> <br /> Better yet, <strong><span style="color: #cc0000;">when an EXPECT_THAT assertion fails, it will print an informative message that includes the expression being validated, its value, and the property we expect it to have</span></strong> – thanks to a matcher's ability to describe itself in human-friendly language. Therefore, not only is the test code readable, the test output it generates is readable too. For instance, the above example might produce:<br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(255,255,50); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: #660000; font-family: courier new;"><strong>Value of:</strong> GetUserList()<br /><strong>Expected:</strong> contains "yoko"<br /><strong> Actual:</strong> { "john", "paul", "george", "ringo" }</span></div> <br /> This message contains relevant information for diagnosing the problem, often without having to use a debugger.<br /> To get the same effect without using a matcher, you'd have to write something like:<br /> <div style="-moz-background-clip: border; -moz-background-inline-policy: continuous; -moz-background-origin: padding; background: rgb(230,245,255); border-bottom: rgb(128,128,128) 1px solid; border-left: rgb(128,128,128) 1px solid; border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; margin-left: 0.39in; margin-right: 0.39in;"> <span style="color: black;">std::vector<std::string> users = GetUserList();<br />EXPECT_TRUE(VectorContains(users, admin_id))<br /> << " GetUserList() returns " << users<br /> << " and admin_id is " << admin_id;</span></div> <br /> which is harder to write and less clear than the one-liner we saw earlier.<br /> <br /> Google C++ Mocking Framework (<a href="http://code.google.com/p/googlemock/">http://code.google.com/p/googlemock/</a>) provides dozens of matchers for validating many kinds of values: numbers, strings, STL containers, structs, etc. They all produce friendly and informative messages. See <a href="http://code.google.com/p/googlemock/wiki/CheatSheet">http://code.google.com/p/googlemock/wiki/CheatSheet</a> to learn more. If you cannot<br /> find one that matches (pun intended) your need, you can either combine existing matchers, or define your own from scratch. Both are quite easy to do. We'll show you how in another episode. Stay tuned!<br /> <br /> <a href="https://drive.google.com/file/d/0B_EQS8-2f7m5bzBmZlJ0ZEZKVzg/edit?usp=sharing">Toilet-friendly version</a> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:TotT: Literate Testing With Matchers&url=https://testing.googleblog.com/2009/09/tott-literate-testing-with-matchers.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/09/tott-literate-testing-with-matchers.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/09/tott-literate-testing-with-matchers.html#comments' style='font-weight: 500; text-decoration: underline;'>2 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/09/tott-literate-testing-with-matchers.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/C%2B%2B' rel='tag'> C++ </a> , <a class='label' href='https://testing.googleblog.com/search/label/TotT' rel='tag'> TotT </a> , <a class='label' href='https://testing.googleblog.com/search/label/Zhanyong%20Wan' rel='tag'> Zhanyong Wan </a> </span> </div> </div> </div> <div class='post' data-id='4509944480391294720' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html' itemprop='url' title='Checked exceptions I love you, but you have to go'> Checked exceptions I love you, but you have to go </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, September 16, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <div> By <a href="http://misko.hevery.com/">Miško Hevery</a></div> <div> <br /></div> Once upon a time Java created an experiment called checked-exceptions, you know, you have to declare exceptions or catch them. Since that time, no other language (I know of) has decided to copy this idea, but somehow the Java developers are in love with checked exceptions. Here, I am going to "try" to convince you that checked-exceptions, even though look like a good idea at first glance, are actually not a good idea at all:<br /> <br /> <strong>Empirical Evidence</strong><br /> <br /> Let's start with an observation of your code base. Look through your code and tell me what percentage of catch blocks do rethrow or print error? My guess is that it is in high 90s. I would go as far as 98% of catch blocks are meaningless, since they just print an error or rethrow the exception which will later be printed as an error. The reason for this is very simple. Most exceptions such as FileNotFoundException, IOException, and so on are sign that we as developers have missed a corner case. The exceptions are used as away of informing us that we, as developers, have messed up. So if we did not have checked exceptions, the exception would be throw and the main method would print it and we would be done with it (optionally we would catch all exceptions in the main log them if we are a server).<br /> <br /> Checked exceptions force me to write catch blocks which are meaningless: more code, harder to read, and higher chance that I will mess up the rethrow logic and eat the exception.<br /> <br /> <strong>Lost in Noise</strong><br /> <br /> Now lets look at the 2-5% of the catch blocks which are not rethrow and real interesting logic happens there. Those interesting bits of useful and important information is lost in the noise, since my eye has been trained to skim over the catch blocks. I would much rather have code where a catch would indicate: "pay, attention! here, something interesting is happening!", rather than, "it is just a rethrow." Now, if we did not have checked exceptions, you would write your code without catch blocks, test your code (you do test right?) and realize that under some circumstances an exception is throw and deal with it by writing the catch block. In such a case forgetting to write a catch block is no different than forgetting to write an else block of the if statement. We don't have checked ifs and yet no one misses them, so why do we need to tell developers that FileNotFound can happen. What if the developer knows for a fact that it can not happen since he has just placed the file there, and so such an exception would mean that your filesystem has just disappeared! (and your application is not place to handle that.)<br /> <br /> Checked exception make me skim the catch blocks as most are just rethrows, making it likely that you will miss something important.<br /> <br /> <strong>Unreachable Code</strong><br /> <br /> I love to write tests first and implement as a consequence of tests. In such a situation you should always have 100% coverage since you are only writing what the tests are asking for. But you don't! It is less than 100% because checked exceptions force you to write catch blocks which are impossible to execute. Check this code out:<br /> <pre>bytesToString(byte[] bytes) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { out.write(bytes); out.close() return out.toSring(); } catch (IOException e) { // This can never happen! // Should I rethrow? Eat it? Print Error? } }</pre> <br /> ByteArrayOutputStream will never throw IOException! You can look through its implementation and see that it is true! So why are you making me catch a phantom exception which can never happen and which I can not write a test for? As a result I cannot claim 100% coverage because of things outside my control.<br /> <br /> Checked exceptions create dead code which will never execute.<br /> <br /> <strong>Closures Don't Like You</strong><br /> <br /> Java does not have closures but it has visitor pattern. Let me explain with concrete example. I was creating a custom class loader and need to override load() method on MyClassLoader which throws ClassNotFoundException under some circumstances. I use ASM library which allows me to inspect Java bytecodes. The way ASM works is that it is a visitor pattern, I write visitors and as ASM parses the bytecodes it calls specific methods on my visitor implementation. One of my visitors, as it is examining bytecodes, decides that things are not right and needs to throw a ClassNotFondException which the class loader contract says it should throw. But now we have a problem. What we have on a stack is MyClassLoader -> ASMLibrary -> MyVisitor. MyVisitor wants to throw an exception which MyClassLoader expects but it can not since ClassNotFoundException is checked and ASMLibrary does not declare it (nor should it). So I have to throw RuntimeClassNotFoundException from MyVisitor which can pass through ASMLibrary which MyClassLoader can then catch and rethrow as ClassNotFoundException.<br /> <br /> Checked exception get in the way of functional programing.<br /> <br /> <strong>Lost Fidelity</strong><br /> <br /> Suppose java.sql package would be implemented with useful exception such as SqlDuplicateKeyExceptions and SqlForeignKeyViolationException and so on (we can wish) and suppose these exceptions are checked (which they are). We say that the SQL package has high fidelity of exception since each exception is to a very specific problem. Now lets say we have the same set up as before where there is some other layer between us and the SQL package, that layer can either redeclare all of the exceptions, or more likely throw its own. Let's look at an example, Hibernate is object-relational-database-mapper, which means it converts your SQL rows into java objects. So on the stack you have MyApplication -> Hibernate -> SQL. Here Hibernate is trying hard to hide the fact that you are talking to SQL so it throws HibernateExceptions instead of SQLExceptions. And here lies the problem. Your code knows that there is SQL under Hibernate and so it could have handled SqlDuplicateKeyException in some useful way, such as showing an error to the user, but Hibernate was forced to catch the exception and rethrow it as generic HibernateException. We have gone from high fidelitySqlDuplicateKeyException to low fidelity HibernateException. An so MyApplication can not do anything. Now Hibernate could have throw HibernateDuplicateKeyException but that means that Hibernate now has the same exception hierarchy as SQL and we are duplicating effort and repeating ourselves.<br /> <br /> Rethrowing checked exceptions causes you to lose fidelity and hence makes it less likely that you could do something useful with the exception later on.<br /> <br /> <strong>You can't do Anything Anyway</strong><br /> <br /> In most cases when exception is throw there is no recovery. We show a generic error to the user and log an exception so that we con file a bug and make sure that that exception will not happen again. Since 90+% of the exception are bugs in our code and all we do is log, why are we forced to rethrow it over and over again.<br /> <br /> It is rare that anything useful can be done when checked exception happens, in most case we die with error! Therefor I want that to be the default behavior of my code with no additional typing.<br /> <br /> <strong>How I deal with the code</strong><br /> <br /> Here is my strategy to deal with checked exceptions in java:<br /> <ul><br /> <li>Always catch all checked exceptions at source and rethrow them as LogRuntimeException.<br /><ul><br /> <li>LogRuntimeException is my runtime un-checked exception which says I don't care just log it.</li> <br /> <li>Here I have lost Exception fidelity.</li> </ul> </li> <br /> <li>All of my methods do not declare any exceptions</li> <br /> <li>As I discover that I need to deal with a specific exception I go back to the source where LogRuntimeException was thrown and I change it to <Specific>RuntimeException (This is rarer than you think)<br /><ul><br /> <li>I am restoring the exception fidelity only where needed.</li> </ul> </li> <br /> <li>Net effect is that when you come across a try-catch clause you better pay attention as interesting things are happening there.<br /><ul><br /> <li>Very few try-catch calluses, code is much easier to read.</li> <br /> <li>Very close to 100% test coverage as there is no dead code in my catch blocks.</li> </ul> </li> </ul> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <div> By <a href="http://misko.hevery.com/">Miško Hevery</a></div> <div> <br /></div> Once upon a time Java created an experiment called checked-exceptions, you know, you have to declare exceptions or catch them. Since that time, no other language (I know of) has decided to copy this idea, but somehow the Java developers are in love with checked exceptions. Here, I am going to "try" to convince you that checked-exceptions, even though look like a good idea at first glance, are actually not a good idea at all:<br /> <br /> <strong>Empirical Evidence</strong><br /> <br /> Let's start with an observation of your code base. Look through your code and tell me what percentage of catch blocks do rethrow or print error? My guess is that it is in high 90s. I would go as far as 98% of catch blocks are meaningless, since they just print an error or rethrow the exception which will later be printed as an error. The reason for this is very simple. Most exceptions such as FileNotFoundException, IOException, and so on are sign that we as developers have missed a corner case. The exceptions are used as away of informing us that we, as developers, have messed up. So if we did not have checked exceptions, the exception would be throw and the main method would print it and we would be done with it (optionally we would catch all exceptions in the main log them if we are a server).<br /> <br /> Checked exceptions force me to write catch blocks which are meaningless: more code, harder to read, and higher chance that I will mess up the rethrow logic and eat the exception.<br /> <br /> <strong>Lost in Noise</strong><br /> <br /> Now lets look at the 2-5% of the catch blocks which are not rethrow and real interesting logic happens there. Those interesting bits of useful and important information is lost in the noise, since my eye has been trained to skim over the catch blocks. I would much rather have code where a catch would indicate: "pay, attention! here, something interesting is happening!", rather than, "it is just a rethrow." Now, if we did not have checked exceptions, you would write your code without catch blocks, test your code (you do test right?) and realize that under some circumstances an exception is throw and deal with it by writing the catch block. In such a case forgetting to write a catch block is no different than forgetting to write an else block of the if statement. We don't have checked ifs and yet no one misses them, so why do we need to tell developers that FileNotFound can happen. What if the developer knows for a fact that it can not happen since he has just placed the file there, and so such an exception would mean that your filesystem has just disappeared! (and your application is not place to handle that.)<br /> <br /> Checked exception make me skim the catch blocks as most are just rethrows, making it likely that you will miss something important.<br /> <br /> <strong>Unreachable Code</strong><br /> <br /> I love to write tests first and implement as a consequence of tests. In such a situation you should always have 100% coverage since you are only writing what the tests are asking for. But you don't! It is less than 100% because checked exceptions force you to write catch blocks which are impossible to execute. Check this code out:<br /> <pre>bytesToString(byte[] bytes) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { out.write(bytes); out.close() return out.toSring(); } catch (IOException e) { // This can never happen! // Should I rethrow? Eat it? Print Error? } }</pre> <br /> ByteArrayOutputStream will never throw IOException! You can look through its implementation and see that it is true! So why are you making me catch a phantom exception which can never happen and which I can not write a test for? As a result I cannot claim 100% coverage because of things outside my control.<br /> <br /> Checked exceptions create dead code which will never execute.<br /> <br /> <strong>Closures Don't Like You</strong><br /> <br /> Java does not have closures but it has visitor pattern. Let me explain with concrete example. I was creating a custom class loader and need to override load() method on MyClassLoader which throws ClassNotFoundException under some circumstances. I use ASM library which allows me to inspect Java bytecodes. The way ASM works is that it is a visitor pattern, I write visitors and as ASM parses the bytecodes it calls specific methods on my visitor implementation. One of my visitors, as it is examining bytecodes, decides that things are not right and needs to throw a ClassNotFondException which the class loader contract says it should throw. But now we have a problem. What we have on a stack is MyClassLoader -> ASMLibrary -> MyVisitor. MyVisitor wants to throw an exception which MyClassLoader expects but it can not since ClassNotFoundException is checked and ASMLibrary does not declare it (nor should it). So I have to throw RuntimeClassNotFoundException from MyVisitor which can pass through ASMLibrary which MyClassLoader can then catch and rethrow as ClassNotFoundException.<br /> <br /> Checked exception get in the way of functional programing.<br /> <br /> <strong>Lost Fidelity</strong><br /> <br /> Suppose java.sql package would be implemented with useful exception such as SqlDuplicateKeyExceptions and SqlForeignKeyViolationException and so on (we can wish) and suppose these exceptions are checked (which they are). We say that the SQL package has high fidelity of exception since each exception is to a very specific problem. Now lets say we have the same set up as before where there is some other layer between us and the SQL package, that layer can either redeclare all of the exceptions, or more likely throw its own. Let's look at an example, Hibernate is object-relational-database-mapper, which means it converts your SQL rows into java objects. So on the stack you have MyApplication -> Hibernate -> SQL. Here Hibernate is trying hard to hide the fact that you are talking to SQL so it throws HibernateExceptions instead of SQLExceptions. And here lies the problem. Your code knows that there is SQL under Hibernate and so it could have handled SqlDuplicateKeyException in some useful way, such as showing an error to the user, but Hibernate was forced to catch the exception and rethrow it as generic HibernateException. We have gone from high fidelitySqlDuplicateKeyException to low fidelity HibernateException. An so MyApplication can not do anything. Now Hibernate could have throw HibernateDuplicateKeyException but that means that Hibernate now has the same exception hierarchy as SQL and we are duplicating effort and repeating ourselves.<br /> <br /> Rethrowing checked exceptions causes you to lose fidelity and hence makes it less likely that you could do something useful with the exception later on.<br /> <br /> <strong>You can't do Anything Anyway</strong><br /> <br /> In most cases when exception is throw there is no recovery. We show a generic error to the user and log an exception so that we con file a bug and make sure that that exception will not happen again. Since 90+% of the exception are bugs in our code and all we do is log, why are we forced to rethrow it over and over again.<br /> <br /> It is rare that anything useful can be done when checked exception happens, in most case we die with error! Therefor I want that to be the default behavior of my code with no additional typing.<br /> <br /> <strong>How I deal with the code</strong><br /> <br /> Here is my strategy to deal with checked exceptions in java:<br /> <ul><br /> <li>Always catch all checked exceptions at source and rethrow them as LogRuntimeException.<br /><ul><br /> <li>LogRuntimeException is my runtime un-checked exception which says I don't care just log it.</li> <br /> <li>Here I have lost Exception fidelity.</li> </ul> </li> <br /> <li>All of my methods do not declare any exceptions</li> <br /> <li>As I discover that I need to deal with a specific exception I go back to the source where LogRuntimeException was thrown and I change it to <Specific>RuntimeException (This is rarer than you think)<br /><ul><br /> <li>I am restoring the exception fidelity only where needed.</li> </ul> </li> <br /> <li>Net effect is that when you come across a try-catch clause you better pay attention as interesting things are happening there.<br /><ul><br /> <li>Very few try-catch calluses, code is much easier to read.</li> <br /> <li>Very close to 100% test coverage as there is no dead code in my catch blocks.</li> </ul> </li> </ul> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Checked exceptions I love you, but you have to go&url=https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html#comments' style='font-weight: 500; text-decoration: underline;'>34 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Java' rel='tag'> Java </a> , <a class='label' href='https://testing.googleblog.com/search/label/Misko%20Hevery' rel='tag'> Misko Hevery </a> </span> </div> </div> </div> <div class='post' data-id='8689734607920519872' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/09/plague-of-entropy.html' itemprop='url' title='The Plague of Entropy'> The Plague of Entropy </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Monday, September 14, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By James Whittaker<div><br />Mathematically entropy is a measure of uncertainty. If there are, say, five events then maximum entropy occurs when those five events are equally likely and minimum entropy when one of those events is certain and the other four impossible. </div><div><br /></div><div>The more uncertain events you have to consider, the higher measured entropy climbs. People often think of entropy as a measure of randomness: the more (uncertain) events one must consider, the more random the outcome becomes. </div><div><br /></div><div>Testers introduce entropy into development by adding to the number of things a developer has to do. When developers are writing code, entropy is low. When we submit bugs, we increase entropy. Bugs divert their attention from coding. They must now progress in parallel on creating and fixing features. More bugs means more parallel tasks and raises entropy. This entropy is one reason that bugs foster more bugs ... the entropic principle ensures it. Entropy creates more entropy! Finally there is math to show what is intuitively <span class="blsp-spelling-corrected" id="SPELLING_ERROR_0">appealing</span>: that prevention beats a cure. </div><div><br /></div><div>However, there is nothing we can do to completely prevent the plague of entropy other than create developers who never err. Since this is unlikely any time soon we must recognize how and when we are introducing entropy and do what we can to manage it. The more we can do <i>during</i> development the better. Helping out in code reviews, educating our developers about test plans, user scenarios and execution environments so they can code against them will reduce the number of bugs we have to report. Smoking out bugs early, submitting them in batches and making sure we submit only high quality bugs by triaging them ourselves will keep their mind on development. Writing good bug reports and quickly regressing fixes will keep their attention where it needs to be. In effect, it maximizes the certainty of the 'development event' and minimizes the number and impact of bugs. Entropy thus tends toward it's minimum. </div><div><br /></div><div>We can't banish this plague but if we can recognize the introduction of entropy into development and understand its inevitable effect on code quality, we can keep it at bay. </div><div><br /></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By James Whittaker<div><br />Mathematically entropy is a measure of uncertainty. If there are, say, five events then maximum entropy occurs when those five events are equally likely and minimum entropy when one of those events is certain and the other four impossible. </div><div><br /></div><div>The more uncertain events you have to consider, the higher measured entropy climbs. People often think of entropy as a measure of randomness: the more (uncertain) events one must consider, the more random the outcome becomes. </div><div><br /></div><div>Testers introduce entropy into development by adding to the number of things a developer has to do. When developers are writing code, entropy is low. When we submit bugs, we increase entropy. Bugs divert their attention from coding. They must now progress in parallel on creating and fixing features. More bugs means more parallel tasks and raises entropy. This entropy is one reason that bugs foster more bugs ... the entropic principle ensures it. Entropy creates more entropy! Finally there is math to show what is intuitively <span class="blsp-spelling-corrected" id="SPELLING_ERROR_0">appealing</span>: that prevention beats a cure. </div><div><br /></div><div>However, there is nothing we can do to completely prevent the plague of entropy other than create developers who never err. Since this is unlikely any time soon we must recognize how and when we are introducing entropy and do what we can to manage it. The more we can do <i>during</i> development the better. Helping out in code reviews, educating our developers about test plans, user scenarios and execution environments so they can code against them will reduce the number of bugs we have to report. Smoking out bugs early, submitting them in batches and making sure we submit only high quality bugs by triaging them ourselves will keep their mind on development. Writing good bug reports and quickly regressing fixes will keep their attention where it needs to be. In effect, it maximizes the certainty of the 'development event' and minimizes the number and impact of bugs. Entropy thus tends toward it's minimum. </div><div><br /></div><div>We can't banish this plague but if we can recognize the introduction of entropy into development and understand its inevitable effect on code quality, we can keep it at bay. </div><div><br /></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:The Plague of Entropy&url=https://testing.googleblog.com/2009/09/plague-of-entropy.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/09/plague-of-entropy.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/09/plague-of-entropy.html#comments' style='font-weight: 500; text-decoration: underline;'>4 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/09/plague-of-entropy.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/James%20Whittaker' rel='tag'> James Whittaker </a> </span> </div> </div> </div> <div class='post' data-id='3776965704329854742' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/09/it-is-not-about-writing-tests-its-about.html' itemprop='url' title='It is not about writing tests, its about writing stories'> It is not about writing tests, its about writing stories </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, September 02, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <div> by <a href="http://misko.hevery.com/">Miško Hevery</a></div> <div> <br /></div> I would like to make an analogy between building software and building a car. I know it is imperfect one, as one is about design and the other is about manufacturing, but indulge me, the lessons are very similar.<br /> <br /> A piece of software is like a car. Lets say you would like to test a car, which you are in the process of designing, would you test is by driving it around and making modifications to it, or would you prove your design by testing each component separately? I think that testing all of the corner cases by driving the car around is very difficult, yes if the car drives you know that a lot of things must work (engine, transmission, electronics, etc), but if it does not work you have no idea where to look. However, there are some things which you will have very hard time reproducing in this end-to-end test. For example, it will be very hard for you to see if the car will be able to start in the extreme cold of the north pole, or if the engine will not overheat going full throttle up a sand dune in Sahara. I propose we take the engine out and simulate the load on it in a laboratory.<br /> <br /> We call driving car around an end-to-end test and testing the engine in isolation a unit-test. With unit tests it is much easier to simulate failures and corner cases in a much more controlled environment. We need both tests, but I feel that most developers can only imagine the end-to-end tests.<br /> <br /> But lets see how we could use the tests to design a transmission. But first, little terminology change, lets not call them test, but instead call them stories. They are stories because that is what they tell you about your design. My first story is that:<br /> <ul><br /> <li>the transmission should allow the output shaft to be locked, move in same direction (D) as the input shaft, move in opposite (R) or move independently (N)</li> </ul> <br /> Given such a story I could easily create a test which would prove that the above story is true for any design submitted to me. What I would most likely get is a transmission which would only have a single gear in each direction. So lets write another story<br /> <ul><br /> <li>the transmission should allow the ratio between input and output shaft to be [-1, 0, 1, 2, 3, 4]</li> </ul> <br /> Again I can write a test for such a transmission but i have not specified how the forward gear should be chosen, so such a transmission would most likely be permanently stuck in 1st gear and limit my speed, it will also over-rev the engine.<br /> <ul><br /> <li>the transmission should start in 1st and than switch to higher gear before the engine reaches maximum revolutions.</li> </ul> <br /> This is better, but my transmission would most likely rev the engine to maximum before it would switch, and once it would switch to higher gear and I would slow down, it would not down-shift.<br /> <ul><br /> <li>the transmission should down shift whenever the engine RPM fall bellow 1000 RPMs</li> </ul> <br /> OK, now it is starting to drive like a car, but still the limits for shifting really are 1000-6000 RPMs which is not very fuel efficient way to drive your car.<br /> <ul><br /> <li>the transmission should up-shift whenever the estimated fuel consumption at a higher gear ration is better than the current one.</li> </ul> <br /> So now our engine will not rev any more but it will be a lazy car since once the transmission is in the fuel efficient mode it will not want to down-shift<br /> <ul><br /> <li>the transmission should down-shift whenever the gas pedal is depressed more than 50% and the RPM is lower than the engine's peak output RPM.</li> </ul> <br /> I am not a transmission designer, but I think this is a decent start.<br /> <br /> Notice how I focused on the end result of the transmission rather than on testing specific internals of it. The transmission designer would have a lot of levy in choosing how it worked internally, Once we would have something and we would test it in the real world we could augment these list of stories with additional stories as we discovered additional properties which we would like the transmission to posses.<br /> <br /> If we would decide to change the internal design of the transmission for whatever reason we would have these stories as guides to make sure that we did not forget about anything. The stories represent assumptions which need to be true at all times. Over the lifetime of the component we can collect hundreds of stories which represent equal number of assumption which is built into the system.<br /> <br /> Now imagine that a new designer comes on board and makes a design change which he believes will improve the responsiveness of the transmission, he can do so because the existing stories are not restrictive in how, only it what the outcome should be. The stories save the designer from breaking an existing assumption which was already designed into the transmission.<br /> <br /> Now lets contrast this with how we would test the transmission if it would already be build.<br /> <ul><br /> <li>test to make sure all of the gears work</li> <br /> <li>test to make sure that the engine is not allowed to over-rev</li> </ul> <br /> It is hard now to think about what other tests to write, since we are not using the tests to drive the design. Now, lets say that someone now insist that we get 100% coverage, we open the transmission up and we see all kinds of logic, and rules and we don't know why since we were not part of the design so we write a test<br /> <ul><br /> <li>at 3000 RPM input shaft, apply 100% throttle and assert that the transmission goes to 2nd gear.</li> </ul> <br /> Tests like that are not very useful when you want to change the design, since you are likely to break the test, without fully understanding why the test was testing that specific conditions, it is hard to know if anything was broken if the tests is red.. That is because the tests does not tell a story any more, it only asserts the current design. It is likely that such a test will be in the way when you will try to do design changes. The point I am trying to make is that there is huge difference between writing tests before or after. When we write tests before we are:<br /> <ul><br /> <li>creating a story which is forcing a particular design decision.</li> <br /> <li>tests are a collection of assumptions which needs to be true at all times.</li> </ul> <br /> when we write tests after the fact we:<br /> <ul><br /> <li>miss a lot of reasons why things are done in particular way even if we have 100% coverage</li> <br /> <li>test are often brittle because they are tied to particulars of the current implementation</li> <br /> <li>tests are just snapshots and don't tell a story of why the component does something, only that it does.</li> </ul> <br /> For this reason there are huge differences in quality when writing assumptions as stories before (which force design to emerge) or writing tests after which take a snapshot of a given design. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <div> by <a href="http://misko.hevery.com/">Miško Hevery</a></div> <div> <br /></div> I would like to make an analogy between building software and building a car. I know it is imperfect one, as one is about design and the other is about manufacturing, but indulge me, the lessons are very similar.<br /> <br /> A piece of software is like a car. Lets say you would like to test a car, which you are in the process of designing, would you test is by driving it around and making modifications to it, or would you prove your design by testing each component separately? I think that testing all of the corner cases by driving the car around is very difficult, yes if the car drives you know that a lot of things must work (engine, transmission, electronics, etc), but if it does not work you have no idea where to look. However, there are some things which you will have very hard time reproducing in this end-to-end test. For example, it will be very hard for you to see if the car will be able to start in the extreme cold of the north pole, or if the engine will not overheat going full throttle up a sand dune in Sahara. I propose we take the engine out and simulate the load on it in a laboratory.<br /> <br /> We call driving car around an end-to-end test and testing the engine in isolation a unit-test. With unit tests it is much easier to simulate failures and corner cases in a much more controlled environment. We need both tests, but I feel that most developers can only imagine the end-to-end tests.<br /> <br /> But lets see how we could use the tests to design a transmission. But first, little terminology change, lets not call them test, but instead call them stories. They are stories because that is what they tell you about your design. My first story is that:<br /> <ul><br /> <li>the transmission should allow the output shaft to be locked, move in same direction (D) as the input shaft, move in opposite (R) or move independently (N)</li> </ul> <br /> Given such a story I could easily create a test which would prove that the above story is true for any design submitted to me. What I would most likely get is a transmission which would only have a single gear in each direction. So lets write another story<br /> <ul><br /> <li>the transmission should allow the ratio between input and output shaft to be [-1, 0, 1, 2, 3, 4]</li> </ul> <br /> Again I can write a test for such a transmission but i have not specified how the forward gear should be chosen, so such a transmission would most likely be permanently stuck in 1st gear and limit my speed, it will also over-rev the engine.<br /> <ul><br /> <li>the transmission should start in 1st and than switch to higher gear before the engine reaches maximum revolutions.</li> </ul> <br /> This is better, but my transmission would most likely rev the engine to maximum before it would switch, and once it would switch to higher gear and I would slow down, it would not down-shift.<br /> <ul><br /> <li>the transmission should down shift whenever the engine RPM fall bellow 1000 RPMs</li> </ul> <br /> OK, now it is starting to drive like a car, but still the limits for shifting really are 1000-6000 RPMs which is not very fuel efficient way to drive your car.<br /> <ul><br /> <li>the transmission should up-shift whenever the estimated fuel consumption at a higher gear ration is better than the current one.</li> </ul> <br /> So now our engine will not rev any more but it will be a lazy car since once the transmission is in the fuel efficient mode it will not want to down-shift<br /> <ul><br /> <li>the transmission should down-shift whenever the gas pedal is depressed more than 50% and the RPM is lower than the engine's peak output RPM.</li> </ul> <br /> I am not a transmission designer, but I think this is a decent start.<br /> <br /> Notice how I focused on the end result of the transmission rather than on testing specific internals of it. The transmission designer would have a lot of levy in choosing how it worked internally, Once we would have something and we would test it in the real world we could augment these list of stories with additional stories as we discovered additional properties which we would like the transmission to posses.<br /> <br /> If we would decide to change the internal design of the transmission for whatever reason we would have these stories as guides to make sure that we did not forget about anything. The stories represent assumptions which need to be true at all times. Over the lifetime of the component we can collect hundreds of stories which represent equal number of assumption which is built into the system.<br /> <br /> Now imagine that a new designer comes on board and makes a design change which he believes will improve the responsiveness of the transmission, he can do so because the existing stories are not restrictive in how, only it what the outcome should be. The stories save the designer from breaking an existing assumption which was already designed into the transmission.<br /> <br /> Now lets contrast this with how we would test the transmission if it would already be build.<br /> <ul><br /> <li>test to make sure all of the gears work</li> <br /> <li>test to make sure that the engine is not allowed to over-rev</li> </ul> <br /> It is hard now to think about what other tests to write, since we are not using the tests to drive the design. Now, lets say that someone now insist that we get 100% coverage, we open the transmission up and we see all kinds of logic, and rules and we don't know why since we were not part of the design so we write a test<br /> <ul><br /> <li>at 3000 RPM input shaft, apply 100% throttle and assert that the transmission goes to 2nd gear.</li> </ul> <br /> Tests like that are not very useful when you want to change the design, since you are likely to break the test, without fully understanding why the test was testing that specific conditions, it is hard to know if anything was broken if the tests is red.. That is because the tests does not tell a story any more, it only asserts the current design. It is likely that such a test will be in the way when you will try to do design changes. The point I am trying to make is that there is huge difference between writing tests before or after. When we write tests before we are:<br /> <ul><br /> <li>creating a story which is forcing a particular design decision.</li> <br /> <li>tests are a collection of assumptions which needs to be true at all times.</li> </ul> <br /> when we write tests after the fact we:<br /> <ul><br /> <li>miss a lot of reasons why things are done in particular way even if we have 100% coverage</li> <br /> <li>test are often brittle because they are tied to particulars of the current implementation</li> <br /> <li>tests are just snapshots and don't tell a story of why the component does something, only that it does.</li> </ul> <br /> For this reason there are huge differences in quality when writing assumptions as stories before (which force design to emerge) or writing tests after which take a snapshot of a given design. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:It is not about writing tests, its about writing stories&url=https://testing.googleblog.com/2009/09/it-is-not-about-writing-tests-its-about.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/09/it-is-not-about-writing-tests-its-about.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/09/it-is-not-about-writing-tests-its-about.html#comments' style='font-weight: 500; text-decoration: underline;'>8 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/09/it-is-not-about-writing-tests-its-about.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Misko%20Hevery' rel='tag'> Misko Hevery </a> </span> </div> </div> </div> <div class='post' data-id='2441538521820777322' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/09/7th-plague-and-beyond.html' itemprop='url' title='The 7th Plague and Beyond'> The 7th Plague and Beyond </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, September 02, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> By James Whittaker<div><br /></div><div>Sorry I haven't followed up on this, let the excuse parade begin: A) My new book just came out and I have spent a lot of time corresponding with readers. B) I have taken on leadership of some new projects including the testing of Chrome and Chrome OS (yes you will hear more about these projects right here in the future). C) I've gotten just short of 100 emails suggesting the 7<span class="blsp-spelling-error" id="SPELLING_ERROR_0">th</span> plague and that takes time to sort through.</div><div><br /></div><div>This is clearly one plague-ridden industry (and, no, I am not talking about my book!)</div><div><br /></div><div>I've thrown out many of them that deal with a specific organization or person who just doesn't take testing seriously enough. Things like the Plague of Apathy (suggested exactly 17 times!) just doesn't fit. This isn't an industry plague, it's a personal/group plague. If you don't care about quality, please do us all a favor and get out of the software business. Go screw someone <span class="blsp-spelling-error" id="SPELLING_ERROR_1">else's</span> industry up, we have enough organic problems we have to deal with. I also didn't put down the Plague of the Deluded Developer (suggested by various names 22 times) because it dealt with developers that as a Googler I no longer have to deal with ... those who think they never write bugs. Our developers know better and if I find out exactly where they purchased that clue I will forward the link. </div><div><br /></div><div>Here's some of the best. As many of them have multiple suggesters I have credited the persons who were either first or gave the most thoughtful analysis. Feel free, if you are one of these people, to give further details or clarifications in the comments of this post as I am sure these summaries do not do them justice. </div><div><br /></div><div><b>The Plague of Metrics</b> (Nicole Klein, Curtis <span class="blsp-spelling-error" id="SPELLING_ERROR_2">Pettit</span> plus 18 others): Metrics change behavior and once a tester knows how the measurement works, they test to make themselves look good or say what they want it to say ignoring other more important factors. The metric becomes the goal instead of measuring progress. The distaste for metrics in many of these emails was palpable!</div><div><br /></div><div><b>The Plague of Semantics</b> (Chris <span class="blsp-spelling-error" id="SPELLING_ERROR_3">LeMesurier</span> plus 3 others): We misuse and overuse terms and people like to assign their own meaning to certain terms. It means that designs and specs are often misunderstood or misinterpreted. This was also called the plague of assumptions by other contributors. </div><div><br /></div><div><b>The Plague of Infinity</b> (<span class="blsp-spelling-error" id="SPELLING_ERROR_4">Jarod</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Salamond</span>, <span class="blsp-spelling-error" id="SPELLING_ERROR_6">Radislav</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_7">Vasilev</span> and 14 others): The testing problem is so huge it's overwhelming. We spend so much time trying to justify our coverage and explain what we are and are not testing that it takes away from our focus on testing. Every time we take a look at the testing problem we see new risks and new things that need our attention. It randomizes us and stalls our progress. This was also called the plague of endlessness and exhaustion. </div><div><br /></div><div><b>The Plague of Miscommunication </b>(Scott White and 2 others): The language of creation (development) and the language of destruction (testing) are different. Testers write a bug report and the <span class="blsp-spelling-error" id="SPELLING_ERROR_8">devs</span> don't understand it and cycles have to be spent explaining and reexplaining. A related plague is the lack of communication that causes testers to redo work and tread over the same paths as unit tests, integration tests and even the tests that other testers on the team are performing. This was also called the plague of language (meaning lack of a common one). </div><div><br /></div><div><b>The Plague of Rigidness</b> (<span class="blsp-spelling-error" id="SPELLING_ERROR_9">Roussi</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Roussev</span>, Steven Woody, Michele Smith and 5 others): Sticking to the plan/process/procedure no matter what. Test strategy cannot be bottled in such a manner yet process heavy teams often ignore creativity for the sake of process. We stick with the same stale testing ideas product after product, release after release. This was also called the plague of complacency. <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Roussi</span> suggested a novel twist calling this the success plague where complacency is brought about through success of the product. How can we be wrong when our software was so successful in the market? </div><div><br /></div><div>And I have my own 7<span class="blsp-spelling-error" id="SPELLING_ERROR_12">th</span> Plague that I'll save for the next post. Unless anyone would like to write it for me? It's called the Plague of Entropy. A free book to the person who nails it.</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> By James Whittaker<div><br /></div><div>Sorry I haven't followed up on this, let the excuse parade begin: A) My new book just came out and I have spent a lot of time corresponding with readers. B) I have taken on leadership of some new projects including the testing of Chrome and Chrome OS (yes you will hear more about these projects right here in the future). C) I've gotten just short of 100 emails suggesting the 7<span class="blsp-spelling-error" id="SPELLING_ERROR_0">th</span> plague and that takes time to sort through.</div><div><br /></div><div>This is clearly one plague-ridden industry (and, no, I am not talking about my book!)</div><div><br /></div><div>I've thrown out many of them that deal with a specific organization or person who just doesn't take testing seriously enough. Things like the Plague of Apathy (suggested exactly 17 times!) just doesn't fit. This isn't an industry plague, it's a personal/group plague. If you don't care about quality, please do us all a favor and get out of the software business. Go screw someone <span class="blsp-spelling-error" id="SPELLING_ERROR_1">else's</span> industry up, we have enough organic problems we have to deal with. I also didn't put down the Plague of the Deluded Developer (suggested by various names 22 times) because it dealt with developers that as a Googler I no longer have to deal with ... those who think they never write bugs. Our developers know better and if I find out exactly where they purchased that clue I will forward the link. </div><div><br /></div><div>Here's some of the best. As many of them have multiple suggesters I have credited the persons who were either first or gave the most thoughtful analysis. Feel free, if you are one of these people, to give further details or clarifications in the comments of this post as I am sure these summaries do not do them justice. </div><div><br /></div><div><b>The Plague of Metrics</b> (Nicole Klein, Curtis <span class="blsp-spelling-error" id="SPELLING_ERROR_2">Pettit</span> plus 18 others): Metrics change behavior and once a tester knows how the measurement works, they test to make themselves look good or say what they want it to say ignoring other more important factors. The metric becomes the goal instead of measuring progress. The distaste for metrics in many of these emails was palpable!</div><div><br /></div><div><b>The Plague of Semantics</b> (Chris <span class="blsp-spelling-error" id="SPELLING_ERROR_3">LeMesurier</span> plus 3 others): We misuse and overuse terms and people like to assign their own meaning to certain terms. It means that designs and specs are often misunderstood or misinterpreted. This was also called the plague of assumptions by other contributors. </div><div><br /></div><div><b>The Plague of Infinity</b> (<span class="blsp-spelling-error" id="SPELLING_ERROR_4">Jarod</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Salamond</span>, <span class="blsp-spelling-error" id="SPELLING_ERROR_6">Radislav</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_7">Vasilev</span> and 14 others): The testing problem is so huge it's overwhelming. We spend so much time trying to justify our coverage and explain what we are and are not testing that it takes away from our focus on testing. Every time we take a look at the testing problem we see new risks and new things that need our attention. It randomizes us and stalls our progress. This was also called the plague of endlessness and exhaustion. </div><div><br /></div><div><b>The Plague of Miscommunication </b>(Scott White and 2 others): The language of creation (development) and the language of destruction (testing) are different. Testers write a bug report and the <span class="blsp-spelling-error" id="SPELLING_ERROR_8">devs</span> don't understand it and cycles have to be spent explaining and reexplaining. A related plague is the lack of communication that causes testers to redo work and tread over the same paths as unit tests, integration tests and even the tests that other testers on the team are performing. This was also called the plague of language (meaning lack of a common one). </div><div><br /></div><div><b>The Plague of Rigidness</b> (<span class="blsp-spelling-error" id="SPELLING_ERROR_9">Roussi</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Roussev</span>, Steven Woody, Michele Smith and 5 others): Sticking to the plan/process/procedure no matter what. Test strategy cannot be bottled in such a manner yet process heavy teams often ignore creativity for the sake of process. We stick with the same stale testing ideas product after product, release after release. This was also called the plague of complacency. <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Roussi</span> suggested a novel twist calling this the success plague where complacency is brought about through success of the product. How can we be wrong when our software was so successful in the market? </div><div><br /></div><div>And I have my own 7<span class="blsp-spelling-error" id="SPELLING_ERROR_12">th</span> Plague that I'll save for the next post. Unless anyone would like to write it for me? It's called the Plague of Entropy. A free book to the person who nails it.</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:The 7th Plague and Beyond&url=https://testing.googleblog.com/2009/09/7th-plague-and-beyond.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/09/7th-plague-and-beyond.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/09/7th-plague-and-beyond.html#comments' style='font-weight: 500; text-decoration: underline;'>8 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/09/7th-plague-and-beyond.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/James%20Whittaker' rel='tag'> James Whittaker </a> </span> </div> </div> </div> <div class='post' data-id='8508162176213086350' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/08/update-speakers-talks-for-gtac.html' itemprop='url' title='Update! Speakers & Talks for GTAC'> Update! Speakers & Talks for GTAC </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Monday, August 17, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> We are thrilled to announce the speakers and talks for the <a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank">4th Google Test Automation Conference (GTAC)</a>. Competition was fierce: we received over 100 submissions and have an acceptance rate of lower than 10%.<br /><ul><li>Testing Applications on Mobile Devices (Doron Reuveni, uTest)</li><li>JsTestDriver (Jeremie Lenfang-Engelmann, Misko Hevery, Google)</li><li>Fighting Layout Bugs (Michael Tamm, optivo GmbH)</li><li>Even better than the real thing - Lessons learned from testing GWT applications (Nicolas Wettstein, Google)</li><li>Selenium: to 2.0 and Beyond! (Simon Stewart, Google)</li><li>Automating Performance Test Data Collection and Reporting (David Burns, David Henderson, smartFOCUS DIGITAL)</li><li>Achieving Web Test Automation with a Mixed-Skills Team (Mark Micallef, BBC Future Media and Technology)</li><li>Score One for Quality! (Joshua Williams and Ross Smith, Microsoft)</li><li>Automatic workarounds for web applications (Antonio Carzaniga, Alessandra Gorla, Nicolò Perino, Mauro Pezzè, University of Lugano )</li><li>Precondition Satisfaction by Smart Object Selection in Random Testing (Yi Wei, Serge Gebhardt, ETH Zurich)</li></ul>For further information on the conference please visit its wepage at <a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank">http://www.gtac.biz</a>. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> We are thrilled to announce the speakers and talks for the <a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank">4th Google Test Automation Conference (GTAC)</a>. Competition was fierce: we received over 100 submissions and have an acceptance rate of lower than 10%.<br /><ul><li>Testing Applications on Mobile Devices (Doron Reuveni, uTest)</li><li>JsTestDriver (Jeremie Lenfang-Engelmann, Misko Hevery, Google)</li><li>Fighting Layout Bugs (Michael Tamm, optivo GmbH)</li><li>Even better than the real thing - Lessons learned from testing GWT applications (Nicolas Wettstein, Google)</li><li>Selenium: to 2.0 and Beyond! (Simon Stewart, Google)</li><li>Automating Performance Test Data Collection and Reporting (David Burns, David Henderson, smartFOCUS DIGITAL)</li><li>Achieving Web Test Automation with a Mixed-Skills Team (Mark Micallef, BBC Future Media and Technology)</li><li>Score One for Quality! (Joshua Williams and Ross Smith, Microsoft)</li><li>Automatic workarounds for web applications (Antonio Carzaniga, Alessandra Gorla, Nicolò Perino, Mauro Pezzè, University of Lugano )</li><li>Precondition Satisfaction by Smart Object Selection in Random Testing (Yi Wei, Serge Gebhardt, ETH Zurich)</li></ul>For further information on the conference please visit its wepage at <a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank">http://www.gtac.biz</a>. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Update! Speakers & Talks for GTAC&url=https://testing.googleblog.com/2009/08/update-speakers-talks-for-gtac.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/08/update-speakers-talks-for-gtac.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/08/update-speakers-talks-for-gtac.html#comments' style='font-weight: 500; text-decoration: underline;'>1 comment</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/08/update-speakers-talks-for-gtac.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/GTAC' rel='tag'> GTAC </a> </span> </div> </div> </div> <div class='post' data-id='4811369746547225829' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/08/super-fast-js-testing.html' itemprop='url' title='Super Fast JS Testing'> Super Fast JS Testing </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, August 12, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <div> <br /> <br /> by <a href="http://theshyam.com/">Shyam Seshadri</a><br /> <br /> Before I jump into how exactly you can perform super fast and easy JS testing, let me give you some background on the problem.<br /> <br /> Javascript is a finicky language (Some people even hesitate to call it a language). And it can easily grow and become a horrible and complicated beast, incapable of being tamed once let loose. And testing it is a nightmare. Once you have decided on a framework (of which there are a dime a dozen), you then have to set it up to run just right. You need to set it up to actually run your tests. Then you have to figure out how to run it in a continuous integration environment. Maybe even run it in headless mode. And everyone solves it in their own ways.<br /> <br /> But the biggest problem I have with most of these frameworks is that executing the tests usually requires a context switch. By that, I mean to run a JSUnit test, you end up usually having to open the browser, browse to a particular url or html page which then runs the test. Then you have to look at the results there, and then come back to your editor to either proceed further or fix your tests. Works, but really slows down development.<br /> <br /> In java, all it takes is to click the run button in your IDE to run your tests. You get instant feedback, a green / red bar and details on which tests passed and failed and at what line. No context switch, you can get it to run at every save, and proceed on your merry way. Till now, this was not possible with Javascript.<br /> <br /> But now, we have <a href="http://code.google.com/p/js-test-driver/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');" target="_blank">JS Test Driver</a>. My colleagues Jeremie and <a href="http://misko.hevery.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/misko.hevery.com');" target="_blank">Misko</a> ended up running into some of the issues I outlined above, and decided that going along with the flow was simply unacceptable. So they created a JS Testing framework which solves these very things. You can capture any browser on any machine, and when you tell it to run tests, it will go ahead and execute them on all these browsers and return you the results in your client. And its blazing fast. I am talking milliseconds to run 100 odd tests. And you can tell it to rerun your tests at each save. All within the comforts of your IDE. And over the last three weeks, I have been working on the eclipse plugin for JS Test Driver, and its now at the point where its in a decent working condition.<br /> <div id="attachment_165" style="width: 438px;"> <a href="http://theshyam.com/wp-content/uploads/2009/08/JS-Test-Driver-Plugin.png"><img alt="The plugin in action" height="478" src="https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_sUzXZ-AvibQ5_2d7IyBj1RsO7F5d-_LhzrSThY24-jv4ul-gzh4EFU3-i6kg7sKjP-d_NCDkiPTSHgXLXIufjGRHWPwB9wxK5dJuwi3_vsJOFcZn6ngAePY8egyJtTRD2Fws6ZyhHvC_4u=s0-d" title="JS Test Driver Plugin" width="428"></a>The plugin in action</div> <br /> The plugin allows you to, from within Eclipse, start the JS Test Driver server, capture some browsers, and then run your tests. You get pretty icons telling you what browsers were captured, the state of the server, the state of the tests. It allows you to filter and show only failures, rerun your last launch configuration, even setup the paths to your browsers so you can launch it all from the safety of eclipse. And as you can see, its super fast. <strong><em>Some 100 odd tests in less than 10 ms</em></strong>. If thats not fast, I don’t know what is.<br /> <br /> For more details on JS Test Driver, visit its <a href="http://code.google.com/p/js-test-driver/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');" target="_blank">Google Code</a> website and see how you can use it in your next project and even integrate it into a continuous integration. Misko talks a little bit more about the motivations behind writing it on his <a href="http://misko.hevery.com/2009/05/22/yet-another-javascript-testing-framework/" onclick="javascript:pageTracker._trackPageview('/outbound/article/misko.hevery.com');" target="_blank">Yet Another JS Testing Framework</a> post. To try out the plugin for yourselves, go add the following update site to eclipse:<br /> <br /> <strong>http://js-test-driver.googlecode.com/svn/update/</strong><br /> <br /> <strong> </strong>For all you IntelliJ fanatics, there is something similar in the works.</div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <div> <br /> <br /> by <a href="http://theshyam.com/">Shyam Seshadri</a><br /> <br /> Before I jump into how exactly you can perform super fast and easy JS testing, let me give you some background on the problem.<br /> <br /> Javascript is a finicky language (Some people even hesitate to call it a language). And it can easily grow and become a horrible and complicated beast, incapable of being tamed once let loose. And testing it is a nightmare. Once you have decided on a framework (of which there are a dime a dozen), you then have to set it up to run just right. You need to set it up to actually run your tests. Then you have to figure out how to run it in a continuous integration environment. Maybe even run it in headless mode. And everyone solves it in their own ways.<br /> <br /> But the biggest problem I have with most of these frameworks is that executing the tests usually requires a context switch. By that, I mean to run a JSUnit test, you end up usually having to open the browser, browse to a particular url or html page which then runs the test. Then you have to look at the results there, and then come back to your editor to either proceed further or fix your tests. Works, but really slows down development.<br /> <br /> In java, all it takes is to click the run button in your IDE to run your tests. You get instant feedback, a green / red bar and details on which tests passed and failed and at what line. No context switch, you can get it to run at every save, and proceed on your merry way. Till now, this was not possible with Javascript.<br /> <br /> But now, we have <a href="http://code.google.com/p/js-test-driver/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');" target="_blank">JS Test Driver</a>. My colleagues Jeremie and <a href="http://misko.hevery.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/misko.hevery.com');" target="_blank">Misko</a> ended up running into some of the issues I outlined above, and decided that going along with the flow was simply unacceptable. So they created a JS Testing framework which solves these very things. You can capture any browser on any machine, and when you tell it to run tests, it will go ahead and execute them on all these browsers and return you the results in your client. And its blazing fast. I am talking milliseconds to run 100 odd tests. And you can tell it to rerun your tests at each save. All within the comforts of your IDE. And over the last three weeks, I have been working on the eclipse plugin for JS Test Driver, and its now at the point where its in a decent working condition.<br /> <div id="attachment_165" style="width: 438px;"> <a href="http://theshyam.com/wp-content/uploads/2009/08/JS-Test-Driver-Plugin.png"><img alt="The plugin in action" height="478" src="https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_sUzXZ-AvibQ5_2d7IyBj1RsO7F5d-_LhzrSThY24-jv4ul-gzh4EFU3-i6kg7sKjP-d_NCDkiPTSHgXLXIufjGRHWPwB9wxK5dJuwi3_vsJOFcZn6ngAePY8egyJtTRD2Fws6ZyhHvC_4u=s0-d" title="JS Test Driver Plugin" width="428"></a>The plugin in action</div> <br /> The plugin allows you to, from within Eclipse, start the JS Test Driver server, capture some browsers, and then run your tests. You get pretty icons telling you what browsers were captured, the state of the server, the state of the tests. It allows you to filter and show only failures, rerun your last launch configuration, even setup the paths to your browsers so you can launch it all from the safety of eclipse. And as you can see, its super fast. <strong><em>Some 100 odd tests in less than 10 ms</em></strong>. If thats not fast, I don’t know what is.<br /> <br /> For more details on JS Test Driver, visit its <a href="http://code.google.com/p/js-test-driver/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');" target="_blank">Google Code</a> website and see how you can use it in your next project and even integrate it into a continuous integration. Misko talks a little bit more about the motivations behind writing it on his <a href="http://misko.hevery.com/2009/05/22/yet-another-javascript-testing-framework/" onclick="javascript:pageTracker._trackPageview('/outbound/article/misko.hevery.com');" target="_blank">Yet Another JS Testing Framework</a> post. To try out the plugin for yourselves, go add the following update site to eclipse:<br /> <br /> <strong>http://js-test-driver.googlecode.com/svn/update/</strong><br /> <br /> <strong> </strong>For all you IntelliJ fanatics, there is something similar in the works.</div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Super Fast JS Testing&url=https://testing.googleblog.com/2009/08/super-fast-js-testing.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/08/super-fast-js-testing.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/08/super-fast-js-testing.html#comments' style='font-weight: 500; text-decoration: underline;'>1 comment</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/08/super-fast-js-testing.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/JavaScript' rel='tag'> JavaScript </a> , <a class='label' href='https://testing.googleblog.com/search/label/Shyam%20Seshadri' rel='tag'> Shyam Seshadri </a> </span> </div> </div> </div> <div class='post' data-id='4383938670771813043' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/08/7th-plague.html' itemprop='url' title='The 7th Plague?'> The 7th Plague? </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Monday, August 10, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <span class="byline-author">By James A. Whittaker</span><div><br /></div><div>Yes, I only posted 6 plagues. Congratulations for catching this purposeful omission! You wouldn't trust a developer who argues "this doesn't need to be tested" or "that function works like so" and you shouldn't trust me when I say there are 7 plagues. In the world of testing <i>all assumptions</i> must be scrutinized and it doesn't work until someone, namely a tester, verifies that it does!</div><div><br /></div><div>Clearly this is an alert and education readership. But why assume even this statement is true? How about another test? Anyone feel like contributing the 7th plague?</div><div><br /></div><div>I've actually received a few via email already and I have an idea of my own 7th. So email them to me at docjamesw@gmail.com and I'll post a few of the best, with attribution, on this blog. Maybe I can even scare up some Google SWAG or a copy of my latest book to the best one. </div><div><br /></div><div>First come, first published. </div><div><br /></div><div><br /></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <span class="byline-author">By James A. Whittaker</span><div><br /></div><div>Yes, I only posted 6 plagues. Congratulations for catching this purposeful omission! You wouldn't trust a developer who argues "this doesn't need to be tested" or "that function works like so" and you shouldn't trust me when I say there are 7 plagues. In the world of testing <i>all assumptions</i> must be scrutinized and it doesn't work until someone, namely a tester, verifies that it does!</div><div><br /></div><div>Clearly this is an alert and education readership. But why assume even this statement is true? How about another test? Anyone feel like contributing the 7th plague?</div><div><br /></div><div>I've actually received a few via email already and I have an idea of my own 7th. So email them to me at docjamesw@gmail.com and I'll post a few of the best, with attribution, on this blog. Maybe I can even scare up some Google SWAG or a copy of my latest book to the best one. </div><div><br /></div><div>First come, first published. </div><div><br /></div><div><br /></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:The 7th Plague?&url=https://testing.googleblog.com/2009/08/7th-plague.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/08/7th-plague.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/08/7th-plague.html#comments' style='font-weight: 500; text-decoration: underline;'>4 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/08/7th-plague.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/James%20Whittaker' rel='tag'> James Whittaker </a> </span> </div> </div> </div> <div class='post' data-id='1479275730986763877' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/08/tott-testing-gwt-without-gwttest.html' itemprop='url' title='TotT: Testing GWT without GwtTestCase'> TotT: Testing GWT without GwtTestCase </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Saturday, August 08, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <div id="doc-contents"><p class="western" style="font-family:Times New Roman;">Because GWT (Google Web Toolkit) is new and exciting it's easy to forget the lessons on clean GUI code structure that have been accumulated over nearly thirty years.</p><p class="western" style="TEXT-ALIGN: left" face="Times New Roman">GwtTestCase is good for testing UI-specific code in JavaScript. If you find yourself using GwtTestCase for testing non-ui client-side logic you may not have a clear View/Presenter separation. <b>Separating the View and the Presenter allows for more modular, more easily tested code with shorter test times</b>. Model View Presenter was introduced in another <span style="FONT-STYLE: normal"><a id="llx6" title="episode back in February" href="http://googletesting.blogspot.com/2009/02/with-all-sport-drug-scandals-of-late.html">episode back in February</a>. </span><b>Here's how to apply it to a GWT app.</b><br /></p><p class="western" style="TEXT-ALIGN: left" face="Times New Roman"><b>Defining terms:</b><br /></p><div style="MARGIN-LEFT: 40px;font-family:Times New Roman;" ><ul><li><b style="COLOR: rgb(153,0,0)">Server</b><span style="COLOR: rgb(0,0,0)"> – a completely standard backend with <span style="FONT-STYLE: normal"><b>no dependency on GWT</b>.</span></span></li></ul></div><div style="MARGIN-LEFT: 40px; FONT-FAMILY: Times New Roman"><ul><li><b><span style="COLOR: rgb(153,0,0)">Model</span> </b><span style="COLOR: rgb(0,0,0)">– the data model. May be shared between the client and server side, or if appropriate you might have a different model for the client side. It has <b>no dependency on GWT</b>.</span></li></ul></div><div style="MARGIN-LEFT: 40px" face="Times New Roman"><ul><li><b><span style="COLOR: rgb(128,0,0)"><span style="COLOR: rgb(153,0,0)">View</span> </span></b><span style="FONT-STYLE: normal"><span style="COLOR: rgb(0,0,0)">– the display. <b>Classes in the view wrap GWT widgets</b>, hiding them from the rest of your code. They contain <b>no logic</b>, <b>no state</b>, and are <b>easy to mock</b>.</span></span></li></ul><ul><li><b><span style="COLOR: rgb(128,0,0)"><span style="COLOR: rgb(153,0,0)">Presenter</span> </span></b><span style="FONT-STYLE: normal"><span style="COLOR: rgb(0,0,0)">– all the client side logic and state; it talks to the server and tells the view what to do. It <b>uses RPC mechanisms from GWT </b>but<b> no widgets</b>.</span></span></li></ul></div><p class="western" align="left" face="Times New Roman"><b>The Presenter, which contains all the interesting client-side code is fully testable in Java!</b></p><p style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-LEFT: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-BOTTOM: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:Courier New, monospace;">public void testRefreshPersonListButtonWasClicked() {<br />IMocksControl easyMockContext = EasyMock.createControl()<br />mockServer = easyMockContext.createMock(Server.class);<br />mockView = easyMockContext.createMock(View.class);<span style="font-family:Courier New, monospace;"><br />List<person> franz = Lists.newArrayList(new Person("Franz", "Mayer"));<b><br />mockServer.getPersonList(AsyncCallbackSuccessMatcher<list<person>>reportSuccess(franz)));<br />mockView.clearPersonList());<br />mockView.addPerson(“Franz”, “Mayer”);</b></span><br />easyMockContext.replay();<br />presenter.refreshPersonListButtonClicked();<br />easyMockContext.verify();<br />}</span></p><p class="western" align="left" face="Times New Roman"><b>Testing failure cases is now as easy as changing expectations.</b> By swapping in the following expectations, the above test goes from testing success to testing that after two server failures, we show an error message.</p><p style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,255,230); MARGIN-LEFT: 0.39in; BORDER-LEFT: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-BOTTOM: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:Courier New, monospace;">mockServer.getPersonList(AsyncCallbackFailureMatcher<list<person>><b>reportFailure</b>(failedExpn))<br />expectLastCall().times(2); <i>// Ensure the presenter tries twice<br /></i><br />mockView.showErrorMessage(“Sorry, please try again later”));</span></p><p class="western" style="FONT-FAMILY: Times New Roman"><span style="color:#000000;">You'll still need an end-to-end test. But <b>all your <span style="FONT-STYLE: normal">logic</span> <span style="FONT-STYLE: normal">can be tested in small and fast tests.</span></b> </span></p><p class="western" style="FONT-STYLE: normal" face="Times New Roman">The Source Code for the Matchers is open-sourced and can be downloaded here: <a id="po8i" title="AsynCallbackSuccessMatcher.java" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackSuccessMatcher.java">AsyncCallbackSuccessMatcher.java</a> - <a id="yaum" title="AsyncCallbackFailureMatcher.java" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackFailureMatcher.java">AsyncCallbackFailureMatcher.java</a>.</p><p class="western" style="FONT-STYLE: normal" face="Times New Roman">Consider using Test Driven Development (TDD) to develop the presenter. It tends to result in higher test coverage, faster and more relevant tests, as well as a better code structure.</p><p class="western" face="Times New Roman"><br /></p><p class="western" face="Times New Roman"><i><span style="font-family:Times New Roman;">This week's episode by David Morgan, Christopher Semturs and Nicolas Wettstein based in Zürich, Switzerland – having a real Mountain View</span></i><a id="rmpv" title="Toilet-friendly version" href="http://code.google.com/intl/de-CH/testing/TotT-2009-08-07.pdf"><br /><br /></a></p><p class="western" face="Times New Roman"><a id="ksh_" title="Toilet-friendly version" href="http://code.google.com/intl/de-CH/testing/TotT-2009-08-07.pdf">Toilet-friendly version</a><br /></p><p class="western" face="Times New Roman"><a id="by8d" title="AsynCallbackSuccessMatcher.java" style="COLOR: rgb(85,26,139)" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackSuccessMatcher.java">AsyncCallbackSuccessMatcher.java</a></p><p class="western" style="FONT-FAMILY: Times New Roman"><a id="he0i" title="AsyncCallbackFailureMatcher.java" style="COLOR: rgb(85,26,139)" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackFailureMatcher.java">AsyncCallbackFailureMatcher.java</a>.<br /></p></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <div id="doc-contents"><p class="western" style="font-family:Times New Roman;">Because GWT (Google Web Toolkit) is new and exciting it's easy to forget the lessons on clean GUI code structure that have been accumulated over nearly thirty years.</p><p class="western" style="TEXT-ALIGN: left" face="Times New Roman">GwtTestCase is good for testing UI-specific code in JavaScript. If you find yourself using GwtTestCase for testing non-ui client-side logic you may not have a clear View/Presenter separation. <b>Separating the View and the Presenter allows for more modular, more easily tested code with shorter test times</b>. Model View Presenter was introduced in another <span style="FONT-STYLE: normal"><a id="llx6" title="episode back in February" href="http://googletesting.blogspot.com/2009/02/with-all-sport-drug-scandals-of-late.html">episode back in February</a>. </span><b>Here's how to apply it to a GWT app.</b><br /></p><p class="western" style="TEXT-ALIGN: left" face="Times New Roman"><b>Defining terms:</b><br /></p><div style="MARGIN-LEFT: 40px;font-family:Times New Roman;" ><ul><li><b style="COLOR: rgb(153,0,0)">Server</b><span style="COLOR: rgb(0,0,0)"> – a completely standard backend with <span style="FONT-STYLE: normal"><b>no dependency on GWT</b>.</span></span></li></ul></div><div style="MARGIN-LEFT: 40px; FONT-FAMILY: Times New Roman"><ul><li><b><span style="COLOR: rgb(153,0,0)">Model</span> </b><span style="COLOR: rgb(0,0,0)">– the data model. May be shared between the client and server side, or if appropriate you might have a different model for the client side. It has <b>no dependency on GWT</b>.</span></li></ul></div><div style="MARGIN-LEFT: 40px" face="Times New Roman"><ul><li><b><span style="COLOR: rgb(128,0,0)"><span style="COLOR: rgb(153,0,0)">View</span> </span></b><span style="FONT-STYLE: normal"><span style="COLOR: rgb(0,0,0)">– the display. <b>Classes in the view wrap GWT widgets</b>, hiding them from the rest of your code. They contain <b>no logic</b>, <b>no state</b>, and are <b>easy to mock</b>.</span></span></li></ul><ul><li><b><span style="COLOR: rgb(128,0,0)"><span style="COLOR: rgb(153,0,0)">Presenter</span> </span></b><span style="FONT-STYLE: normal"><span style="COLOR: rgb(0,0,0)">– all the client side logic and state; it talks to the server and tells the view what to do. It <b>uses RPC mechanisms from GWT </b>but<b> no widgets</b>.</span></span></li></ul></div><p class="western" align="left" face="Times New Roman"><b>The Presenter, which contains all the interesting client-side code is fully testable in Java!</b></p><p style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,245,255); MARGIN-LEFT: 0.39in; BORDER-LEFT: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-BOTTOM: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:Courier New, monospace;">public void testRefreshPersonListButtonWasClicked() {<br />IMocksControl easyMockContext = EasyMock.createControl()<br />mockServer = easyMockContext.createMock(Server.class);<br />mockView = easyMockContext.createMock(View.class);<span style="font-family:Courier New, monospace;"><br />List<person> franz = Lists.newArrayList(new Person("Franz", "Mayer"));<b><br />mockServer.getPersonList(AsyncCallbackSuccessMatcher<list<person>>reportSuccess(franz)));<br />mockView.clearPersonList());<br />mockView.addPerson(“Franz”, “Mayer”);</b></span><br />easyMockContext.replay();<br />presenter.refreshPersonListButtonClicked();<br />easyMockContext.verify();<br />}</span></p><p class="western" align="left" face="Times New Roman"><b>Testing failure cases is now as easy as changing expectations.</b> By swapping in the following expectations, the above test goes from testing success to testing that after two server failures, we show an error message.</p><p style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; BACKGROUND: rgb(230,255,230); MARGIN-LEFT: 0.39in; BORDER-LEFT: rgb(128,128,128) 1px solid; MARGIN-RIGHT: 0.39in; BORDER-BOTTOM: rgb(128,128,128) 1px solid; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous"><span style="font-family:Courier New, monospace;">mockServer.getPersonList(AsyncCallbackFailureMatcher<list<person>><b>reportFailure</b>(failedExpn))<br />expectLastCall().times(2); <i>// Ensure the presenter tries twice<br /></i><br />mockView.showErrorMessage(“Sorry, please try again later”));</span></p><p class="western" style="FONT-FAMILY: Times New Roman"><span style="color:#000000;">You'll still need an end-to-end test. But <b>all your <span style="FONT-STYLE: normal">logic</span> <span style="FONT-STYLE: normal">can be tested in small and fast tests.</span></b> </span></p><p class="western" style="FONT-STYLE: normal" face="Times New Roman">The Source Code for the Matchers is open-sourced and can be downloaded here: <a id="po8i" title="AsynCallbackSuccessMatcher.java" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackSuccessMatcher.java">AsyncCallbackSuccessMatcher.java</a> - <a id="yaum" title="AsyncCallbackFailureMatcher.java" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackFailureMatcher.java">AsyncCallbackFailureMatcher.java</a>.</p><p class="western" style="FONT-STYLE: normal" face="Times New Roman">Consider using Test Driven Development (TDD) to develop the presenter. It tends to result in higher test coverage, faster and more relevant tests, as well as a better code structure.</p><p class="western" face="Times New Roman"><br /></p><p class="western" face="Times New Roman"><i><span style="font-family:Times New Roman;">This week's episode by David Morgan, Christopher Semturs and Nicolas Wettstein based in Zürich, Switzerland – having a real Mountain View</span></i><a id="rmpv" title="Toilet-friendly version" href="http://code.google.com/intl/de-CH/testing/TotT-2009-08-07.pdf"><br /><br /></a></p><p class="western" face="Times New Roman"><a id="ksh_" title="Toilet-friendly version" href="http://code.google.com/intl/de-CH/testing/TotT-2009-08-07.pdf">Toilet-friendly version</a><br /></p><p class="western" face="Times New Roman"><a id="by8d" title="AsynCallbackSuccessMatcher.java" style="COLOR: rgb(85,26,139)" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackSuccessMatcher.java">AsyncCallbackSuccessMatcher.java</a></p><p class="western" style="FONT-FAMILY: Times New Roman"><a id="he0i" title="AsyncCallbackFailureMatcher.java" style="COLOR: rgb(85,26,139)" href="http://code.google.com/p/gwt-callback-matchers-for-easymock/source/browse/trunk/src/AsyncCallbackFailureMatcher.java">AsyncCallbackFailureMatcher.java</a>.<br /></p></div> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:TotT: Testing GWT without GwtTestCase&url=https://testing.googleblog.com/2009/08/tott-testing-gwt-without-gwttest.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/08/tott-testing-gwt-without-gwttest.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/08/tott-testing-gwt-without-gwttest.html#comments' style='font-weight: 500; text-decoration: underline;'>2 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/08/tott-testing-gwt-without-gwttest.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/TotT' rel='tag'> TotT </a> </span> </div> </div> </div> <div class='post' data-id='4127025554572568925' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/07/how-to-think-about-oo.html' itemprop='url' title='How to think about OO'> How to think about OO </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Friday, July 31, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> by <a href="http://misko.hevery.com/about/">Miško Hevery</a><br /> <br /> Everyone seems to think that they are writing OO after all they are using OO languages such as Java, Python or Ruby. But if you exam the code it is often procedural in nature.<br /> <br /> <strong>Static Methods</strong><br /> <br /> Static methods are procedural in nature and they have no place in OO world. I can already hear the screams, so let me explain why, but first we need to agree that <a href="http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/">global variables and state is evil</a>. If you agree with previous statement than for a static method to do something interesting it needs to have some arguments, otherwise it will always return a constant. Call to a staticMethod() must always return the same thing, if there is no global state. (Time and random, has global state, so that does not count and object instantiation may have different instance but the object graph will be wired the same way.)<br /> <br /> This means that for a static method to do something interesting it needs to have arguments. But in that case I will argue that the method simply belongs on one of its arguments. Example: Math.abs(-3) should really be -3.abs(). Now that does not imply that -3 needs to be object, only that the compiler needs to do the magic on my behalf, which BTW, Ruby got right. If you have multiple arguments you should choose the argument with which method interacts the most.<br /> <br /> But most justifications for static methods argue that they are "utility methods". Let's say that you want to have toCamelCase() method to convert string "my_workspace" to "myWorkspace". Most developers will solve this as StringUtil.toCamelCase("my_workspace"). But, again, I am going to argue that the method simply belongs to the String class and should be "my_workspace".toCamelCase(). But we can't extend the String class in Java, so we are stuck, but in many other OO languages you can add methods to existing classes.<br /> <br /> In the end I am sometimes (handful of times per year) forced to write static methods <strong>due to limitation of the language</strong>. But that is a rare event since <a href="http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/">static methods are death to testability</a>. What I do find, is that in most projects static methods are rampant.<br /> <br /> <strong>Instance Methods</strong><br /> <br /> So you got rid of all of your static methods but your codes still is procedural. OO says that code and data live together. So when one looks at code one can judge how OO it is without understanding what the code does, simply by looking at the relationship of data and code.<br /> <pre>class Database { // some fields declared here boolean isDirty(Cache cache, Object obj) { for (Object cachedObj : cache.getObjects) { if (cachedObj.equals(obj)) return false; } return true; } }</pre> <br /> The problem here is the method may as well be static! It is in the wrong place, and you can tell this because it does not interact with any of the data in the Database, instead it interacts with the data in cache which it fetches by calling the getObjects() method. My guess is that this method belongs to one of its arguments most likely Cache. If you move it to Cache you well notice that the Cache will no longer need the getObjects() method since the for loop can access the internal state of the Cache directly. Hey, we simplified the code (moved one method, deleted one method) and we have made <a href="http://misko.hevery.com/2008/07/18/breaking-the-law-of-demeter-is-like-looking-for-a-needle-in-the-haystack/">Demeter happy</a>.<br /> <br /> The funny thing about the getter methods is that it usually means that the code where the data is processed is outside of the class which has the data. In other words the code and data are not together.<br /> <pre>class Authenticator { Ldap ldap; Cookie login(User user) { if (user.isSuperUser()) { if ( ldap.auth(user.getUser(), user.getPassword()) ) return new Cookie(user.getActingAsUser()); } else (user.isAgent) { return new Cookie(user.getActingAsUser()); } else { if ( ldap.auth(user.getUser(), user.getPassword()) ) return new Cookie(user.getUser()); } return null; } }</pre> <br /> Now I don't know if this code is well written or not, but I do know that the login() method has a very high affinity to user. It interacts with the user a lot more than it interacts with its own state. Except it does not interact with user, it uses it as a dumb storage for data. Again, code lives with data is being violated. I believe that the method should be on the object with which it interacts the most, in this case on User. So lets have a look:<br /> <pre>class User { String user; String password; boolean isAgent; boolean isSuperUser; String actingAsUser; Cookie login(Ldap ldap) { if (isSuperUser) { if ( ldap.auth(user, password) ) return new Cookie(actingAsUser); } else (user.isAgent) { return new Cookie(actingAsUser); } else { if ( ldap.auth(user, password) ) return new Cookie(user); } return null; } }</pre> <br /> Ok we are making progress, notice how the need for all of the getters has disappeared, (and in this simplified example the need for the Authenticator class disappears) but there is still something wrong. The ifs branch on internal state of the object. My guess is that this code-base is riddled with if (user.isSuperUser()). The issue is that if you add a new flag you have to remember to change all of the ifs which are dispersed all over the code-base. Whenever I see If or switch on a flag I can almost always know that polymorphism is in order.<br /> <pre>class User { String user; String password; Cookie login(Ldap ldap) { if ( ldap.auth(user, password) ) return new Cookie(user); return null; } } class SuperUser extends User { String actingAsUser; Cookie login(Ldap ldap) { if ( ldap.auth(user, password) ) return new Cookie(actingAsUser); return null; } } class AgentUser extends User { String actingAsUser; Cookie login(Ldap ldap) { return new Cookie(actingAsUser); } }</pre> <br /> Now that we took advantage of polymorphism, each different kind of user knows how to log in and we can easily add new kind of user type to the system. Also notice how the user no longer has all of the flag fields which were controlling the ifs to give the user different behavior. The <a href="http://misko.hevery.com/2008/08/14/procedural-language-eliminated-gotos-oo-eliminated-ifs/">ifs and flags have disappeared</a>.<br /> <br /> Now this begs the question: should the User know about the Ldap? There are actually two questions in there. 1) should User have a field reference to Ldap? and 2) should User have compile time dependency on Ldap?<br /> <br /> Should User have a field reference to Ldap? The answer is no, because you may want to serialize the user to database but you don't want to serialize the Ldap. See <a href="http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/">here</a>.<br /> <br /> Should User have compile time dependency on Ldap? This is more complicated, but in general the answer depends on weather or not you are planning on reusing the User on a different project, since compile time dependencies are transitive in strongly typed languages. My experience is that everyone always writes code that one day they will reuse it, but that day never comes, and when it does, usually the code is entangled in other ways anyway, so code reuse after the fact just does not happen. (developing a library is different since code reuse is an explicit goal.) My point is that a lot of people pay the price of "what if" but never get any benefit out of it. Therefore don't worry abut it and make the User depend on Ldap. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> by <a href="http://misko.hevery.com/about/">Miško Hevery</a><br /> <br /> Everyone seems to think that they are writing OO after all they are using OO languages such as Java, Python or Ruby. But if you exam the code it is often procedural in nature.<br /> <br /> <strong>Static Methods</strong><br /> <br /> Static methods are procedural in nature and they have no place in OO world. I can already hear the screams, so let me explain why, but first we need to agree that <a href="http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/">global variables and state is evil</a>. If you agree with previous statement than for a static method to do something interesting it needs to have some arguments, otherwise it will always return a constant. Call to a staticMethod() must always return the same thing, if there is no global state. (Time and random, has global state, so that does not count and object instantiation may have different instance but the object graph will be wired the same way.)<br /> <br /> This means that for a static method to do something interesting it needs to have arguments. But in that case I will argue that the method simply belongs on one of its arguments. Example: Math.abs(-3) should really be -3.abs(). Now that does not imply that -3 needs to be object, only that the compiler needs to do the magic on my behalf, which BTW, Ruby got right. If you have multiple arguments you should choose the argument with which method interacts the most.<br /> <br /> But most justifications for static methods argue that they are "utility methods". Let's say that you want to have toCamelCase() method to convert string "my_workspace" to "myWorkspace". Most developers will solve this as StringUtil.toCamelCase("my_workspace"). But, again, I am going to argue that the method simply belongs to the String class and should be "my_workspace".toCamelCase(). But we can't extend the String class in Java, so we are stuck, but in many other OO languages you can add methods to existing classes.<br /> <br /> In the end I am sometimes (handful of times per year) forced to write static methods <strong>due to limitation of the language</strong>. But that is a rare event since <a href="http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/">static methods are death to testability</a>. What I do find, is that in most projects static methods are rampant.<br /> <br /> <strong>Instance Methods</strong><br /> <br /> So you got rid of all of your static methods but your codes still is procedural. OO says that code and data live together. So when one looks at code one can judge how OO it is without understanding what the code does, simply by looking at the relationship of data and code.<br /> <pre>class Database { // some fields declared here boolean isDirty(Cache cache, Object obj) { for (Object cachedObj : cache.getObjects) { if (cachedObj.equals(obj)) return false; } return true; } }</pre> <br /> The problem here is the method may as well be static! It is in the wrong place, and you can tell this because it does not interact with any of the data in the Database, instead it interacts with the data in cache which it fetches by calling the getObjects() method. My guess is that this method belongs to one of its arguments most likely Cache. If you move it to Cache you well notice that the Cache will no longer need the getObjects() method since the for loop can access the internal state of the Cache directly. Hey, we simplified the code (moved one method, deleted one method) and we have made <a href="http://misko.hevery.com/2008/07/18/breaking-the-law-of-demeter-is-like-looking-for-a-needle-in-the-haystack/">Demeter happy</a>.<br /> <br /> The funny thing about the getter methods is that it usually means that the code where the data is processed is outside of the class which has the data. In other words the code and data are not together.<br /> <pre>class Authenticator { Ldap ldap; Cookie login(User user) { if (user.isSuperUser()) { if ( ldap.auth(user.getUser(), user.getPassword()) ) return new Cookie(user.getActingAsUser()); } else (user.isAgent) { return new Cookie(user.getActingAsUser()); } else { if ( ldap.auth(user.getUser(), user.getPassword()) ) return new Cookie(user.getUser()); } return null; } }</pre> <br /> Now I don't know if this code is well written or not, but I do know that the login() method has a very high affinity to user. It interacts with the user a lot more than it interacts with its own state. Except it does not interact with user, it uses it as a dumb storage for data. Again, code lives with data is being violated. I believe that the method should be on the object with which it interacts the most, in this case on User. So lets have a look:<br /> <pre>class User { String user; String password; boolean isAgent; boolean isSuperUser; String actingAsUser; Cookie login(Ldap ldap) { if (isSuperUser) { if ( ldap.auth(user, password) ) return new Cookie(actingAsUser); } else (user.isAgent) { return new Cookie(actingAsUser); } else { if ( ldap.auth(user, password) ) return new Cookie(user); } return null; } }</pre> <br /> Ok we are making progress, notice how the need for all of the getters has disappeared, (and in this simplified example the need for the Authenticator class disappears) but there is still something wrong. The ifs branch on internal state of the object. My guess is that this code-base is riddled with if (user.isSuperUser()). The issue is that if you add a new flag you have to remember to change all of the ifs which are dispersed all over the code-base. Whenever I see If or switch on a flag I can almost always know that polymorphism is in order.<br /> <pre>class User { String user; String password; Cookie login(Ldap ldap) { if ( ldap.auth(user, password) ) return new Cookie(user); return null; } } class SuperUser extends User { String actingAsUser; Cookie login(Ldap ldap) { if ( ldap.auth(user, password) ) return new Cookie(actingAsUser); return null; } } class AgentUser extends User { String actingAsUser; Cookie login(Ldap ldap) { return new Cookie(actingAsUser); } }</pre> <br /> Now that we took advantage of polymorphism, each different kind of user knows how to log in and we can easily add new kind of user type to the system. Also notice how the user no longer has all of the flag fields which were controlling the ifs to give the user different behavior. The <a href="http://misko.hevery.com/2008/08/14/procedural-language-eliminated-gotos-oo-eliminated-ifs/">ifs and flags have disappeared</a>.<br /> <br /> Now this begs the question: should the User know about the Ldap? There are actually two questions in there. 1) should User have a field reference to Ldap? and 2) should User have compile time dependency on Ldap?<br /> <br /> Should User have a field reference to Ldap? The answer is no, because you may want to serialize the user to database but you don't want to serialize the Ldap. See <a href="http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/">here</a>.<br /> <br /> Should User have compile time dependency on Ldap? This is more complicated, but in general the answer depends on weather or not you are planning on reusing the User on a different project, since compile time dependencies are transitive in strongly typed languages. My experience is that everyone always writes code that one day they will reuse it, but that day never comes, and when it does, usually the code is entangled in other ways anyway, so code reuse after the fact just does not happen. (developing a library is different since code reuse is an explicit goal.) My point is that a lot of people pay the price of "what if" but never get any benefit out of it. Therefore don't worry abut it and make the User depend on Ldap. <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:How to think about OO&url=https://testing.googleblog.com/2009/07/how-to-think-about-oo.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/07/how-to-think-about-oo.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/07/how-to-think-about-oo.html#comments' style='font-weight: 500; text-decoration: underline;'>19 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/07/how-to-think-about-oo.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/Misko%20Hevery' rel='tag'> Misko Hevery </a> </span> </div> </div> </div> <div class='post' data-id='2008207006708361742' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/07/call-for-attendance-google-test.html' itemprop='url' title='Call for Attendance: Google Test Automation Conference (GTAC) 2009'> Call for Attendance: Google Test Automation Conference (GTAC) 2009 </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, July 29, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <p><span class="Apple-style-span" style="WORD-SPACING: 0px; FONT: 16px 'Times New Roman'; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; -webkit-text-size-adjust: auto; orphans: 2; widows: 2; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="BORDER-COLLAPSE: collapse;font-family:arial;" ><span style="font-size:85%;">by Juergen Allgayer, Conference Chair</span></span></span></p><p><span class="Apple-style-span" style="WORD-SPACING: 0px; FONT: 16px 'Times New Roman'; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; -webkit-text-size-adjust: auto; orphans: 2; widows: 2; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="BORDER-COLLAPSE: collapse;font-family:arial;" ><span style="font-size:85%;">Testing for the Web is the theme of the 4th Google Test Automation Conference (</span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank"><span style="font-size:85%;">GTAC</span></a><span style="font-size:85%;">), to be held in Zurich, October 21-22.<span class="Apple-converted-space"> </span><br /><br />We are happy to announce that we are now accepting applications for attendance. The success of the conference depends on active participation of the attendees. Because the available spaces for the conference are limited, we ask each person to apply for attendance. Since we aim for a balanced audience of seasoned practitioners, students and academics, we ask the applicants to provide a brief background statement.<br /><br /><br /><b>How to apply</b><br />Please visit<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-attendance" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/call-for-<wbr>attendance</span></a><span style="font-size:85%;"><span class="Apple-converted-space"> </span>to apply for a attendance.<br /><br /></span></span></span><span class="Apple-style-span" style="WORD-SPACING: 0px; FONT: 16px 'Times New Roman'; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; -webkit-text-size-adjust: auto; orphans: 2; widows: 2; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="BORDER-COLLAPSE: collapse;font-family:arial;" ><b><br /><span style="font-size:85%;">Deadline</span></b><span style="font-size:85%;"><br />Please submit your application until August 28, 2009 at the latest.<br /><br /></span><b><br /><span style="font-size:85%;">Registration Fees</span></b><span style="font-size:85%;"><br />There are no registration fees. We will send out detailed registration instructions to each invited applicant. We will provide breakfast and lunch. There will be a reception on the evening of October 21.<br /><br /></span><b><br /><span style="font-size:85%;">Cancellation</span></b><span style="font-size:85%;"><br />If you applied but can no longer attend the conference please notify us<br />immediately by sending an email to<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="mailto:gtac-2009-cfa@google.com" target="_blank"><span style="font-size:85%;">gtac-2009-cfa@google.com</span></a><span style="font-size:85%;"><span class="Apple-converted-space"> </span>so<br />someone from the waiting list can get the opportunity instead.<br /><br /><br /><b>Further information</b><br />General website:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/</span></a><span style="font-size:85%;"><br />Call for proposals:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-proposals" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/call-for-<wbr>proposals</span></a><span style="font-size:85%;"><br />Call for attendance:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-attendance" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/call-for-<wbr>attendance</span></a><span style="font-size:85%;"><br />Accommodations:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/accomodations" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/<wbr>accomodations</span></a></span></span></p> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <p><span class="Apple-style-span" style="WORD-SPACING: 0px; FONT: 16px 'Times New Roman'; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; -webkit-text-size-adjust: auto; orphans: 2; widows: 2; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="BORDER-COLLAPSE: collapse;font-family:arial;" ><span style="font-size:85%;">by Juergen Allgayer, Conference Chair</span></span></span></p><p><span class="Apple-style-span" style="WORD-SPACING: 0px; FONT: 16px 'Times New Roman'; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; -webkit-text-size-adjust: auto; orphans: 2; widows: 2; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="BORDER-COLLAPSE: collapse;font-family:arial;" ><span style="font-size:85%;">Testing for the Web is the theme of the 4th Google Test Automation Conference (</span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank"><span style="font-size:85%;">GTAC</span></a><span style="font-size:85%;">), to be held in Zurich, October 21-22.<span class="Apple-converted-space"> </span><br /><br />We are happy to announce that we are now accepting applications for attendance. The success of the conference depends on active participation of the attendees. Because the available spaces for the conference are limited, we ask each person to apply for attendance. Since we aim for a balanced audience of seasoned practitioners, students and academics, we ask the applicants to provide a brief background statement.<br /><br /><br /><b>How to apply</b><br />Please visit<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-attendance" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/call-for-<wbr>attendance</span></a><span style="font-size:85%;"><span class="Apple-converted-space"> </span>to apply for a attendance.<br /><br /></span></span></span><span class="Apple-style-span" style="WORD-SPACING: 0px; FONT: 16px 'Times New Roman'; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; -webkit-text-size-adjust: auto; orphans: 2; widows: 2; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px"><span class="Apple-style-span" style="BORDER-COLLAPSE: collapse;font-family:arial;" ><b><br /><span style="font-size:85%;">Deadline</span></b><span style="font-size:85%;"><br />Please submit your application until August 28, 2009 at the latest.<br /><br /></span><b><br /><span style="font-size:85%;">Registration Fees</span></b><span style="font-size:85%;"><br />There are no registration fees. We will send out detailed registration instructions to each invited applicant. We will provide breakfast and lunch. There will be a reception on the evening of October 21.<br /><br /></span><b><br /><span style="font-size:85%;">Cancellation</span></b><span style="font-size:85%;"><br />If you applied but can no longer attend the conference please notify us<br />immediately by sending an email to<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="mailto:gtac-2009-cfa@google.com" target="_blank"><span style="font-size:85%;">gtac-2009-cfa@google.com</span></a><span style="font-size:85%;"><span class="Apple-converted-space"> </span>so<br />someone from the waiting list can get the opportunity instead.<br /><br /><br /><b>Further information</b><br />General website:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/</span></a><span style="font-size:85%;"><br />Call for proposals:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-proposals" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/call-for-<wbr>proposals</span></a><span style="font-size:85%;"><br />Call for attendance:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-attendance" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/call-for-<wbr>attendance</span></a><span style="font-size:85%;"><br />Accommodations:<span class="Apple-converted-space"> </span></span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/accomodations" target="_blank"><span style="font-size:85%;">http://www.gtac.biz/<wbr>accomodations</span></a></span></span></p> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Call for Attendance: Google Test Automation Conference (GTAC) 2009&url=https://testing.googleblog.com/2009/07/call-for-attendance-google-test.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/07/call-for-attendance-google-test.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/07/call-for-attendance-google-test.html#comments' style='font-weight: 500; text-decoration: underline;'>1 comment</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/07/call-for-attendance-google-test.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/GTAC' rel='tag'> GTAC </a> </span> </div> </div> </div> <div class='post' data-id='8474638822530544537' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/07/plague-of-blindness.html' itemprop='url' title='The Plague of Blindness'> The Plague of Blindness </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Wednesday, July 29, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <span class="Apple-style-span" style="font-family:Verdana;font-size:13;"><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:130%;"><span class="Apple-style-span" style="font-size:16;">By James A. Whittaker</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:130%;"><span class="Apple-style-span" style="font-size:16;"><br /></span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;">And now for the last plague in this series. I hope you enjoyed them (the posts ...not the plagues!)</span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">Imagine playing a video game blindfolded or even with the heads up display turned off. You cannot monitor your character's health, your targeting system is gone. There is no look ahead radar and no advance warning of any kind. In gaming, the inability to access information about the campaign world is debilitating and a good way to get your character killed. </span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">There are many aspects of testing software that fall into this invisible spectrum. Software itself is invisible. We see it only through the </span><span class="Apple-style-span" style="font-size:medium;">UI</span><span class="Apple-style-span" style="font-size:medium;"> with much of what is happening doing so under the covers and out of our line of sight. It’s not like building a car in which you can clearly see missing pieces and many engineers can look at a car and get the exact same view of it. There is no arguing whether the car has a bumper installed, it is in plain sight for everyone involved to </span></span><i><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">see</span></span></i><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">. Not so with software which exists as magnetic fluctuations on storage media. It’s not a helpful visual.</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;">Software testing is much like game playing while blindfolded. We can't see bugs; we can't see coverage; we can't see code changes. This information, so valuable to us as testers, is hidden in useless static reports. If someone outfitted us with an actual blindfold, we might not even notice. </span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">This blindness concerning our product and its behavior creates some very real problems for the software tester. Which parts of the software have enjoyed the most unit testing? Which parts have changed from one build to the next? Which parts have existing bugs posted against them? What part of the software does a specific test case cover? Which parts have been tested thoroughly and which parts have received no attention whatsoever?</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">Our folk remedy for the blindness plague has always been to measure code coverage, </span><span class="Apple-style-span" style="font-size:medium;">API</span><span class="Apple-style-span" style="font-size:medium;">/method coverage or </span><span class="Apple-style-span" style="font-size:medium;">UI</span><span class="Apple-style-span" style="font-size:medium;"> coverage. We pick the things we can see the best and measure them, but do they really tell us anything? We’</span><span class="Apple-style-span" style="font-size:medium;">ve</span><span class="Apple-style-span" style="font-size:medium;"> been doing it this way for years not because it is insightful, but simply because it is all our blindness will allow us to do. We’re interacting with our application under test a great deal, but we must rely on other, less concrete senses for any feedback about our effort.</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;">Software testers could learn a lot from the world of gaming. Turn on your heads up display and see the information you've been blind to. There's power in information. </span></p></span> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <span class="Apple-style-span" style="font-family:Verdana;font-size:13;"><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:130%;"><span class="Apple-style-span" style="font-size:16;">By James A. Whittaker</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:130%;"><span class="Apple-style-span" style="font-size:16;"><br /></span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;">And now for the last plague in this series. I hope you enjoyed them (the posts ...not the plagues!)</span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">Imagine playing a video game blindfolded or even with the heads up display turned off. You cannot monitor your character's health, your targeting system is gone. There is no look ahead radar and no advance warning of any kind. In gaming, the inability to access information about the campaign world is debilitating and a good way to get your character killed. </span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">There are many aspects of testing software that fall into this invisible spectrum. Software itself is invisible. We see it only through the </span><span class="Apple-style-span" style="font-size:medium;">UI</span><span class="Apple-style-span" style="font-size:medium;"> with much of what is happening doing so under the covers and out of our line of sight. It’s not like building a car in which you can clearly see missing pieces and many engineers can look at a car and get the exact same view of it. There is no arguing whether the car has a bumper installed, it is in plain sight for everyone involved to </span></span><i><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">see</span></span></i><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">. Not so with software which exists as magnetic fluctuations on storage media. It’s not a helpful visual.</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;">Software testing is much like game playing while blindfolded. We can't see bugs; we can't see coverage; we can't see code changes. This information, so valuable to us as testers, is hidden in useless static reports. If someone outfitted us with an actual blindfold, we might not even notice. </span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">This blindness concerning our product and its behavior creates some very real problems for the software tester. Which parts of the software have enjoyed the most unit testing? Which parts have changed from one build to the next? Which parts have existing bugs posted against them? What part of the software does a specific test case cover? Which parts have been tested thoroughly and which parts have received no attention whatsoever?</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span style="font-size:+0;"><span class="Apple-style-span" style="font-size:medium;">Our folk remedy for the blindness plague has always been to measure code coverage, </span><span class="Apple-style-span" style="font-size:medium;">API</span><span class="Apple-style-span" style="font-size:medium;">/method coverage or </span><span class="Apple-style-span" style="font-size:medium;">UI</span><span class="Apple-style-span" style="font-size:medium;"> coverage. We pick the things we can see the best and measure them, but do they really tell us anything? We’</span><span class="Apple-style-span" style="font-size:medium;">ve</span><span class="Apple-style-span" style="font-size:medium;"> been doing it this way for years not because it is insightful, but simply because it is all our blindness will allow us to do. We’re interacting with our application under test a great deal, but we must rely on other, less concrete senses for any feedback about our effort.</span></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;"><br /></span></p><p class="MsoNormal" style="MARGIN: 0px"><span class="Apple-style-span" style="font-size:medium;">Software testers could learn a lot from the world of gaming. Turn on your heads up display and see the information you've been blind to. There's power in information. </span></p></span> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:The Plague of Blindness&url=https://testing.googleblog.com/2009/07/plague-of-blindness.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/07/plague-of-blindness.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/07/plague-of-blindness.html#comments' style='font-weight: 500; text-decoration: underline;'>12 comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/07/plague-of-blindness.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/James%20Whittaker' rel='tag'> James Whittaker </a> </span> </div> </div> </div> <div class='post' data-id='4488802826080153097' itemscope='' itemtype='http://schema.org/BlogPosting'> <h2 class='title' itemprop='name'> <a href='https://testing.googleblog.com/2009/07/update-gtac-keynote-speakers-niklaus.html' itemprop='url' title='Update! GTAC Keynote Speakers: Niklaus Wirth and Alberto di Meglio'> Update! GTAC Keynote Speakers: Niklaus Wirth and Alberto di Meglio </a> </h2> <div class='post-header'> <div class='published'> <span class='publishdate' itemprop='datePublished'> Friday, July 24, 2009 </span> </div> </div> <div class='post-body'> <div class='post-content' itemprop='articleBody'> <script type='text/template'> <span style="font-family:arial;">by <span class="blsp-spelling-error" id="SPELLING_ERROR_0">Juergen</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_1">Allgayer</span>, Conference Chair</span><br /><span style="font-family:arial;"></span><br /><span style="font-family:arial;">We are thrilled to announce that <span class="blsp-spelling-error" id="SPELLING_ERROR_2">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_3">Wirth</span> and Alberto <span class="blsp-spelling-error" id="SPELLING_ERROR_4">di</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Meglio</span> are this years keynote speakers at the 4<span class="blsp-spelling-error" id="SPELLING_ERROR_6">th</span> Google Test Automation Conference (</span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank"><span style="font-family:arial;"><span class="blsp-spelling-error" id="SPELLING_ERROR_7">GTAC</span></span></a><span style="font-family:arial;">).</span><br /><span style="font-family:arial;"></span><br /><strong><span style="font-family:arial;"><span class="blsp-spelling-error" id="SPELLING_ERROR_8">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_9">Wirth</span></span></strong><br /><span style="font-family:arial;">Prof. <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Wirth</span>, is the designer of several programing languages and operating systems, including Pascal and Oberon. He received many awards including the Turing award, is author of many books and articles such as "Program Development by Stepwise Refinement" and "Algorithms + Data Structures = Programs". Prof. <span class="blsp-spelling-error" id="SPELLING_ERROR_12">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_13">Wirth</span> served as professor at Stanford, University of Zurich, and <span class="blsp-spelling-error" id="SPELLING_ERROR_14">ETH</span> Zurich.</span><br /><span style="font-family:arial;"></span><br /><strong><span style="font-family:arial;">Alberto <span class="blsp-spelling-error" id="SPELLING_ERROR_15">di</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_16">Meglio</span></span></strong><br /><span style="font-family:arial;">In 2003, Dr. Alberto <span class="blsp-spelling-error" id="SPELLING_ERROR_17">di</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_18">Meglio</span> was appointed by <span class="blsp-spelling-error" id="SPELLING_ERROR_19">CERN</span> as Software Integration Manager in the <span class="blsp-spelling-error" id="SPELLING_ERROR_20">Middleware</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_21">Reengineering</span> Activity of the first Enabling Grids for E-science (<span class="blsp-spelling-error" id="SPELLING_ERROR_22">EGEE</span>) project. At the end of the <span class="blsp-spelling-error" id="SPELLING_ERROR_23">EGEE</span> project, thanks to the very successful results obtained with the integration and testing tools and procedures developed for the grid <span class="blsp-spelling-error" id="SPELLING_ERROR_24">middleware</span> developed by <span class="blsp-spelling-error" id="SPELLING_ERROR_25">EGEE</span>, Alberto set up Infrastructure for Testing, Integration and Configuration of Software, an international infrastructure co-funded by EC <span class="blsp-spelling-error" id="SPELLING_ERROR_26">FP</span>7 (European Commission: <span class="blsp-spelling-error" id="SPELLING_ERROR_27">CORDIS</span> - Seventh Framework Programme) for building and testing software on the grid, of which he is currently Project Director.</span><br /><span style="font-family:arial;"></span><br /><strong><span style="font-family:arial;">Reminder: Call for proposals</span></strong><br /><span style="font-family:arial;">If you would like to give a talk at <span class="blsp-spelling-error" id="SPELLING_ERROR_28">GTAC</span> please remember to submit your proposal until August 1 at the latest. Please visit </span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-proposals" target="_blank"><span style="font-family:arial;">http://www.gtac.biz/call-for-proposals</span></a><span style="font-family:arial;"> for details.</span> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </script> <noscript> <span style="font-family:arial;">by <span class="blsp-spelling-error" id="SPELLING_ERROR_0">Juergen</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_1">Allgayer</span>, Conference Chair</span><br /><span style="font-family:arial;"></span><br /><span style="font-family:arial;">We are thrilled to announce that <span class="blsp-spelling-error" id="SPELLING_ERROR_2">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_3">Wirth</span> and Alberto <span class="blsp-spelling-error" id="SPELLING_ERROR_4">di</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Meglio</span> are this years keynote speakers at the 4<span class="blsp-spelling-error" id="SPELLING_ERROR_6">th</span> Google Test Automation Conference (</span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/" target="_blank"><span style="font-family:arial;"><span class="blsp-spelling-error" id="SPELLING_ERROR_7">GTAC</span></span></a><span style="font-family:arial;">).</span><br /><span style="font-family:arial;"></span><br /><strong><span style="font-family:arial;"><span class="blsp-spelling-error" id="SPELLING_ERROR_8">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_9">Wirth</span></span></strong><br /><span style="font-family:arial;">Prof. <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Wirth</span>, is the designer of several programing languages and operating systems, including Pascal and Oberon. He received many awards including the Turing award, is author of many books and articles such as "Program Development by Stepwise Refinement" and "Algorithms + Data Structures = Programs". Prof. <span class="blsp-spelling-error" id="SPELLING_ERROR_12">Niklaus</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_13">Wirth</span> served as professor at Stanford, University of Zurich, and <span class="blsp-spelling-error" id="SPELLING_ERROR_14">ETH</span> Zurich.</span><br /><span style="font-family:arial;"></span><br /><strong><span style="font-family:arial;">Alberto <span class="blsp-spelling-error" id="SPELLING_ERROR_15">di</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_16">Meglio</span></span></strong><br /><span style="font-family:arial;">In 2003, Dr. Alberto <span class="blsp-spelling-error" id="SPELLING_ERROR_17">di</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_18">Meglio</span> was appointed by <span class="blsp-spelling-error" id="SPELLING_ERROR_19">CERN</span> as Software Integration Manager in the <span class="blsp-spelling-error" id="SPELLING_ERROR_20">Middleware</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_21">Reengineering</span> Activity of the first Enabling Grids for E-science (<span class="blsp-spelling-error" id="SPELLING_ERROR_22">EGEE</span>) project. At the end of the <span class="blsp-spelling-error" id="SPELLING_ERROR_23">EGEE</span> project, thanks to the very successful results obtained with the integration and testing tools and procedures developed for the grid <span class="blsp-spelling-error" id="SPELLING_ERROR_24">middleware</span> developed by <span class="blsp-spelling-error" id="SPELLING_ERROR_25">EGEE</span>, Alberto set up Infrastructure for Testing, Integration and Configuration of Software, an international infrastructure co-funded by EC <span class="blsp-spelling-error" id="SPELLING_ERROR_26">FP</span>7 (European Commission: <span class="blsp-spelling-error" id="SPELLING_ERROR_27">CORDIS</span> - Seventh Framework Programme) for building and testing software on the grid, of which he is currently Project Director.</span><br /><span style="font-family:arial;"></span><br /><strong><span style="font-family:arial;">Reminder: Call for proposals</span></strong><br /><span style="font-family:arial;">If you would like to give a talk at <span class="blsp-spelling-error" id="SPELLING_ERROR_28">GTAC</span> please remember to submit your proposal until August 1 at the latest. Please visit </span><a style="COLOR: rgb(42,93,176)" href="http://www.gtac.biz/call-for-proposals" target="_blank"><span style="font-family:arial;">http://www.gtac.biz/call-for-proposals</span></a><span style="font-family:arial;"> for details.</span> <span itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://plus.google.com/116899029375914044550' itemprop='url'/> </span> </noscript> </div> </div> <div class='share'> <span class='twitter-custom social-wrapper' data-href='http://twitter.com/share?text=Google Testing Blog:Update! GTAC Keynote Speakers: Niklaus Wirth and Alberto di Meglio&url=https://testing.googleblog.com/2009/07/update-gtac-keynote-speakers-niklaus.html&via=googletesting'> <img alt='Share on Twitter' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png' width='24'/> </span> <span class='fb-custom social-wrapper' data-href='https://www.facebook.com/sharer.php?u=https://testing.googleblog.com/2009/07/update-gtac-keynote-speakers-niklaus.html'> <img alt='Share on Facebook' height='24' src='https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png' width='24'/> </span> </div> <div class='comment-container'> <i class='comment-img material-icons'>  </i> <a href='https://testing.googleblog.com/2009/07/update-gtac-keynote-speakers-niklaus.html#comments' style='font-weight: 500; text-decoration: underline;'>No comments</a> </div> <div class='post-footer'> <div class='cmt_iframe_holder' data-href='https://testing.googleblog.com/2009/07/update-gtac-keynote-speakers-niklaus.html' data-viewtype='FILTERED_POSTMOD'></div> <a href='https://plus.google.com/112374322230920073195' rel='author' style='display:none;'> Google </a> <div class='label-footer'> <span class='labels-caption'> Labels: </span> <span class='labels'> <a class='label' href='https://testing.googleblog.com/search/label/GTAC' rel='tag'> GTAC </a> </span> </div> </div> </div> <div class='blog-pager' id='blog-pager'> <a class='home-link' href='https://testing.googleblog.com/'> <i class='material-icons'>  </i> </a> <span id='blog-pager-newer-link'> <a class='blog-pager-newer-link' href='https://testing.googleblog.com/search?updated-max=2010-03-12T20:56:00-08:00&max-results=5&reverse-paginate=true' id='Blog1_blog-pager-newer-link' title='Newer Posts'> <i class='material-icons'>  </i> </a> </span> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' href='https://testing.googleblog.com/search?updated-max=2009-07-24T16:22:00-07:00&max-results=5' id='Blog1_blog-pager-older-link' title='Older Posts'> <i class='material-icons'>  </i> </a> </span> </div> <div class='clear'></div> </div></div> </div> </div> <div class='col-right'> <div class='section' id='sidebar-top'><div class='widget HTML' data-version='1' id='HTML8'> <div class='widget-content'> <div class='searchBox'> <input type='text' title='Search This Blog' placeholder='Search blog ...' /> </div> </div> <div class='clear'></div> </div> </div> <div id='aside'> <div class='section' id='sidebar'><div class='widget Label' data-version='1' id='Label1'> <div class='tab'> <img class='sidebar-icon' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYpJREFUeNrs2aFuwzAQBmAvKRkMKRjZA4QMDJaWFgyMjuzFRg37DIUlA3uFkoGQSaWzJU+tpri5O9+l/zSfdFJlpe59yTmyVedq1PjfcZMZ70NuQnaF8w8htyE/rABtpviXkLcK88c5HhLkMBfgVan43zfFBNGMjHVGT/s55KP2pAvidbGHd+nzKt1RKSLG3rKF1iPFv6UWiPke8i7kEqGdGsI1O+LYVdqJAjgirwkKYD0ytkJBUNbAMvX8V3q9PhUsYvU1sWD8SO/sQvx2ahxOiNoJCSBCoAHYCEQAC4EKICOQASQEOmAS8RcAFxFN5hiIiugpgC3wk9hQAHH/70EBHXUN7IER5EWMiBgo2+nzOKQv9SCAeEM/OQAkhE/ncccFICB87qzQMia5FsJfOui0zMnmRvipU1ormHQuxGTxUsAcCFLxJQBLBLn4UoAFglW8BkATwS5eC6CBEBWvCShBiIvXBkgQRcVbADiI4uKtABSESvGWgB9EzHt3+tNwyO0qa9SoIYtvAQYAqDJhaWWeMecAAAAASUVORK5CYII='/> <h2> Labels </h2> <i class='material-icons arrow'>  </i> </div> <div class='widget-content list-label-widget-content'> <ul> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/TotT'> TotT </a> <span dir='ltr'> 102 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/GTAC'> GTAC </a> <span dir='ltr'> 61 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/James%20Whittaker'> James Whittaker </a> <span dir='ltr'> 42 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Misko%20Hevery'> Misko Hevery </a> <span dir='ltr'> 32 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Code%20Health'> Code Health </a> <span dir='ltr'> 30 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Anthony%20Vallone'> Anthony Vallone </a> <span dir='ltr'> 27 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Patrick%20Copeland'> Patrick Copeland </a> <span dir='ltr'> 23 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jobs'> Jobs </a> <span dir='ltr'> 18 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Andrew%20Trenk'> Andrew Trenk </a> <span dir='ltr'> 12 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/C%2B%2B'> C++ </a> <span dir='ltr'> 11 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Patrik%20H%C3%B6glund'> Patrik Höglund </a> <span dir='ltr'> 8 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/JavaScript'> JavaScript </a> <span dir='ltr'> 7 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Allen%20Hutchison'> Allen Hutchison </a> <span dir='ltr'> 6 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/George%20Pirocanac'> George Pirocanac </a> <span dir='ltr'> 6 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Zhanyong%20Wan'> Zhanyong Wan </a> <span dir='ltr'> 6 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Harry%20Robinson'> Harry Robinson </a> <span dir='ltr'> 5 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Java'> Java </a> <span dir='ltr'> 5 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Julian%20Harty'> Julian Harty </a> <span dir='ltr'> 5 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Adam%20Bender'> Adam Bender </a> <span dir='ltr'> 4 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Alberto%20Savoia'> Alberto Savoia </a> <span dir='ltr'> 4 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Ben%20Yu'> Ben Yu </a> <span dir='ltr'> 4 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Erik%20Kuefler'> Erik Kuefler </a> <span dir='ltr'> 4 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Philip%20Zembrod'> Philip Zembrod </a> <span dir='ltr'> 4 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Shyam%20Seshadri'> Shyam Seshadri </a> <span dir='ltr'> 4 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Chrome'> Chrome </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dillon%20Bly'> Dillon Bly </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/John%20Thomas'> John Thomas </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Lesley%20Katzen'> Lesley Katzen </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Marc%20Kaplan'> Marc Kaplan </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Markus%20Clermont'> Markus Clermont </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Max%20Kanat-Alexander'> Max Kanat-Alexander </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Sonal%20Shah'> Sonal Shah </a> <span dir='ltr'> 3 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/APIs'> APIs </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Abhishek%20Arya'> Abhishek Arya </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Alan%20Myrvold'> Alan Myrvold </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Alek%20Icev'> Alek Icev </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Android'> Android </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/April%20Fools'> April Fools </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Chaitali%20Narla'> Chaitali Narla </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Chris%20Lewis'> Chris Lewis </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Chrome%20OS'> Chrome OS </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Diego%20Salas'> Diego Salas </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dori%20Reuveni'> Dori Reuveni </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jason%20Arbon'> Jason Arbon </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jochen%20Wuttke'> Jochen Wuttke </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kostya%20Serebryany'> Kostya Serebryany </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Marc%20Eaddy'> Marc Eaddy </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Marko%20Ivankovi%C4%87'> Marko Ivanković </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mobile'> Mobile </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Oliver%20Chang'> Oliver Chang </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Simon%20Stewart'> Simon Stewart </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Stefan%20Kennedy'> Stefan Kennedy </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Test%20Flakiness'> Test Flakiness </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Titus%20Winters'> Titus Winters </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Tony%20Voellm'> Tony Voellm </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/WebRTC'> WebRTC </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Yiming%20Sun'> Yiming Sun </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Yvette%20Nameth'> Yvette Nameth </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Zuri%20Kemp'> Zuri Kemp </a> <span dir='ltr'> 2 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Aaron%20Jacobs'> Aaron Jacobs </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Adam%20Porter'> Adam Porter </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Adam%20Raider'> Adam Raider </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Adel%20Saoud'> Adel Saoud </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Alan%20Faulkner'> Alan Faulkner </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Alex%20Eagle'> Alex Eagle </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Amy%20Fu'> Amy Fu </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Anantha%20Keesara'> Anantha Keesara </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Antoine%20Picard'> Antoine Picard </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/App%20Engine'> App Engine </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Ari%20Shamash'> Ari Shamash </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Arif%20Sukoco'> Arif Sukoco </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Benjamin%20Pick'> Benjamin Pick </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Bob%20Nystrom'> Bob Nystrom </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Bruce%20Leban'> Bruce Leban </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Carlos%20Arguelles'> Carlos Arguelles </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Carlos%20Israel%20Ortiz%20Garc%C3%ADa'> Carlos Israel Ortiz García </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Cathal%20Weakliam'> Cathal Weakliam </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Christopher%20Semturs'> Christopher Semturs </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Clay%20Murphy'> Clay Murphy </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dagang%20Wei'> Dagang Wei </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dan%20Maksimovich'> Dan Maksimovich </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dan%20Shi'> Dan Shi </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dan%20Willemsen'> Dan Willemsen </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dave%20Chen'> Dave Chen </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dave%20Gladfelter'> Dave Gladfelter </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/David%20Bendory'> David Bendory </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/David%20Mandelberg'> David Mandelberg </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Derek%20Snyder'> Derek Snyder </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Diego%20Cavalcanti'> Diego Cavalcanti </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Dmitry%20Vyukov'> Dmitry Vyukov </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Eduardo%20Bravo%20Ortiz'> Eduardo Bravo Ortiz </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Ekaterina%20Kamenskaya'> Ekaterina Kamenskaya </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Elliott%20Karpilovsky'> Elliott Karpilovsky </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Elliotte%20Rusty%20Harold'> Elliotte Rusty Harold </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Espresso'> Espresso </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Felipe%20Sodr%C3%A9'> Felipe Sodré </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Francois%20Aube'> Francois Aube </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Gene%20Volovich'> Gene Volovich </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Google%2B'> Google+ </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Goran%20Petrovic'> Goran Petrovic </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Goranka%20Bjedov'> Goranka Bjedov </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Hank%20Duan'> Hank Duan </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Havard%20Rast%20Blok'> Havard Rast Blok </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Hongfei%20Ding'> Hongfei Ding </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jason%20Elbaum'> Jason Elbaum </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jason%20Huggins'> Jason Huggins </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jay%20Han'> Jay Han </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jeff%20Hoy'> Jeff Hoy </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jeff%20Listfield'> Jeff Listfield </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jessica%20Tomechak'> Jessica Tomechak </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jim%20Reardon'> Jim Reardon </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Joe%20Allan%20Muharsky'> Joe Allan Muharsky </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Joel%20Hynoski'> Joel Hynoski </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/John%20Micco'> John Micco </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/John%20Penix'> John Penix </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jonathan%20Rockway'> Jonathan Rockway </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Jonathan%20Velasquez'> Jonathan Velasquez </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Josh%20Armour'> Josh Armour </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Julie%20Ralph'> Julie Ralph </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kai%20Kent'> Kai Kent </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Karin%20Lundberg'> Karin Lundberg </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kaue%20Silveira'> Kaue Silveira </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kevin%20Bourrillion'> Kevin Bourrillion </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kevin%20Graney'> Kevin Graney </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kirkland'> Kirkland </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Kurt%20Alfred%20Kluever'> Kurt Alfred Kluever </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Manjusha%20Parvathaneni'> Manjusha Parvathaneni </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Marek%20Kiszkis'> Marek Kiszkis </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Marius%20Latinis'> Marius Latinis </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mark%20Ivey'> Mark Ivey </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mark%20Manley'> Mark Manley </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mark%20Striebeck'> Mark Striebeck </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Matt%20Lowrie'> Matt Lowrie </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Meredith%20Whittaker'> Meredith Whittaker </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Michael%20Bachman'> Michael Bachman </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Michael%20Klepikov'> Michael Klepikov </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mike%20Aizatsky'> Mike Aizatsky </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mike%20Wacker'> Mike Wacker </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Mona%20El%20Mahdy'> Mona El Mahdy </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Noel%20Yap'> Noel Yap </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Palak%20Bansal'> Palak Bansal </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Patricia%20Legaspi'> Patricia Legaspi </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Per%20Jacobsson'> Per Jacobsson </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Peter%20Arrenbrecht'> Peter Arrenbrecht </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Peter%20Spragins'> Peter Spragins </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Phil%20Norman'> Phil Norman </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Phil%20Rollet'> Phil Rollet </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Pooja%20Gupta'> Pooja Gupta </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Project%20Showcase'> Project Showcase </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Radoslav%20Vasilev'> Radoslav Vasilev </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Rajat%20Dewan'> Rajat Dewan </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Rajat%20Jain'> Rajat Jain </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Rich%20Martin'> Rich Martin </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Richard%20Bustamante'> Richard Bustamante </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Roshan%20Sembacuttiaratchy'> Roshan Sembacuttiaratchy </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Ruslan%20Khamitov'> Ruslan Khamitov </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Sam%20Lee'> Sam Lee </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Sean%20Jordan'> Sean Jordan </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Sharon%20Zhou'> Sharon Zhou </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Shiva%20Garg'> Shiva Garg </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Siddartha%20Janga'> Siddartha Janga </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Simran%20Basi'> Simran Basi </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Stan%20Chan'> Stan Chan </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Stephen%20Ng'> Stephen Ng </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Tejas%20Shah'> Tejas Shah </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Test%20Analytics'> Test Analytics </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Test%20Engineer'> Test Engineer </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Tim%20Lyakhovetskiy'> Tim Lyakhovetskiy </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Tom%20O%27Neill'> Tom O'Neill </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/Vojta%20J%C3%ADna'> Vojta Jína </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/automation'> automation </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/dead%20code'> dead code </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/iOS'> iOS </a> <span dir='ltr'> 1 </span> </li> <li> <a dir='ltr' href='https://testing.googleblog.com/search/label/mutation%20testing'> mutation testing </a> <span dir='ltr'> 1 </span> </li> </ul> <div class='clear'></div> </div> </div><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <div class='tab'> <i class='material-icons icon'>  </i> <h2> Archive </h2> <i class='material-icons arrow'>  </i> </div> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(12)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/10/'> Oct </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/09/'> Sep </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/08/'> Aug </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/07/'> Jul </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/04/'> Apr </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/03/'> Mar </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2024/02/'> Feb </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/'> 2023 </a> <span class='post-count' dir='ltr'>(14)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/12/'> Dec </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/11/'> Nov </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/10/'> Oct </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/09/'> Sep </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/08/'> Aug </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2023/04/'> Apr </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2022/'> 2022 </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2022/02/'> Feb </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2021/'> 2021 </a> <span class='post-count' dir='ltr'>(3)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2021/06/'> Jun </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2021/04/'> Apr </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2021/03/'> Mar </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/'> 2020 </a> <span class='post-count' dir='ltr'>(8)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/12/'> Dec </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/10/'> Oct </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/08/'> Aug </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/07/'> Jul </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2020/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2019/'> 2019 </a> <span class='post-count' dir='ltr'>(4)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2019/12/'> Dec </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2019/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2019/07/'> Jul </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2019/01/'> Jan </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/'> 2018 </a> <span class='post-count' dir='ltr'>(7)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/09/'> Sep </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/07/'> Jul </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/06/'> Jun </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2018/02/'> Feb </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/'> 2017 </a> <span class='post-count' dir='ltr'>(17)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/12/'> Dec </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/10/'> Oct </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/09/'> Sep </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/08/'> Aug </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/07/'> Jul </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/06/'> Jun </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/04/'> Apr </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/02/'> Feb </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2017/01/'> Jan </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/'> 2016 </a> <span class='post-count' dir='ltr'>(15)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/12/'> Dec </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/11/'> Nov </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/10/'> Oct </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/09/'> Sep </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/08/'> Aug </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/06/'> Jun </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/04/'> Apr </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/03/'> Mar </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2016/02/'> Feb </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/'> 2015 </a> <span class='post-count' dir='ltr'>(14)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/12/'> Dec </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/10/'> Oct </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/08/'> Aug </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/06/'> Jun </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/04/'> Apr </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/03/'> Mar </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/02/'> Feb </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2015/01/'> Jan </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/'> 2014 </a> <span class='post-count' dir='ltr'>(24)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/12/'> Dec </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/10/'> Oct </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/09/'> Sep </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/08/'> Aug </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/07/'> Jul </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/06/'> Jun </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/04/'> Apr </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/03/'> Mar </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/02/'> Feb </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2014/01/'> Jan </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/'> 2013 </a> <span class='post-count' dir='ltr'>(16)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/12/'> Dec </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/11/'> Nov </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/10/'> Oct </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/08/'> Aug </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/07/'> Jul </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/06/'> Jun </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/04/'> Apr </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/03/'> Mar </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2013/01/'> Jan </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2012/'> 2012 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2012/12/'> Dec </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2012/11/'> Nov </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2012/10/'> Oct </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2012/09/'> Sep </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2012/08/'> Aug </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/'> 2011 </a> <span class='post-count' dir='ltr'>(39)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/11/'> Nov </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/10/'> Oct </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/09/'> Sep </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/08/'> Aug </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/07/'> Jul </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/06/'> Jun </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/05/'> May </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/04/'> Apr </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/03/'> Mar </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/02/'> Feb </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2011/01/'> Jan </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/'> 2010 </a> <span class='post-count' dir='ltr'>(37)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/12/'> Dec </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/11/'> Nov </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/10/'> Oct </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/09/'> Sep </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/08/'> Aug </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/07/'> Jul </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/06/'> Jun </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/04/'> Apr </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/03/'> Mar </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/02/'> Feb </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2010/01/'> Jan </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/'> 2009 </a> <span class='post-count' dir='ltr'>(54)</span> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/12/'> Dec </a> <span class='post-count' dir='ltr'>(3)</span> <ul class='posts'> <li> <a href='https://testing.googleblog.com/2009/12/httptwittercomgoogletesting.html'> http://twitter.com/googletesting </a> </li> <li> <a href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager-cont.html'> "If you were a brand new QA manager ..." (cont) </a> </li> <li> <a href='https://testing.googleblog.com/2009/12/if-you-were-brand-new-qa-manager.html'> "If you were a brand new QA manager ..." </a> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/11/'> Nov </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='posts'> <li> <a href='https://testing.googleblog.com/2009/11/testing-chrome-os.html'> Testing Chrome OS </a> </li> <li> <a href='https://testing.googleblog.com/2009/11/how-to-get-started-with-tdd.html'> How to get Started with TDD </a> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/10/'> Oct </a> <span class='post-count' dir='ltr'>(3)</span> <ul class='posts'> <li> <a href='https://testing.googleblog.com/2009/10/fedex-tour.html'> The FedEx Tour </a> </li> <li> <a href='https://testing.googleblog.com/2009/10/tott-making-perfect-matcher.html'> TotT: Making a Perfect Matcher </a> </li> <li> <a href='https://testing.googleblog.com/2009/10/cost-of-testing.html'> Cost of Testing </a> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/09/'> Sep </a> <span class='post-count' dir='ltr'>(5)</span> <ul class='posts'> <li> <a href='https://testing.googleblog.com/2009/09/tott-literate-testing-with-matchers.html'> TotT: Literate Testing With Matchers </a> </li> <li> <a href='https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html'> Checked exceptions I love you, but you have to go </a> </li> <li> <a href='https://testing.googleblog.com/2009/09/plague-of-entropy.html'> The Plague of Entropy </a> </li> <li> <a href='https://testing.googleblog.com/2009/09/it-is-not-about-writing-tests-its-about.html'> It is not about writing tests, its about writing s... </a> </li> <li> <a href='https://testing.googleblog.com/2009/09/7th-plague-and-beyond.html'> The 7th Plague and Beyond </a> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/08/'> Aug </a> <span class='post-count' dir='ltr'>(4)</span> <ul class='posts'> <li> <a href='https://testing.googleblog.com/2009/08/update-speakers-talks-for-gtac.html'> Update! Speakers & Talks for GTAC </a> </li> <li> <a href='https://testing.googleblog.com/2009/08/super-fast-js-testing.html'> Super Fast JS Testing </a> </li> <li> <a href='https://testing.googleblog.com/2009/08/7th-plague.html'> The 7th Plague? </a> </li> <li> <a href='https://testing.googleblog.com/2009/08/tott-testing-gwt-without-gwttest.html'> TotT: Testing GWT without GwtTestCase </a> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/07/'> Jul </a> <span class='post-count' dir='ltr'>(15)</span> <ul class='posts'> <li> <a href='https://testing.googleblog.com/2009/07/how-to-think-about-oo.html'> How to think about OO </a> </li> <li> <a href='https://testing.googleblog.com/2009/07/call-for-attendance-google-test.html'> Call for Attendance: Google Test Automation Confer... </a> </li> <li> <a href='https://testing.googleblog.com/2009/07/plague-of-blindness.html'> The Plague of Blindness </a> </li> <li> <a href='https://testing.googleblog.com/2009/07/update-gtac-keynote-speakers-niklaus.html'> Update! GTAC Keynote Speakers: Niklaus Wirth and A... </a> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/06/'> Jun </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/04/'> Apr </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/02/'> Feb </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2009/01/'> Jan </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/'> 2008 </a> <span class='post-count' dir='ltr'>(75)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/12/'> Dec </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/11/'> Nov </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/10/'> Oct </a> <span class='post-count' dir='ltr'>(9)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/09/'> Sep </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/08/'> Aug </a> <span class='post-count' dir='ltr'>(9)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/07/'> Jul </a> <span class='post-count' dir='ltr'>(9)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/06/'> Jun </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/05/'> May </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/04/'> Apr </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/03/'> Mar </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/02/'> Feb </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2008/01/'> Jan </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/'> 2007 </a> <span class='post-count' dir='ltr'>(41)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/10/'> Oct </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/09/'> Sep </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/08/'> Aug </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/07/'> Jul </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/06/'> Jun </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/04/'> Apr </a> <span class='post-count' dir='ltr'>(7)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/03/'> Mar </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/02/'> Feb </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://testing.googleblog.com/2007/01/'> Jan </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> </div> </div> <div class='clear'></div> </div> </div><div class='widget HTML' data-version='1' id='HTML6'> <div class='widget-content'> <a href="http://googletesting.blogspot.com/atom.xml"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAihJREFUeNrsWa9Pw0AU7viRMDFRBAkzJDMIBIhJJhCzk7NILIqMv4AEhdz+BCY3OYssAlGBoAJREpZwAlHEBO8lr8nSvNeVbu1dyX3JlzTrXfa+u/e9d7c5joWFhYVO1Fa8PwH2gK6m+BRwAvSlAdsrgr8E1jUuMH73GTAEzrkBWymTewZlihhLmgDXIAFuHgGVQOUF7OSYM1p6PgTuA1vAZlUEvAnPdapcMY0VICECekQ0XRfYrqoHsAGNgXfAoMomRiFDEhOZkkL3S88hMaB2LwXp0bj+ps2edpToZpjfoIDQtBeU+xjoDzP2G/gCPKZ5f8WsCAFJoJgOCcFdWSTeL9YQMSvTA1h9BkI5jaiXhLpSCL/8mVZY0UpyJ9ZdOkniu1dmJ96BpzQu9w6s28gcOq9j6pwLdR8/36NK5CQKwJSMrb2MhhSglBpt4UjsrdsnNu0B3J0HCozbCc4TjyY2srEgos/4RQljCzNxl4ireQD8FOq+T+W0mTB2g7njhlR+Sy2jsXFvU658U8YTbeaGpdIu7mWkEAq5ZtIjIhFZdtfX7QHckSvB2B6zC3VdAkZk0kAQwaXTk/CzTXK3wjIExCs6ZJpTnE4uY1KV+KzFzA3KTiFPENHJkOPcsfpLhwe4btoSuvUqAR+6TOxlCE6ZfKUsJLgsqGW8OpqAGx2X+sLxrwUog+JUeQRMDBIwyXOcnlPtPnL0/UsT/8LnOxYWFhZG4leAAQAAQHEaYuzHbAAAAABJRU5ErkJggg==" class="sidebar-icon" /> <h2>Feed</h2> </a> </div> <div class='clear'></div> </div></div> <div class='section' id='sidebar-bottom'><div class='widget HTML' data-version='1' id='HTML9'> <div class='widget-content'> <a href='http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fgoogletesting.blogspot.com%2Ffeeds%2Fposts%2Fdefault' target='blank'><img id="feedlyFollow" src="https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_uyBCGbHkcaHI4oTTeV5lKQVuo0K2yULArfacAFY5GW-eFqHtNSIdElX0xskk1ofF5qvvS7bFdyZia6LRchSBAw58s7RgoBXQAqoTqz5RHOdr4DAPOT_-EZZsaij2IiKAF5mA9zBxuc56fJ_SqmZqE=s0-d" alt="follow us in feedly" width="66" height="20"></a> <div class="share followgooglewrapper"> <button data-href="https://twitter.com/intent/follow?original_referer=http://googletesting.blogspot.com/&screen_name=googletesting" onclick='sharingPopup(this);' id='twitter-share'><span class="twitter-follow">Follow @googletesting</span></button> <script> function sharingPopup (button) { var url = button.getAttribute("data-href"); window.open( url,'popUpWindow','height=500,width=500,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=yes,menubar=no,location=no,directories=no,status=yes'); } </script> </div> </div> <div class='clear'></div> </div></div> </div> </div> <div style='clear:both;'></div> </div> <!-- Footer --> <div class='google-footer-outer loading'> <div id='google-footer'> <a href='//www.google.com/'> <img class='google-logo-dark' height='36' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAABICAYAAABFoT/eAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACLVJREFUeNrsXd+L20YQ3vOprdLqiMXFXE2qB7dcwEcTSB7ykIc+9A/PQx/yEMq1TWhNuYIpJriNr7XpmZ5IxFEvmW2EKs3Ornb1w50PxIFP0kiz387OzM6uhGAwGAxGP3Ho+f7x7ri1O7LdccPqZjSNA4dEHsLfaHcEFedJom93x9Xu2OyOFTcBo6sED3fHZHeMEELrkAHJF0B8Rr+gDFsZ5n0luLTQ95AXs4W06D/tjpR50xtM4CjD0y48YGB4rnyZxNOzyA7zBHr+nLnDaJLg0mo/ALekCasg3Z4XbM0ZdTEgnDPeHY8bIne+Qz2GvwyGNwsuyT218KWvIIBMcwGpLiipcolecjMxfBDchNyS1EvxLiOSIecp31q6IJ/C3yrIrMqMm4jhg+AxkdwbIO3aUO4KjqqMjCT3uaazMBhWBJfuxH3CtRfiXf66DhSRZWbmlMnNaILgZxrXJQO/eO3wORZwvwm4JUxuhheCjzVBYAbW1ces45YDSoZrFNOEE835M8FT6oyeEnws8Fz3QnBxFKPHBMem4GU+m6fPGb0leCTwWcM5B36MPgeZI01gudyDdw3hPeXfo8L/rmCUWnuMMdqUL2WqWeRbhf+twfVsO7YagZGNC79fw7OthEVtkiJ4jJzTd3KPwf3CRqhhiTu23AP5sl0/0xiwISQXpNwLIJK87mHF+U8ddzzdmgKlGzlPYjyxGJQouIhNT4k9AqWEFkqfguIvagTWbcq3KW1WE3xS3m8NtA9WS451xofwjKT5kkDoK/b6mDk5FfXr1lWDL4BofZEv2/SRsK/EHGlGdBdu8QNRb8HMCFwt7Yy3DDI/QP7fx5z3VLhdlJEIs4rKNuXXJXdxZPdB7kfCzWqwCO4V1LHgLjInX3tQ1KzCR52Cz+vDj1dydeRuS74rcvs2Pi6fT5H8OaaUQPQPYcWwRSGXyhhscn5dpAnEFMkuEZetbfkTAnlSuH4DxisE+aMGeJAQ3lFl7C4LJE6QWCaCd583ORQ1jYAwjFctal7nOs2ZZvicwvlZx+RHGrcoAwKUVX8uwcc/9TT65INeDOr5shL9LDRB6QTeIy3zwfdh3WOi6axLCEhSjXU7F3h6LqggUtvyJxpynwu8tDkD98fXApOxRj8zoZ9MnGveYVIVZKaGrkBXCY65BCYNN9NkjpKOyQ81Q79JgdxS+Jn3SDTEXRI7SWzaiSTB32oI3nU3BvMfM0urhOVYgwKhuiAfc4tM07wXwm1ZRoQYSl2NUwiu01fEAHVcpixd745FvVz4dzUUc0o8rwoLy8ZSwU6CyFx1RP5II9+1bFPEFs9HWbNLiimDXE+vCm7u1CS47cofzD3aEhVY57mxRo5zlqdt+RFC1JUH2S7bcVXg4liTMakaBZZVxiTICRoivcn1sEUBlk24JmaC6kxUbYmWoqvyfck2xZGGnDFYa9MMzkYQ1ijkCX6qidybrgePiQ0QIQqoi6qRLeqQfIoRsEHaQJLBdHOnLGetSdm/IPcymJuS1PAnbQPH0MOw/39C1vL11DiLOqIsbDI8QcHvGiLnySi2qUXBicaqUSxN5LEB0g7Jt3ENXJLPJ5S1tnaZBoWbpRqrmjRE7qHmpSmNHdQcYrEUadoh+TbBnc9ri7iycI1kzPeNcLDIvbiqXpez9Tmdq6zGREPuzECBoxrPMiI2WtvyNwhJba2wy3JZ6ky5dD1lSvmZS3e4SPA1wcf1VTFHKX+cGwZzdUYcqpvUtvwrD/InDttVlyZeAKlNN5MKbAiurHhKIPlUuJvlTCCiDjSKSCsUmCFWbGLZwCESfK07JB8LvMYWVtw0D00JEHV8Mq2HkqPbE0oHLvvK2g0o8ETg+4cfwTlZDT9JDoWygu4uQQE/ivIvtcnfPkaCqhiupz7jWOAzqL/vjtcdkv9G4MVMt+EaylfuImiPAXEUjRF3pjjaHiPPZ6If9TGGAO4ZY0am6jOCb+DQ+ZCqLkIpOIPrdNfIjnFPY6nyFut7TS/fanrziOBOKMupKw94WaLMtuVnSFt9CPrWWdJE6PeltCX432DEBoh+5Dv8RRhdis8YAv9uyq4/JAwtlEApgBe9Cw9xDD3tdk4Jn0MDfiHwPHcRPxBePCMER3GuIx7kGlv9fkZ4V9lolx2Uv4X7hEj7qJ3LDoAMGbTRMRibu4L2xQ8bgt8AyU+Q+x7nYrvDnH4iuO5LxKsYwPVbkPMvKF9Zky9wXzRfVWizi62r9X5VHf55h+WHhDjGBZ4WRhyTr6z5SlCoLMxLSpBZFsQ9F80uQFbF/6aFWi+Ev51vzzsuX+msyzuQXXjUz8zEBy+zpq9yweXAoxJW4JbYrDS6gYDqGHxPl+TKeiBfxj9/EBIElPYeOA4y8/qRQfknjvSzgRgtq0Pw/M1eQeMdOSb2Bnrhr6Led+1vcp2x7oTFHMnedFW+Ivlty062BUt74oHgSj+vHepnhunn0JJAMtBZgDI/qmGtMujRv8DDpo47zBJ8UtPOuAR/7rKn8t9AJ0tBdmBAmJ/Fu71yxp4I3qh+DhyRqbi5Y1ShVPlSb8X7bRNcfgZFl+WRGYo7uecrWq1r8X5bhmzP5OdlDwsGRm1suSxkg5rYm7ConyGQ3Zl+DgSD8V/kPwrWBMG9YcBtyShBnTLdTiHgttw7qAW7cqh/ZnmPKr/6ignOaKsdyxbsToT5UkPsW00bJjijDXficcX/JsLs6w2BwGtherdckH3w/kNXRPVI0OqJQoHX42/66IMfMj/2huRjxIidgKV/W0JS+bsstDoTeAHcrI8E5zTh/sDkqxL5rZup55/3USlswfcHf4IrQplVDgW9XFlOqnwr6pVPMMEZTuC60EttvdzbLbaZ4PsFVa3nohhO+vW+yn/ZB2fUhpysmQrzBcTSai9EszuZMcEZ1lCFVrp9zGXhm69iLyY4oxFIa178lPe12I/P2DAYDAaDwWAwGAwGg8FgMBgMBoPBYDD2Cf8IMADDRGoQTe+E9AAAAABJRU5ErkJggg==' style='margin-top: -16px;' width='92'/> </a> <ul> <li> <a href='//www.google.com/'> Google </a> </li> <li> <a href='//www.google.com/policies/privacy/'> Privacy </a> </li> <li> <a href='//www.google.com/policies/terms/'> Terms </a> </li> </ul> </div> </div> <script type='text/javascript'> //<![CDATA[ // Social sharing popups. var postEl = document.getElementsByClassName('social-wrapper'); var postCount = postEl.length; for(i=0; i<postCount;i++){ postEl[i].addEventListener("click", function(event){ var postUrl = this.getAttribute("data-href"); window.open( postUrl,'popUpWindow','height=500,width=500,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=yes,menubar=no,location=no,directories=no,status=yes'); });} //]]> </script> <script type='text/javascript'> //<![CDATA[ var BreakpointHandler = function() { this.initted = false; this.isHomePage = false; this.isMobile = false; }; BreakpointHandler.prototype.finalizeSummary = function(summaryHtml, lastNode) { // Use $.trim for IE8 compatibility summaryHtml = $.trim(summaryHtml).replace(/(<br>|\s)+$/,''); if (lastNode.nodeType == 3) { var lastChar = summaryHtml.slice(-1); if (!lastChar.match(/[.”"?]/)) { if (!lastChar.match(/[A-Za-z]/)) { summaryHtml = summaryHtml.slice(0, -1); } summaryHtml += ' ...'; } } else if (lastNode.nodeType == 1 && (lastNode.nodeName == 'I' || lastNode.nodeName == 'A')) { summaryHtml += ' ...'; } return summaryHtml; }; BreakpointHandler.prototype.generateSummaryFromContent = function(content, numWords) { var seenWords = 0; var summaryHtml = ''; for (var i=0; i < content.childNodes.length; i++) { var node = content.childNodes[i]; var nodeText; if (node.nodeType == 1) { if (node.hasAttribute('data-about-pullquote')) { continue; } nodeText = node.textContent; if (nodeText === undefined) { // innerText for IE8 nodeText = node.innerText; } if (node.nodeName == 'DIV' || node.nodeName == 'B') { // Don't end early if we haven't seen enough words. if (seenWords < 10) { continue; } if (i > 0) { summaryHtml = this.finalizeSummary(summaryHtml, content.childNodes[i-1]); } break; } summaryHtml += node.outerHTML; } else if (node.nodeType == 3) { nodeText = node.nodeValue; summaryHtml += nodeText + ' '; } var words = nodeText.match(/\S+\s*/g); if (!words) { continue; } var remain = numWords - seenWords; if (words.length >= remain) { summaryHtml = this.finalizeSummary(summaryHtml, node); break; } seenWords += words.length; } return summaryHtml; }; BreakpointHandler.prototype.detect = function() { var match, pl = /\+/g, search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, query = window.location.search.substring(1); var urlParams = {}; while (match = search.exec(query)) urlParams[decode(match[1])] = decode(match[2]); this.isListPage = $('html').hasClass('list-page'); this.isMobile = urlParams['m'] === '1'; this.isHomePage = window.location.pathname == '/'; }; BreakpointHandler.prototype.initContent = function() { var self = this; $('.post').each(function(index) { var body = $(this).children('.post-body')[0]; var content = $(body).children('.post-content')[0]; $(content).addClass('post-original'); var data = $(content).children('script').html(); data = self.rewriteForSSL(data); // If exists, extract specified editor's preview. var match = data.match(/([\s\S]+?)<div data-is-preview.+?>([\s\S]+)<\/div>/m); if (match) { data = match[1]; } // Prevent big images from loading when they aren't needed. // This must be done as a pre-injection step, since image loading can't be // canceled once embedded into the DOM. if (self.isListPage && self.isMobile) { data = data.replace(/<(img|iframe) .+?>/g, ''); } // Insert template to be rendered as nodes. content.innerHTML = data; if (self.isListPage) { var summary = document.createElement('div'); $(summary).addClass('post-content'); $(summary).addClass('post-summary'); body.insertBefore(summary, content); if (match) { // Use provided summary. summary.innerHTML = match[2]; } else { // Generate a summary. // Summary generation relies on DOM, so it must occur after content is // inserted into the page. summary.innerHTML = self.generateSummaryFromContent(content, 30); } // Add read more link to summary. var titleAnchor = $(this).find('.title a')[0]; var link = titleAnchor.cloneNode(true); link.innerHTML = 'Read More'; $(link).addClass('read-more'); summary.appendChild(link); } }); // Firefox does not allow for proper styling of BR. if (navigator.userAgent.indexOf('Firefox') > -1) { $('.post-content br').replaceWith('<span class="space"></span>'); } $('.loading').removeClass('loading'); }; BreakpointHandler.prototype.process = function() { if (!this.initted) { var makeInsecureImageRegex = function(hosts) { var whitelist = hosts.join('|').replace(/\./g,'\\.'); // Normal image tags, plus input images (yes, this is possible!) return new RegExp('(<(img|input)[^>]+?src=("|\'))http:\/\/(' + whitelist +')', 'g'); }; this.sslImageRegex = makeInsecureImageRegex(BreakpointHandler.KNOWN_HTTPS_HOSTS); this.sslImageCurrentDomainRegex = makeInsecureImageRegex([window.location.hostname]); this.detect(); this.initContent(); this.initted = true; } }; BreakpointHandler.KNOWN_HTTPS_HOSTS = [ "www.google.org", "www.google.com", "services.google.com", "blogger.com", "draft.blogger.com", "www.blogger.com", "photos1.blogger.com", "photos2.blogger.com", "photos3.blogger.com", "blogblog.com", "img1.blogblog.com", "img2.blogblog.com", "www.blogblog.com", "www1.blogblog.com", "www2.blogblog.com", "0.bp.blogspot.com", "1.bp.blogspot.com", "2.bp.blogspot.com", "3.bp.blogspot.com", "4.bp.blogspot.com", "lh3.googleusercontent.com", "lh4.googleusercontent.com", "lh5.googleusercontent.com", "lh6.googleusercontent.com", "themes.googleusercontent.com", ]; BreakpointHandler.prototype.rewriteForSSL = function(html) { // Handle HTTP -> HTTPS source replacement of images, movies, and other embedded content. return html.replace(this.sslImageRegex, '$1https://$4') .replace(this.sslImageCurrentDomainRegex, '$1//$4') .replace(/(<(embed|iframe)[^>]+?src=("|'))http:\/\/([^"']*?(youtube|picasaweb\.google)\.com)/g, '$1https://$4') // Slideshow SWF takes a image host, so we need to rewrite that parameter. .replace(/(<embed[^>]+?feed=http(?=[^s]))/g, '$1s'); }; $(document).ready(function() { var handler = new BreakpointHandler(); handler.process(); // Top-level navigation. $(".BlogArchive .tab").click(function(ev) { ev.preventDefault(); $(this).parent().toggleClass('active'); $(this).siblings().slideToggle(300); }); $(".Label .tab").click(function(ev) { ev.preventDefault(); $(this).parent().toggleClass('active'); $(this).siblings().slideToggle(300); }); // Blog archive year expansion. $('.BlogArchive .intervalToggle').click(function(ev) { ev.preventDefault(); if ($(this).parent().hasClass('collapsed')) { $(this).parent().removeClass('collapsed'); $(this).parent().addClass('expanded'); } else { $(this).parent().removeClass('expanded'); $(this).parent().addClass('collapsed'); } }); // Reverse order of months. $('.BlogArchive .intervalToggle + div').each(function(_, items) { var year = $(this); year.children().each(function(_, month) { year.prepend(month); }); }); // Set anchors to open in new tab. $('.post-content img').parent().each(function(_, node) { if (node.nodeName == 'A') { $(this).attr('target', '_blank'); } }); // Process search requests. $('.searchBox input').on("keypress", function(ev) { if (ev.which == 13) { window.location.href = 'https://www.google.com/search?q=site%3A' + window.location.hostname + '%20' + encodeURIComponent ($(this).val()); } }); }); (function($, window) { var archiveButton = $($('#sidebar .widget.BlogArchive h2')[0]); var folderIcon = $('#sidebar .widget.BlogArchive h2::after'); var archivePanel = $('#BlogArchive1'); archiveButton.click(function(e) { if (archivePanel.hasClass('archive-open')) { // It's open, so now we close it archivePanel.removeClass('archive-open'); archivePanel.addClass('archive-closed'); archiveButton.removeClass('archive-open'); archiveButton.addClass('archive-closed'); archivePanel.css('height', '60px'); } else { // It's closed, so open it archivePanel.removeClass('archive-closed'); archivePanel.addClass('archive-open'); archiveButton.removeClass('archive-closed'); archiveButton.addClass('archive-open'); archivePanel.css('height', 'auto'); folderIcon.css('content', 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggkDzi8QVSua_XxLOnfxWE_8nQc1MAhRPBxJej91JW2ZBbKvb2TjaNf66ZoTqCrBivwRr-TLJYxFm2ZAiMd-zfbJ0TjblXGvUszWFbdRncNOL7jseiJCL9uQoqN8BM8FpesdSw/s1600/keyboard_arrow_up_grey600_24dp.png'); } }); })($, window); (function ($, window, devsite) { devsite.devsite = {}; devsite.expandLeftNav = devsite.expandLeftNav || {}; devsite.expandLeftNav.ANALYTICS_LABEL_ = 'Hamburger menu'; devsite.expandLeftNav.ANALYTICS_ACTION_OPEN_ = 'Open'; devsite.expandLeftNav.ANALYTICS_ACTION_CLOSE_ = 'Close'; devsite.expandLeftNav.lastState_ = 0; devsite.expandLeftNav.chekovEnabled_ = false; devsite.expandLeftNav.init = function () { devsite.expandLeftNav.chekovEnabled_ = true; var navElement; if (devsite.expandLeftNav.chekovEnabled_) { navElement = $('.devsite-nav-responsive'); } else { navElement = $('.devsite-section-nav-responsive'); } this.drawerWidth = parseInt(navElement.css('width')); navElement.css({ 'left': -(this.drawerWidth) }); var expandButton = $('.devsite-expand-section-nav'); expandButton.click(function () { devsite.expandLeftNav.handleNavigationOpened(); }); navElement.find('.devsite-nav-responsive-forward').click( devsite.expandLeftNav.openPanel); navElement.find('.devsite-nav-responsive-back').click( devsite.expandLeftNav.closePanel); if ($('.devsite-nav-responsive-tabs-panel + ' + '.devsite-nav-responsive-sidebar-panel').length) { devsite.expandLeftNav.openPanel(); } }; devsite.expandLeftNav.handleNavigationOpened = function (opt_noAnimate) { var mask; if (devsite.expandLeftNav.chekovEnabled_) { var nav = $('.devsite-nav-responsive'); if (opt_noAnimate) { nav.addClass('devsite-nav-responsive-no-animate'); mask = devsite.devsite.showSiteMask(0); } else { mask = devsite.devsite.showSiteMask(); } nav.addClass('devsite-nav-responsive-open'); } else { devsite.expandLeftNav.lastState_ = devsite.sticky.currentState; devsite.sticky.setState(devsite.sticky.state.COLLAPSED_HEADER); var headerHeight = devsite.sticky.getHeaderHeight() - devsite.sticky.desiredMargin; var drawerHeight = $(window).height() - headerHeight; mask = devsite.devsite.showArticleMask(); $('.devsite-section-nav-responsive').css({ 'height': drawerHeight, 'left': '0', 'top': headerHeight, 'visibility': 'visible' }).focus(); } $(mask).click(devsite.expandLeftNav.handleNavigationClosed); $(document).on('keydown.escape', function (e) { if (e.keyCode == $.ui.keyCode.ESCAPE) { devsite.expandLeftNav.handleNavigationClosed(); $(document).off('keydown.escape'); } }); }; devsite.expandLeftNav.getScrollTop = function () { return $(window).scrollTop(); }; devsite.expandLeftNav.handleNavigationClosed = function () { var nav; if (devsite.expandLeftNav.chekovEnabled_) { nav = $('.devsite-nav-responsive'); devsite.devsite.hideSiteMask(); nav.removeClass('devsite-nav-responsive-open ' + 'devsite-nav-responsive-no-animate'); } else { nav = $('.devsite-section-nav-responsive'); devsite.devsite.hideArticleMask(); devsite.sticky.setState(devsite.expandLeftNav.lastState_); nav .css('left', -(devsite.expandLeftNav.drawerWidth)) .one('transitionend', function () { nav.css('visibility', 'hidden'); }); } nav.scrollTop(0); }; devsite.expandLeftNav.openPanel = function () { var parentPanel = $('.devsite-nav-responsive-tabs-panel'); var childPanel = $('.devsite-nav-responsive-sidebar-panel'); childPanel.show(); parentPanel .addClass('devsite-nav-responsive-transition') .addClass('devsite-nav-responsive-transform') .one('transitionend', function () { childPanel .removeClass('devsite-nav-responsive-transition') .removeClass('devsite-nav-responsive-transform'); parentPanel.hide(); }); setTimeout(function () { childPanel .addClass('devsite-nav-responsive-transition') .addClass('devsite-nav-responsive-transform'); }, 1); }; devsite.expandLeftNav.closePanel = function () { var parentPanel = $('.devsite-nav-responsive-tabs-panel'); var childPanel = $('.devsite-nav-responsive-sidebar-panel'); childPanel .removeClass('devsite-nav-responsive-transition') .addClass('devsite-nav-responsive-transform'); parentPanel .show() .removeClass('devsite-nav-responsive-transition') .addClass('devsite-nav-responsive-transform'); setTimeout(function () { parentPanel .addClass('devsite-nav-responsive-transition') .removeClass('devsite-nav-responsive-transform'); childPanel .addClass('devsite-nav-responsive-transition') .removeClass('devsite-nav-responsive-transform') .one('transitionend', function () { childPanel.hide(); parentPanel.removeClass('devsite-nav-responsive-transition'); }); }, 1); }; devsite.expandLeftNav.FADE_SLOW_ = 'slow'; devsite.expandLeftNav.FADE_FAST_ = 'fast'; devsite.expandLeftNav.SITE_MASK_CSS_ = '.devsite-site-mask'; devsite.devsite.showSiteMask = function(opt_animate) { if (opt_animate === undefined) { opt_animate = devsite.expandLeftNav.FADE_SLOW_; } devsite.devsite.setMouseScrollingEnabled(false); return devsite.devsite.setMask_(devsite.expandLeftNav.SITE_MASK_CSS_, false, opt_animate); }; devsite.devsite.hideSiteMask = function(opt_animate) { if (opt_animate === undefined) { opt_animate = devsite.expandLeftNav.FADE_FAST_; } devsite.devsite.setMouseScrollingEnabled(true); return devsite.devsite.setMask_(devsite.expandLeftNav.SITE_MASK_CSS_, true, opt_animate); }; devsite.devsite.showArticleMask = function() { devsite.devsite.setMouseScrollingEnabled(false); return devsite.devsite.setMask_('.devsite-article-mask', false, devsite.expandLeftNav.FADE_SLOW_); }; devsite.devsite.hideArticleMask = function() { devsite.devsite.setMouseScrollingEnabled(true); return devsite.devsite.setMask_('.devsite-article-mask', true, devsite.expandLeftNav.FADE_FAST_); }; devsite.devsite.setMask_ = function(className, out, opt_fadeTime) { var query = $(className); if (opt_fadeTime === 0) { out ? query.hide() : query.show(); } else { out ? query.fadeOut(opt_fadeTime) : query.fadeIn(opt_fadeTime); } return $(className)[0]; }; devsite.devsite.setMouseScrollingEnabled = function(trueOrFalse) { if (trueOrFalse == true) { $('html, body').css({ 'overflow': '' }); } else { $('html, body').css({ 'overflow': 'hidden' }); } }; })($, window, devsite = {}); if (window.jQuery) { $(document).ready(function () { if (window.devsite) { devsite.expandLeftNav.init(); } }); } //]]> </script> <style> .widget ul{ line-height: 1.6 !important; } #sidebar ul li a{ color:black; line-height: 20px; } #sidebar ul li a:hover{ color:#4184F3; } </style> <script type="text/javascript" src="https://www.blogger.com/static/v1/widgets/984859869-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY49jyvE8rQOzYI70xbISVQkHKNOxQ:1732799885675';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d15045980','//testing.googleblog.com/2009/','15045980'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '15045980', 'title': 'Google Testing Blog', 'url': 'https://testing.googleblog.com/2009/', 'canonicalUrl': 'https://testing.googleblog.com/2009/', 'homepageUrl': 'https://testing.googleblog.com/', 'searchUrl': 'https://testing.googleblog.com/search', 'canonicalHomepageUrl': 'https://testing.googleblog.com/', 'blogspotFaviconUrl': 'https://testing.googleblog.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': true, 'httpsEnabled': true, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'G-838ZCPQWM6', 'analytics4': true, 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Google Testing Blog - Atom\x22 href\x3d\x22https://testing.googleblog.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Google Testing Blog - RSS\x22 href\x3d\x22https://testing.googleblog.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Google Testing Blog - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/15045980/posts/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/2fafd358a4bcb2b4', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'archive', 'pageName': '2009', 'pageTitle': 'Google Testing Blog: 2009'}}, {'name': 'features', 'data': {}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Google Testing Blog', 'description': '', 'url': 'https://testing.googleblog.com/2009/', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': true, 'isLabelSearch': false, 'archive': {'year': 2009, 'rangeMessage': 'Showing posts from 2009'}}}]); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/2646514562-lbx.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/1964470060-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML8', 'sidebar-top', document.getElementById('HTML8'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_LabelView', new _WidgetInfo('Label1', 'sidebar', document.getElementById('Label1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML6', 'sidebar', document.getElementById('HTML6'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML9', 'sidebar-bottom', document.getElementById('HTML9'), {}, 'displayModeFull')); </script> </body> </html>