tag:blogger.com,1999:blog-2843993027649099322024-03-08T23:02:50.138-08:00Confessions of an Agile CoachLancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.comBlogger56125tag:blogger.com,1999:blog-284399302764909932.post-39556057718587273822022-09-13T15:09:00.003-07:002022-10-10T09:50:24.008-07:00An Agile Coach's guide to CapEx and OpEx and Why they Matter<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHMoXo45M1WTOwM6tv4f8x0vR9xMB7kZPaQ7twCMd3Wut1JJcvl07GqqYteTrZNgqfDtAocfACl9SI2qvIqnLyN5y4QqRM0kvswMZ1QF2e9X0pTdG9ItY081AAY5klkeHm41s1nKbQ3AtlM73Q9Y4JfQGR8RQPci4fe8D2fBNveGI7ckqtJmYSBD8/s919/leamonaid%20stand.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="919" height="251" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHMoXo45M1WTOwM6tv4f8x0vR9xMB7kZPaQ7twCMd3Wut1JJcvl07GqqYteTrZNgqfDtAocfACl9SI2qvIqnLyN5y4QqRM0kvswMZ1QF2e9X0pTdG9ItY081AAY5klkeHm41s1nKbQ3AtlM73Q9Y4JfQGR8RQPci4fe8D2fBNveGI7ckqtJmYSBD8/s320/leamonaid%20stand.jpg" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><span style="text-align: left;"><br /></span></div>Budgets for services related to agile coaching are lumped into two spending categories: CapEx (capital expenditures) and OpEx (operations expenditures). Your client or your organization will make decisions on what services it's willing to pay for based on which category your service falls under.<p></p><h1 style="text-align: left;">Definitions</h1><div>Just like doing personal taxes, how expenditures get categorized can help or hurt how much profit a company gets taxed against. Entrepreneurial junior high student Dominque is running a lemon stand. He's going to pay taxes on the profit made at the lemon stand. During his grand opening, he made $10. His friend Joey, whose mom is a CPA, has informed him that if he hasn't accounted for any expenditures, his day's taxable income will be $10. Joey tells Dominique over a glass of lemonade, "The name of the game is to report expenditures which will reduce the taxable profit of your business."</div><h1 style="text-align: left;">Expenditures </h1><div>To have a lemonade stand, Dominque <br />had the following expenditures:</div><div><ol style="text-align: left;"><li>$2 serving pitcher</li><li>$5 stand</li><li>$1 sign</li><li>$3 lemons</li><li>$3 sugar</li><li>$0.10 water</li><li>$1 Kate <- Dominique's sister who is working as his employee</li></ol>Capital expenses are typically one time expenses to get an asset setup. Operational expenses are typically materials you need to pay for to keep the business going. When you look through the seven expenditures, how many capex do you see and how many opex do you see?</div><div><br /></div><div>(The answer written backwards is: eerht capex and ruof opex)</div><div><br /></div><div>If you got the answer correct, Dominique will be able to report a taxable income of $10 minus opex of $7.10 which equals a taxable income of $2.90.</div><div><h1 style="text-align: left;">Digital Transformation</h1><div>Silvstr, a friend of Dominique has a dad that is an Agile coach. Silvstr tells Dominique, "There's big money in it if you go digital." Dominique agrees to digitally transform his lemonade stand.</div></div><div>Silvstr bills the following to Dominique:</div><div><ol style="text-align: left;"><li>$2 design and build website</li><li>$0.20 monthly cloud hosting fee</li><li>$100 sign redesign service</li><li>$5 training service for Kate</li></ol><div>That month Dominique makes $300. Joey asks Dominique to categorize his Digital Transformation expenses. For the above points, how many are for capex and how many for opex?</div></div><div><br /></div><div>(The answer written backwards is: eno for capex and eerht for opex)</div><div><br /></div><div>If you got the answer correct, Dominique's taxable income will be $300 minus opex (ignoring lemons, sugar, water, and Kate's wages) $105.20 which equals a subtotal (we aren't including the lemons etc) of $194.80.</div><div><br /></div><div>Said another way, the creation of new software (the website) is a capex expense. The maintenance of the website (hosting), the sign, and employee training are opex.</div><div><br /></div><h1 style="text-align: left;">Resources Used in Writing this Article</h1><div><a href="https://sagittarius.agency/en/our-thinking/blog/capex-v-opex">https://sagittarius.agency/en/our-thinking/blog/capex-v-opex</a></div><div><br /></div><div><br /></div>Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-51067499460904406182021-12-11T10:19:00.001-08:002021-12-11T10:24:10.428-08:00AndroidStudio Development with an M1 Mac<p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiD4Y6yJRitAfVtW74dHBcj-1gi37XcNQvT6gGBcecn_oj_n1YWfIqE75q8pej46Qg3-T7ROGWf7KRwcJonzpubZ42XfrRyizKv9arGTcph_rReTArWP9MgamD5sppFtASyIR_uwfnE9nGUKgPs-O0QcBL033tXvVzJcTXEItkuVYIPQRt5yHvji1o=s613" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="350" data-original-width="613" height="229" src="https://blogger.googleusercontent.com/img/a/AVvXsEiD4Y6yJRitAfVtW74dHBcj-1gi37XcNQvT6gGBcecn_oj_n1YWfIqE75q8pej46Qg3-T7ROGWf7KRwcJonzpubZ42XfrRyizKv9arGTcph_rReTArWP9MgamD5sppFtASyIR_uwfnE9nGUKgPs-O0QcBL033tXvVzJcTXEItkuVYIPQRt5yHvji1o=w400-h229" width="400" /></a></div><br />I got to use an M1 Mac as a loaner while my other Mac (2019, 32GB, MBP) was in the shop. So I used Time Machine to restore my work on the M1 MBP with 16GB. This M1 really is impressive in performance and using less power. Having half the RAM of the Intel Mac wasn't noticeable at all.<p></p><p>Everyone should ditch their Intel and get one of these! That said, there are two impediments I had with getting work done: Android Studio and Open Broadcast Studio. In this article I'll share how I resolved my issue with Android Studio.</p><h1 style="text-align: left;">The Issue </h1><p>Everything worked "out of the box" with Arctic Fox patch 4 (not a native M1 app), except I still couldn't get the emulator to launch. But it actually was pretty easy to resolve once I learned a bit more about how Android emulation works. </p><p>When I chose to launch my app, nothing would happen. What was happening in my case is that it was trying to launch an emulator based on the x86 architecture. So I needed to change the emulator. The following is how. There are articles on StackOverflow, Reddit, and Medium but as AndroidStudio frequently releases new versions, a lot of the information is educational but the steps are quickly out of date. (We are speeding toward the Singularity after all.)</p><h1 style="text-align: left;">Changing the Virtual Device</h1><div>Note: As you go through the below images, you'll notice they are a tiny bit fuzzy. This is a compromise to get these big screenshots to fit in this webpage's layout. If you can't read something clearly, click on the image and it'll expand to full size and everything will be clear.</div><h3 style="text-align: left;">Go into the Android Vertical Device manager (AVD).</h3><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh6hV5sjfoMj70da6xQLtCZtzjj5SEP24WQ4KQkZNcBcLIffBMuUsx_oQEjyVJiNP5jffslFS1xznZNNkCqUIWkR2cCO2jjX5K-6o3-pfmBpEm8RUjk4BEQRCk4w3Lbmr5BUVuSZ2UIp5qCKG1XFLRLdUbH7jsDO2BUDXlqr7nXn2ATMBJaLIrDVvQ=s780" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="324" data-original-width="780" src="https://blogger.googleusercontent.com/img/a/AVvXsEh6hV5sjfoMj70da6xQLtCZtzjj5SEP24WQ4KQkZNcBcLIffBMuUsx_oQEjyVJiNP5jffslFS1xznZNNkCqUIWkR2cCO2jjX5K-6o3-pfmBpEm8RUjk4BEQRCk4w3Lbmr5BUVuSZ2UIp5qCKG1XFLRLdUbH7jsDO2BUDXlqr7nXn2ATMBJaLIrDVvQ=s16000" /></a></div><br /><div><br /></div><h3 style="text-align: left;">Create a new virtual device. </h3><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEixufQu7yFizHdP6KJxK6dSmUJVCoK549MvC056YC22LeHtAYToOfUVsrejvoe44brrqy1GdsFtrS1iq0w-EnuumZwfAsNmBeYL5C-DaXDMxqu_rYhXGDkQ9j9agK9RdTATp9_nYE7H7AC4tYvhacNvx8BxTvGgU2esVXTjsrkvNkPOT1CCKUfWNIw=s2048" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1158" data-original-width="2048" height="362" src="https://blogger.googleusercontent.com/img/a/AVvXsEixufQu7yFizHdP6KJxK6dSmUJVCoK549MvC056YC22LeHtAYToOfUVsrejvoe44brrqy1GdsFtrS1iq0w-EnuumZwfAsNmBeYL5C-DaXDMxqu_rYhXGDkQ9j9agK9RdTATp9_nYE7H7AC4tYvhacNvx8BxTvGgU2esVXTjsrkvNkPOT1CCKUfWNIw=w640-h362" width="640" /></a></div><br /><h3 style="text-align: left;">Select the same phone type you were using before (I'm using Pixel 3a).</h3><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh8qAU0AIu2DY070eTFHiJXuY4u6YadhXjeRzimWGZ1UN4Ojg-4URww3vuC3WwqhoNweBDE8CrK63RJTNywHE6muBNvFRAQtvk-QJ6Cs2-OVfqt4TRK51ksuRcacuEhrumlhQwRuUz1pS-Aj2jwUnuZ2KsLptvpdTrqxgk76Ogk0M1nTJP3O4zK9Zg=s2002" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1626" data-original-width="2002" height="520" src="https://blogger.googleusercontent.com/img/a/AVvXsEh8qAU0AIu2DY070eTFHiJXuY4u6YadhXjeRzimWGZ1UN4Ojg-4URww3vuC3WwqhoNweBDE8CrK63RJTNywHE6muBNvFRAQtvk-QJ6Cs2-OVfqt4TRK51ksuRcacuEhrumlhQwRuUz1pS-Aj2jwUnuZ2KsLptvpdTrqxgk76Ogk0M1nTJP3O4zK9Zg=w640-h520" width="640" /></a></div><br /><div><br /></div><h3 style="text-align: left;">Download the version of Android OS you need (notice the ABI is for ARM).</h3><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi0yONu_lMNQWkKCE0a4uoMWRnk4dvoQDhOfEURul-Cd6YJpRwtNvpIXPql00C9L_LxXRxcUr97m6DNxRHIZnH3-BKRZ-MoRWBPX4vJpc8o11u4tAOrMlZU903-qnl0UbZ_uA2qVAqC_wm5YBz9_7JtcBUJdUZ4vqKYLb7QU50HlhRhw9_Q8SEuKM0=s1996" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1588" data-original-width="1996" height="509" src="https://blogger.googleusercontent.com/img/a/AVvXsEi0yONu_lMNQWkKCE0a4uoMWRnk4dvoQDhOfEURul-Cd6YJpRwtNvpIXPql00C9L_LxXRxcUr97m6DNxRHIZnH3-BKRZ-MoRWBPX4vJpc8o11u4tAOrMlZU903-qnl0UbZ_uA2qVAqC_wm5YBz9_7JtcBUJdUZ4vqKYLb7QU50HlhRhw9_Q8SEuKM0=w640-h509" width="640" /></a></div><div><br /></div><h3 style="text-align: left;">Select the ARM emulator. (Remember, Apple Silicon is an ARM architecture.)</h3><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj3VH7iPXGQwoOqMcvArKRvxQyIZ4-sREp-za_E_yAU78Przr4F-2pMwfClKVp3BNIGdoT_7XlV6JgfTlRWuYdOBYbDcZ_3DTtaBMbxjErkIpdi1961GELpoiwfvTNfDSQC3H2qseXsDKKYFd0A9ZxIkarbgvPXZtQhza60_EW2kHvnL-gegef7fV8=s1988" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1602" data-original-width="1988" height="516" src="https://blogger.googleusercontent.com/img/a/AVvXsEj3VH7iPXGQwoOqMcvArKRvxQyIZ4-sREp-za_E_yAU78Przr4F-2pMwfClKVp3BNIGdoT_7XlV6JgfTlRWuYdOBYbDcZ_3DTtaBMbxjErkIpdi1961GELpoiwfvTNfDSQC3H2qseXsDKKYFd0A9ZxIkarbgvPXZtQhza60_EW2kHvnL-gegef7fV8=w640-h516" width="640" /></a></div><br /><div><br /></div><div><br /></div><div>After clicking finish you're ready to go back to AndroidStudio and launch the app in the emulator. (It will take more time on the first launch as it doesn't have things locally cached.)</div><h1 style="text-align: left;">Cleanup</h1><div>When you're happy with the results of the new virtual device, go back into the Android Virtual Device manager and delete your old, unusable device and free up those GBs of storage.</div><div><br /></div>Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-31892975760304085822021-04-16T12:27:00.002-07:002021-12-11T10:22:12.688-08:00Code Craftspersonship with S.O.L.I.D. (SOLID) for Java, Swift, Kotlin<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsm8S08ZTx0cGw9YzmKOmy9yXfphMmNkRlvB5XoHAVnrGQ54NFmk3riT2HCB2Z5Vvg0yYgXH94YRXxnHrVF4vErDMes0GAVTlmn61kcYoEPcsm8IBK54rroN-oTR-QMFs5BDk0RQXUf1s/s750/solid_jenga.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="750" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsm8S08ZTx0cGw9YzmKOmy9yXfphMmNkRlvB5XoHAVnrGQ54NFmk3riT2HCB2Z5Vvg0yYgXH94YRXxnHrVF4vErDMes0GAVTlmn61kcYoEPcsm8IBK54rroN-oTR-QMFs5BDk0RQXUf1s/w400-h320/solid_jenga.jpg" width="400" /></a></div><br />Code craftspersonship is important as it is what allows code to be adjustable in the future. If it's not adjustable, then you're assuming that you're only releasing ONCE, and who does that? If software is valuable, then we'll keep adding more features the market would like. So it's the job of a professional developer to enable this ability, otherwise, what you are delivering is not as valuable after the first release.<p></p><p>A popular model introduced by <a href="https://cleancoders.com/episode/clean-code-episode-8" target="_blank">Bob Martin in Clean Code is S.O.L.I.D</a>.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDw7taiuWo9WACpCIhiC8Q6yRd6o0VsX5yMlDhtrhBi0coLEHq6tlBIwyq1ZwPPxsQU8IA8eiwuXU7MSJuSn5KxMMwuf79e2sBUQNyssb69aZvYy3ztJ8FdnBIfvFdkNhf5ri7P2CfljM/s600/SOLID+code2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="600" height="410" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDw7taiuWo9WACpCIhiC8Q6yRd6o0VsX5yMlDhtrhBi0coLEHq6tlBIwyq1ZwPPxsQU8IA8eiwuXU7MSJuSn5KxMMwuf79e2sBUQNyssb69aZvYy3ztJ8FdnBIfvFdkNhf5ri7P2CfljM/w410-h410/SOLID+code2.jpg" width="410" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><p>Learning how to put SOLID to work and then teach your team will bring a lot of value to your software and organization. Here are resources I recommend for individuals and teams to learn how to apply SOLID to Java, Swift, and Kotlin. (I'll add more as they come up on my day job as a technical coach.)</p><h1 style="text-align: left;">Language Agnostic Materials</h1><div>This introduction teaches with cartoons and is my favorite resource on this whole page! </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvgZ-XH1x7F0_sA0BzZMLcDJhuPMxsgXqExTlIEj1-dQ9_QnSA540H8vzwPLRFhvjFirRaXnlG0CQCDsuPozbDdP58rXHx16AAPLBGNVBtvVCFh9COZcyYIIRPyeZtWpRIv_hZ5gw1OOg/s2400/1*wrxj0oBKpA_GXb8LPhXOeg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="888" data-original-width="2400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvgZ-XH1x7F0_sA0BzZMLcDJhuPMxsgXqExTlIEj1-dQ9_QnSA540H8vzwPLRFhvjFirRaXnlG0CQCDsuPozbDdP58rXHx16AAPLBGNVBtvVCFh9COZcyYIIRPyeZtWpRIv_hZ5gw1OOg/s320/1*wrxj0oBKpA_GXb8LPhXOeg.png" width="320" /></a></div><br /><div>Give the author some love with some "claps." <a href="https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898" style="font-family: Helvetica; font-size: 14px;" target="_blank">https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898</a></div><div><br /></div><div>This next is a slide deck that teaches with CRC diagrams: <a href="https://www.slideshare.net/sjabnouni/solid-code-by-example">https://www.slideshare.net/sjabnouni/solid-code-by-example</a></div><div><br /></div><div>If you want more details, Code magazine has a very nice article by Derick Bailey that shows how a project starts with something simple and evolves into something requiring more design: <a href="https://www.codemag.com/Article/1001061/S.O.L.I.D.-Software-Development-One-Step-at-a-Time">https://www.codemag.com/Article/1001061/S.O.L.I.D.-Software-Development-One-Step-at-a-Time</a></div><div><br /></div><h1 style="text-align: left;">Java</h1><div>Here is a deck of slides in English: <a href="https://www.slideshare.net/ionutbilica/solid-design-principles-applied-in-java">https://www.slideshare.net/ionutbilica/solid-design-principles-applied-in-java</a></div><div>Another deck in German: <a href="https://www.slideshare.net/RolArc/solid-mit-java-8">https://www.slideshare.net/RolArc/solid-mit-java-8</a></div><div><br /></div><h1 style="text-align: left;">Swift</h1><div>Here is a good deck of slides: <a href="https://www.slideshare.net/Indeema/clean-code-solid-151968746">https://www.slideshare.net/Indeema/clean-code-solid-151968746</a></div><div><br /></div><h2 style="text-align: left;">Kotlin</h2><div><div>Here are some good articles:</div><div><a href="https://blog.joshua-greenwood.com/the-solid-principles-and-kotlin/">https://blog.joshua-greenwood.com/the-solid-principles-and-kotlin/</a></div><div><a href="https://blog.berkberber.com/solid-principles-in-kotlin-4f37e9b62dde">https://blog.berkberber.com/solid-principles-in-kotlin-4f37e9b62dde</a></div><div><a href="https://www.coderefer.com/blog/solid-principles-in-android-with-kotlin-examples/">https://www.coderefer.com/blog/solid-principles-in-android-with-kotlin-examples/</a></div><div><br /></div><div>This slide deck has some nice design concepts in it but it isn't great at teaching SOLID:</div><div><a href="https://www.slideshare.net/AntonRutkevich/cutting-edge-android-stack-one-year-later?qid=0a2739ce-0da7-4b3e-a6bb-6ef6341b8601&v=&b=&from_search=1">https://www.slideshare.net/AntonRutkevich/cutting-edge-android-stack-one-year-later?qid=0a2739ce-0da7-4b3e-a6bb-6ef6341b8601&v=&b=&from_search=1</a></div></div><div><br /></div><div><span style="caret-color: rgb(255, 255, 255); color: white; font-family: Helvetica; font-size: 14px;">https://www.slideshare.net/ionutbilica/solid-design-principles-applied-in-java</span><br style="caret-color: rgb(255, 255, 255); color: white; font-family: Helvetica; font-size: 14px;" /></div><div><span style="caret-color: rgb(255, 255, 255); color: white; font-family: Helvetica; font-size: 14px;"><br /></span></div>Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com1tag:blogger.com,1999:blog-284399302764909932.post-38342136550204700512021-01-07T16:14:00.006-08:002021-01-09T09:54:02.405-08:00Zoom Screen Sharing freezing on MacOS<p></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4kwJHZLnn2Cm4v7FMKR1HeoL_D5yGG6Gh_Mwe04cW0BD4Us4TIH7bmBSYlY0NnQMwnR69A3Pz2Ka2PctiXvoKfLugjkzH-FcT7VoJ_w9k2ZTa839FdgyqnnUlV2gxi5Z6U_pzC-hIqJM/s306/Unknown-3.jpg" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="164" data-original-width="306" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4kwJHZLnn2Cm4v7FMKR1HeoL_D5yGG6Gh_Mwe04cW0BD4Us4TIH7bmBSYlY0NnQMwnR69A3Pz2Ka2PctiXvoKfLugjkzH-FcT7VoJ_w9k2ZTa839FdgyqnnUlV2gxi5Z6U_pzC-hIqJM/s0/Unknown-3.jpg" /></a></div><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghyphenhyphenRiwsR0aQ60ft7h9yKMXyir1VTjREPzKhlFOzvV6kL0zOReAnu-VHwuOojNnVFj_ZtOVPklIu9UaNd7a4r3GdrkZconY8G6MVV1QFJSWsnjCmT1inZlN082EKz4Vpz9XiTTfjXH8FJ4/s590/mac+%2526+Zoom.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="590" data-original-width="396" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghyphenhyphenRiwsR0aQ60ft7h9yKMXyir1VTjREPzKhlFOzvV6kL0zOReAnu-VHwuOojNnVFj_ZtOVPklIu9UaNd7a4r3GdrkZconY8G6MVV1QFJSWsnjCmT1inZlN082EKz4Vpz9XiTTfjXH8FJ4/s320/mac+%2526+Zoom.jpg" /></a></div>You're all set. You've got the best presentation ready. Your green screen is shiny and your new pinstripe T-Shirt displaying the M1X logo on the chest is well pressed and lightly starched and you're totally killing it on your 4K webcam, but then one of your participants says, "Oh? Did you change slides? Your screen sharing is frozen." <p></p><p>OK. So you close the sharing and turn it on again and it goes even worse: "Now it's a blank screen."</p><p>A NIGHTMARE! And you KNOW your hard wired connection to gigabit ethernet can't be the problem. And they say your are looking GOOD on the webcam. That means this is <b>a screen sharing problem.</b></p><h2 style="text-align: left;">Here are some things that worked for me if you're living this nightmare:</h2><div>Of the following two steps, doing the steps in #1 didn't resolve the issue. Doing the steps for #2 did.</div><p></p><ol style="text-align: left;"><li>learn about your Mac's ability to switch video card, know how to identify what card it's using, and configure it to not switch when connected to power.</li><li>configure Zoom to avoid the situation</li></ol><div>I included the steps of #1 because:</div><div><ul style="text-align: left;"><li>sometimes more knowledge will save your bacon, </li><li>I wanted to capture these notes, and </li><li>if you're a live streamer like me (<a href="https://m.youtube.com/channel/UCU1U6oPscxzKd_IWAIOm60w" target="_blank">TDD.Academy Youtube</a>, <a href="https://www.twitch.tv/tddacademy" target="_blank">TDD.Academy Twitch</a>), you may need to "amp things up" to increase your frame rates for your live stream.</li></ul><div>It's possible after some time and experience, that I'll go back to #1 and allow my machine to automatically switch video chips while connected to power. But for now, I've some experiments to try out.</div></div><h2 style="text-align: left;">Did you know your Mac could have more than one graphics processor?</h2><div>Go to your battery settings to see if your Mac has "Automatic graphics switching." If it does, you've got more than one graphics processor. Here are the steps:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOlx2CBQh0RDsJalW6n5Eyg5_TdFi_RiaIdonKs8UVaVgo7-Z6zYB4Cg5RSl77hks2nTMmjO8876qqIoA7PHAKi2XeMfKuNYWKa87OFq_ahx1s_eYE-rw1KCkcVgFN-ucHYjegZ5KJgO4/s1308/Screen+Shot+2021-01-07+at+3.29.01+PM.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1114" data-original-width="1308" height="341" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOlx2CBQh0RDsJalW6n5Eyg5_TdFi_RiaIdonKs8UVaVgo7-Z6zYB4Cg5RSl77hks2nTMmjO8876qqIoA7PHAKi2XeMfKuNYWKa87OFq_ahx1s_eYE-rw1KCkcVgFN-ucHYjegZ5KJgO4/w400-h341/Screen+Shot+2021-01-07+at+3.29.01+PM.png" width="400" /></a></div><br /><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj339rcsVj6RxFk6vhkUKrpbAC_JfjnOYdoBfAPwHnmxejPaf9JwEOywQx1J4XYkdvK9ilQEc3j70QQ_xNW8jb2TC4Xcz8T7i2P-qtjV4VIPpP_-jONQOcjvi13GaZqJxwQTulBTeTx7sc/s1338/Screen+Shot+2021-01-07+at+1.44.53+PM.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1014" data-original-width="1338" height="486" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj339rcsVj6RxFk6vhkUKrpbAC_JfjnOYdoBfAPwHnmxejPaf9JwEOywQx1J4XYkdvK9ilQEc3j70QQ_xNW8jb2TC4Xcz8T7i2P-qtjV4VIPpP_-jONQOcjvi13GaZqJxwQTulBTeTx7sc/w640-h486/Screen+Shot+2021-01-07+at+1.44.53+PM.jpeg" width="640" /></a></div><br /></div><div><br /></div><div>For more background, read this article: <a href="https://it.umn.edu/services-technologies/how-tos/zoom-black-screen-during-screen-sharing">https://it.umn.edu/services-technologies/how-tos/zoom-black-screen-during-screen-sharing</a></div><div><br /></div><div>If you're curious about which GPU your machine is using at any time, either open Activity Monitor or click on the apple icon in the menu bar and select "About this mac." Activity Monitor will tell you if they are using the "high performance" card or not. About this mac will tell you which GPU (Graphics Processing Unit) is active. Typically the low power GPU is from Intel and the high performance one is from Radeon or Invidea.</div><div><br /></div><div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCOEV8iaZ70VpDWy8cweb_Rzz1942ldRbHMAmi8UFQL_T4Lf24PUpNfPOfa5qCztZqkpHjhsfjp26yUa8M1N9YLDaUw_Hx3KC5hau4XWc8bL-cwelMwVr3gL51H0gc5_3fDPrsXkXxD8A/s1570/Screen+Shot+2021-01-07+at+1.54.00+PM.jpeg" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="1486" data-original-width="1570" height="606" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCOEV8iaZ70VpDWy8cweb_Rzz1942ldRbHMAmi8UFQL_T4Lf24PUpNfPOfa5qCztZqkpHjhsfjp26yUa8M1N9YLDaUw_Hx3KC5hau4XWc8bL-cwelMwVr3gL51H0gc5_3fDPrsXkXxD8A/w640-h606/Screen+Shot+2021-01-07+at+1.54.00+PM.jpeg" width="640" /></a></div><div><br /></div><div>To change or turn off the ability to switch GPUs, your can deselect the "Automatic graphics switching." I did this for when my machine is running on power. I want it to select to the low power GPU when it's running on battery. When I'm presenting my best face to the world, I'm usually connect to battery and hardwired to the LAN.</div><h2 style="text-align: left;">Zoom settings that solved this problem</h2><div>Zoom has two interesting settings. One of which, I'm sure resolved my problem. Go into Zoom preferences and turn them both on if your having issues.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge8P6r5OudlMl4d-hRDEZorfYGn92ug6yADwz5v4GTQvPvDy36IHvbja19oFULzGKKiAbEstyaVQkqgInCbSFJZucKLjktNSeh4Wk7jxH6f6siIxQ0I5NFaYPW7PntFKf3uKbwQidO_ec/s520/Screen+Shot+2021-01-07+at+3.45.48+PM.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="196" data-original-width="520" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge8P6r5OudlMl4d-hRDEZorfYGn92ug6yADwz5v4GTQvPvDy36IHvbja19oFULzGKKiAbEstyaVQkqgInCbSFJZucKLjktNSeh4Wk7jxH6f6siIxQ0I5NFaYPW7PntFKf3uKbwQidO_ec/s320/Screen+Shot+2021-01-07+at+3.45.48+PM.png" width="320" /></a></div><br /><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiur_nRwiF11pSlEN7qhHF1nV-2p6ADnuFgqgKuuyJongyrwUxSYD9fRkp9fum4x7r9ATk_jyfmISmMHflsfJQsXeyYLxr8r1qwCa3rbSIQJvJ48xage9eE4RS4FdpzZtBXJigielw9H0U/s1596/Screen+Shot+2021-01-07+at+2.37.28+PM.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1298" data-original-width="1596" height="520" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiur_nRwiF11pSlEN7qhHF1nV-2p6ADnuFgqgKuuyJongyrwUxSYD9fRkp9fum4x7r9ATk_jyfmISmMHflsfJQsXeyYLxr8r1qwCa3rbSIQJvJ48xage9eE4RS4FdpzZtBXJigielw9H0U/w640-h520/Screen+Shot+2021-01-07+at+2.37.28+PM.jpg" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpWOBY-5zWrpGEbrZhYK64vha-I9Rie42NJcm4JVL50i8TfZMYFG0715J_gM8Gk4PA5c4frRsQMdqBlAVEnl2jnho1BzuFkPJ5Ez8oyqZynKQkye87gFtIsQ_lCSbuZjkD0CJqjymNQT4/s1610/Screen+Shot+2021-01-07+at+2.37.39+PM.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1228" data-original-width="1610" height="488" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpWOBY-5zWrpGEbrZhYK64vha-I9Rie42NJcm4JVL50i8TfZMYFG0715J_gM8Gk4PA5c4frRsQMdqBlAVEnl2jnho1BzuFkPJ5Ez8oyqZynKQkye87gFtIsQ_lCSbuZjkD0CJqjymNQT4/w640-h488/Screen+Shot+2021-01-07+at+2.37.39+PM.jpg" width="640" /></a></div><br /><div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div></div><p></p><p><br /></p><br />Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-47058179549641590772020-07-26T13:53:00.003-07:002020-07-26T13:53:30.670-07:00"NodeJS cannot be opened" dialog box on MacOS<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-owjfhPuppFD_dwf40lfoDYao12BWSwlZiwaSxnXws9GrTejHbY-LIRsWeHBOzloEzEupjSg5L2z11I5Gnyk_RMSj8G-EwVV6DFK7O3lpeK9m1r93fYWBrKxjad4uCHYG7pgeAkPHSa8/s1600/7bd32b7a-6de6-4e29-98ef-3cc5bf5b0933.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="404" data-original-width="886" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-owjfhPuppFD_dwf40lfoDYao12BWSwlZiwaSxnXws9GrTejHbY-LIRsWeHBOzloEzEupjSg5L2z11I5Gnyk_RMSj8G-EwVV6DFK7O3lpeK9m1r93fYWBrKxjad4uCHYG7pgeAkPHSa8/s400/7bd32b7a-6de6-4e29-98ef-3cc5bf5b0933.png" width="400" /></a></div>
If you're using MacOS Catalina or newer, using your IDE may get frustrating. In Spring, every few <u>seconds</u> I'd get a dialog warning me that nodeJS isn't signed and MacOS can't identify if it's malware. This is an easy problem to solve if you can identify the executable because once you do, you use the context menu to "open" it anyhow which tells MacOS that "yeah, the user knows it's not signed but they are confident in this executable." However the problem in this case is that because this is happening via the IDE, you've no idea in which of the many locations you've got a Node is the binary your IDE is using. Not an easy problem to solve.<br />
<h2 style="text-align: left;">
Here's how to solve it</h2>
<div>
To narrow down your search for which Node binary by paying attention to the date in the warning dialog.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUVs7EjlziFVqJgRa1Cu4Ym_fl9_QqwE_KN8GI-1PsbKbqFmIPHa4csRm4swLZUIOhA45L3OAlDNje5L4Vmx9Gb9Q22CgBgLtrU9nHowQTDZ3eVJWGf5BOdBr53NE8PAGOEOGC3VUmUw4/s1600/7bd32b7a-6de6-4e29-98ef-3cc5bf5b0933.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="404" data-original-width="886" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUVs7EjlziFVqJgRa1Cu4Ym_fl9_QqwE_KN8GI-1PsbKbqFmIPHa4csRm4swLZUIOhA45L3OAlDNje5L4Vmx9Gb9Q22CgBgLtrU9nHowQTDZ3eVJWGf5BOdBr53NE8PAGOEOGC3VUmUw4/s400/7bd32b7a-6de6-4e29-98ef-3cc5bf5b0933.png" width="400" /></a></div>
<div>
<br /></div>
<h3 style="text-align: left;">
Put MacOS Finder to work:</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5vAjJk2-HAgN1E9NTvlbxMsPAr3RLmqX9Q_JEgtcbOHk5meawQFSFLXwdl21COEw2qCPyo6sftilY0kpF3peKGAtY_MY3uH4qrTlp5wGb_NI2iLffpt8jFyfwCwdN3QegelqKzoEo4Zg/s1600/Screen+Shot+2020-07-26+at+1.26.42+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="253" data-original-width="1600" height="99" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5vAjJk2-HAgN1E9NTvlbxMsPAr3RLmqX9Q_JEgtcbOHk5meawQFSFLXwdl21COEw2qCPyo6sftilY0kpF3peKGAtY_MY3uH4qrTlp5wGb_NI2iLffpt8jFyfwCwdN3QegelqKzoEo4Zg/s640/Screen+Shot+2020-07-26+at+1.26.42+PM.jpg" width="640" /></a></div>
<div>
<br /></div>
<h3 style="text-align: left;">
Refine the search by date and type:</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXncl2jt7tzR8TWfVR3zCRnkjQWEJT1XbB0zlO8TDBsGLfPr9vDmCv0n7b-1WyTSPcqdN-5TYS7YU29pwQWDGfYzeOFGhyy_HPeve51xMscmjZ9RwOGVVIIazSXPas_A8A2cW9YIIpmpQ/s1600/Screen+Shot+2020-07-26+at+1.26.57+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="478" data-original-width="1422" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXncl2jt7tzR8TWfVR3zCRnkjQWEJT1XbB0zlO8TDBsGLfPr9vDmCv0n7b-1WyTSPcqdN-5TYS7YU29pwQWDGfYzeOFGhyy_HPeve51xMscmjZ9RwOGVVIIazSXPas_A8A2cW9YIIpmpQ/s640/Screen+Shot+2020-07-26+at+1.26.57+PM.jpg" width="640" /></a></div>
<div>
<br /></div>
<div>
As you adjust the search criteria below, the list of filenames with "node" will adjust in a reactive manner. I discovered the "Kind" by adding a "Kind" column to the search results which showed "Kind." This is also the same "Kind" which you see when selecting a file in Finder and examining its "Info." The Kind of "Unix Executable" is synonymous with Terminal Document in this case.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigL9pdbGGdZ0fiP8zivCshhUHHSlwxBhhZOZ5rfPvrdwseLwgDs9H0BDGPEwgtTtuanFIw5Kk-i8G_xt8rZaf5t4EKKRN8d-VRW2U3IHLd3O8-4ODl7upkhFsbl5EcUuG_tFtJ2WPUe7A/s1600/Screen+Shot+2020-07-26+at+1.16.47+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="479" data-original-width="1600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigL9pdbGGdZ0fiP8zivCshhUHHSlwxBhhZOZ5rfPvrdwseLwgDs9H0BDGPEwgtTtuanFIw5Kk-i8G_xt8rZaf5t4EKKRN8d-VRW2U3IHLd3O8-4ODl7upkhFsbl5EcUuG_tFtJ2WPUe7A/s1600/Screen+Shot+2020-07-26+at+1.16.47+PM.jpg" /></a></div>
<div>
<br /></div>
<h3 style="text-align: left;">
Then give the "node" executable the "context menu->open" treatment:</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAYzhz3mRTelQGf0eZZ0kXqOtIgmkkcthVC62CNj8PuxLPTj4cwv44UzauVlsCduxWMwCjcBw9VqlO8bB69t1fpzJ8yaYSCdI2PqsP7EezxuApM05w2AjQuTuI-Rln1MTUlBoApNvBMNU/s1600/Screen+Shot+2020-07-26+at+1.41.55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="76" data-original-width="602" height="40" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAYzhz3mRTelQGf0eZZ0kXqOtIgmkkcthVC62CNj8PuxLPTj4cwv44UzauVlsCduxWMwCjcBw9VqlO8bB69t1fpzJ8yaYSCdI2PqsP7EezxuApM05w2AjQuTuI-Rln1MTUlBoApNvBMNU/s320/Screen+Shot+2020-07-26+at+1.41.55+PM.png" width="320" /></a></div>
<div>
<br /></div>
<div>
The above action will open a terminal window and launch Node. What this does is register the app with MacOS that the user has marked the binary as trusted even though it wasn't signed. Once the terminal running Node is launched, you can close terminal (or double ctrl-C), as we've achieved the result we needed—registering it for execution. At this point, try out your IDE and you shouldn't see the dialog box anymore.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<br /></div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-79675256078249091942020-05-08T19:39:00.002-07:002020-05-11T00:03:07.675-07:00Full Stack Java Web App, Fact or Fiction?<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-a_CbNLayVBdRoZPtjA3Ah1eA1lJmzOeEXIgAe0Dyq9_TbQwYN0_-0zbOGhuEK4FysI42dH1H0qBQfnv72OlqM030AgErlwYRh4hymhoLUODCLP649fCuTI41UxCM3i8PK5zyPmbwENU/s1600/Playing+with+Java2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="340" data-original-width="525" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-a_CbNLayVBdRoZPtjA3Ah1eA1lJmzOeEXIgAe0Dyq9_TbQwYN0_-0zbOGhuEK4FysI42dH1H0qBQfnv72OlqM030AgErlwYRh4hymhoLUODCLP649fCuTI41UxCM3i8PK5zyPmbwENU/s400/Playing+with+Java2.jpg" width="400" /></a></div>
An Agile tech coach's job is never done in a world where relatively few people can do eXtreme Practices such as TDD. Part of the job is entertainer (make training fun), part of the job is teacher (how the heck do I test this!), and part of the job is adoption (how to get my organization to support me doing this). So I'm faced with creating an enjoyable—no, exciting!—experience for training Java developers TDD. So the formula for success looks like this:<br />
<ol style="text-align: left;">
<li>Useful modern technologies for mainstream workers</li>
<li>Fun</li>
<li>TDD</li>
</ol>
So perfect! I want to take my class on TDD through building a three tier web application, full stack, in Java: DB, micro services, web API.<br />
<br />
<h2 style="text-align: left;">
But WAIT! Can one use Java to build a graphical user interface in the web browser?</h2>
<br />
Well it turns out you CAN. However I had to sift through a lot of claims about "full stack Java" which sometimes means building TCP/IP clients for macro test automation. But come on! Nothing impresses the folks at the IT bar like showing off a build that actually works on a smart phone. <br />
<br />
But I've got an additional "hangup." To me, modern technologies in the UI ought to be *reactive* (event driven), meaning, <b>when an event happens on the server side, it updates the web clients</b>.<br />
<br />
So I went through <a href="https://raygun.com/blog/popular-java-frameworks/" target="_blank">RayGun's article on 17 popular Java frameworks</a>, investigated them, and filtered the list down to three potential candidates. See the following table.<br />
<iframe height="610" src="https://docs.google.com/spreadsheets/d/e/2PACX-1vS0AyW-Vfr2Vxg9TOuF9k3bXJBShZoT02vJImiA05TZdSVSVZcEKA4mEydXL_KRYf7yYvThWdrftmIo/pubhtml?gid=0&single=true&widget=true&headers=false" width="600"></iframe>
<br />
<br />
<h2 style="text-align: left;">
Conclusions</h2>
So ladies and gentleman, <b>full stack</b> Java Web Apps are FACT!<br />
<ul style="text-align: left;">
<li>There are actually <b>THREE</b> frameworks that will allow you to <b>use Java to build a web application view</b>! (See "yes"s in the third column.)</li>
</ul>
<div>
<b>But full stack</b> <b>reactive</b> Java web apps need <b>more investigation.</b></div>
<ul style="text-align: left;">
<li>The big problem for me is that none of the reactive choices use Java to describe a view, <b>with the exception of some branches of GWT.</b></li>
</ul>
If I care only about reactive and give-up on using Java to build the view, then there are three frameworks that would do the job. But that means bringing in TDD in Javascript (for functionality in the front end) into my Java class.<br />
<h2 style="text-align: left;">
Next steps</h2>
<div>
It comes down to the practicality of TDD in one of the GWT branches that are reactive. If it works, then I'm all good. If it doesn't do reactive well or if it doesn't make sense to do TDD in GWT, then I move onto one of the other reactive alternatives.<br />
<br />
I'll build some POC's and evaluate what will work for a Java TDD course that's fun and uses mainstream technologies.</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-44896617613127068522020-04-13T17:16:00.002-07:002020-05-08T12:54:43.172-07:00Splitting a Cell in Excel<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEb4HV1uJ1fG7-oad9hDwPn38xUlAK-L_JeeeUBiayB5oB4ILrtvoMvspL1VD5a7PkH_z-Avr1YidzYF3Yeu4PyyR4PIuoGlgDdwdRcCNR2ZMyn-Nk4LZ7_OiJamsvUDBjGXiPusfLy2o/s1600/excel+cell+division.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1116" data-original-width="1200" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEb4HV1uJ1fG7-oad9hDwPn38xUlAK-L_JeeeUBiayB5oB4ILrtvoMvspL1VD5a7PkH_z-Avr1YidzYF3Yeu4PyyR4PIuoGlgDdwdRcCNR2ZMyn-Nk4LZ7_OiJamsvUDBjGXiPusfLy2o/s320/excel+cell+division.jpg" width="320" /></a></div>
I confess to be a Mac fanboy but sometimes I need to work with people who use Excel rather than Numbers. One problem that took twenty minutes of searching to figure out (but is easy to do with Apple's Numbers spreadsheet) is how to split a cell into two pieces.<br />
<br />
After watching YouTube and reading comments from others who remained confused, it finally hit me: <b>Excel ONLY provides "merge cell."</b> And you don't technically need "split cell" because <b>you can split cells already</b> with "insert row." <b>So the idea is to insert a row and then merge the parts that you don't want split.</b><br />
<br />
<b>Here are the steps: Insert a row, merge cells that shouldn't be split, done!</b><br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2 style="text-align: left;">
Example</h2>
<div>
Here is an example. I want to split the cells to the right of the headers that label the days of the week.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4sCks4OKauEGmeQO1wDcFlqo1QFc4Zea9OPqLAVop0wnGsBQGdldz9vP7rsf2BsQCExssIb1QpnyuDNCD6leMQ7sq5OAxxoGmuvUum5H2Phbh8tKsclbfMq7ZXaPmT92eekaUS1QE3NI/s1600/Screen+Shot+2020-04-13+at+4.34.24+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="498" data-original-width="1586" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4sCks4OKauEGmeQO1wDcFlqo1QFc4Zea9OPqLAVop0wnGsBQGdldz9vP7rsf2BsQCExssIb1QpnyuDNCD6leMQ7sq5OAxxoGmuvUum5H2Phbh8tKsclbfMq7ZXaPmT92eekaUS1QE3NI/s640/Screen+Shot+2020-04-13+at+4.34.24+PM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2 style="text-align: left;">
Insert Row</h2>
<div>
Since insert row inserts above the selected row, you need to select the row beneath your target. So to do Monday, I had to select the row beneath Monday.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0NvJWiMO07ExUW8zdCsn4GWzGHlrq9LOPZ1irKKqATqBameHQvtusJCDwhTY9ujObqzPIkVGHxElxy3gKs_aFwL8kOwHZr70hyzASGZ4JoLD4gVZkwaasVaKFU-FR12wf20HicUHa03w/s1600/Screen+Shot+2020-04-13+at+4.35.06+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="774" data-original-width="454" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0NvJWiMO07ExUW8zdCsn4GWzGHlrq9LOPZ1irKKqATqBameHQvtusJCDwhTY9ujObqzPIkVGHxElxy3gKs_aFwL8kOwHZr70hyzASGZ4JoLD4gVZkwaasVaKFU-FR12wf20HicUHa03w/s320/Screen+Shot+2020-04-13+at+4.35.06+PM.png" width="187" /></a></div>
<h2 style="text-align: left;">
Merge other Cells</h2>
<div>
Select the Monday cell and the cell beneath, then click on "Merge & Center" and then "Merge Cells."</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTSUDGpC0x1p_UX2DdSK3FNr_bgjDidPS9bIgNr3Wk13LF_ETnSrKSGS55F-eVrwq5sYQFLRh5GZkxYkyNP_u_UhYg7dYRjy8Fd6WyMGJF3UGm4TMe9fSUCNOFpkogBH9PD3GmBU-fTcM/s1600/Screen+Shot+2020-04-13+at+4.36.12+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="532" data-original-width="1490" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTSUDGpC0x1p_UX2DdSK3FNr_bgjDidPS9bIgNr3Wk13LF_ETnSrKSGS55F-eVrwq5sYQFLRh5GZkxYkyNP_u_UhYg7dYRjy8Fd6WyMGJF3UGm4TMe9fSUCNOFpkogBH9PD3GmBU-fTcM/s640/Screen+Shot+2020-04-13+at+4.36.12+PM.png" width="640" /></a></div>
<h2 style="text-align: left;">
Wrap-up </h2>
<div>
So Excel doesn't believe in cell division, only cell fusion. :-) And I'll have to either repeat the process more times for each day of the week or copy paste the formatting. If you want to do something different such as split cells vertically, then follow the same process but insert a column rather than a row.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH4Z-72LX9g3ofULjg0QTE-FxMquFNoTl7JpARVWLFXz5NUuRce01OuDpdG-ymuv7uWkkcBNPTNakel7erv1nkEHOqM8_kYGXi_TYezXsOQgxka8AERfOQmNcGaLiu_EFZELO4epOm-2U/s1600/Screen+Shot+2020-04-13+at+4.36.28+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="202" data-original-width="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH4Z-72LX9g3ofULjg0QTE-FxMquFNoTl7JpARVWLFXz5NUuRce01OuDpdG-ymuv7uWkkcBNPTNakel7erv1nkEHOqM8_kYGXi_TYezXsOQgxka8AERfOQmNcGaLiu_EFZELO4epOm-2U/s1600/Screen+Shot+2020-04-13+at+4.36.28+PM.jpg" /></a></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com1tag:blogger.com,1999:blog-284399302764909932.post-51514722618596670552020-04-11T09:41:00.003-07:002020-04-11T10:01:52.410-07:00Games for the Virtual Office<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTz2DvQsbkqFJ_zB6H2cUneUN54Y23GaXo4XlLylQ7l7lhEviOS_H2hP1V8kCzYOn7cwKkiqhFk4P7dhQ8_qX7-ls8zISxkoBLjUDYqqpLlu8gqgp_xmlnwjjngywevK-z6PKH1am6AWk/s1600/Pong-653x400.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="400" data-original-width="653" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTz2DvQsbkqFJ_zB6H2cUneUN54Y23GaXo4XlLylQ7l7lhEviOS_H2hP1V8kCzYOn7cwKkiqhFk4P7dhQ8_qX7-ls8zISxkoBLjUDYqqpLlu8gqgp_xmlnwjjngywevK-z6PKH1am6AWk/s320/Pong-653x400.png" width="320" /></a></div>
<h2 style="text-align: left;">
Games you can play with colleagues at the virtual office.</h2>
The Agile Thoughts podcast will produce an episode on games for the virtual office. Please share your favorites and I'll put them in the episode. You can share by either Tweeting to @LancerKind, commenting on this post, or adding to this <a href="https://news.ycombinator.com/item?id=22842586" target="_blank">Hacker News thread</a>.<br />
<br />
Click here for <a href="https://agilenoir.biz/en/agilethoughts/" target="_blank">Agile Thoughts</a>. </div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-292428887538603192020-03-10T18:16:00.000-07:002020-03-10T18:16:19.042-07:00USB 3.0/3.1 ruins Wi-Fi connectivity<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4aYQJyI-Ch6bYrU05SMpXcmj2gi8TqAzNAwAD9M_C6RWSDJkHW-k4vOTGig-xcuYN5ItP4vxxtGyDWo7bIZqwKOTxdZmVC5BUGdz_hxM6nexZ-V810vMHc3nsoPRJYEOLgs566pY5QcE/s1600/IMG_5101.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="322" data-original-width="430" height="239" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4aYQJyI-Ch6bYrU05SMpXcmj2gi8TqAzNAwAD9M_C6RWSDJkHW-k4vOTGig-xcuYN5ItP4vxxtGyDWo7bIZqwKOTxdZmVC5BUGdz_hxM6nexZ-V810vMHc3nsoPRJYEOLgs566pY5QcE/s320/IMG_5101.PNG" width="320" /></a></div>
Have you ever had a webcast or video conference to attend and then the very moment you need your internet to work smoothly, it performs like elves are hanging tinfoil ornaments on your Wifi router? Perhaps you, like me, are hooking in keyboards and docking stations via the USB ports, and <b>doing so could be ruining your internet connectivity! </b>And not by a little, but a LOT.<br />
<h2 style="text-align: left;">
USB 3.0/3.1 Hubs</h2>
Honestly, USB 3.0 hubs should come <b>giant</b> warning labels. Many USB devices you've been buying have some <b>fine print that mentions "may reduce performance of Wifi."</b> For the last decade (since the time I got my first USB 3.0 hub) I've been having strange wifi performance which then causes phone calls to my network provider. So for years I've been barking at the wrong people, because it turns out, <b>USB 3.0/3.1 hubs cause interference that interfere with 2.4 GHZ wifi!</b><br />
<br />
Let me break it down for you with a Behavior Driven Development scenario:<br />
<b>Given</b> laptop <b>And</b> usb 3.0 hub<br />
<b>When</b> using Wifi over 2.4GHZ<br />
<b>Then</b> internet performance is greatly reduced (reduction by 20% - 100%)<br />
<br />
Check out these before and after photos done on a late 2019 Mac Book Pro and pay attention to the speed difference from when the blue cable is plugged in and when it is not (the cable leads to my 3.0 USB hub which sits about a foot away from my laptop):<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHZCOyYrt1UF5jwAQKiIdU1rOPvnLdofWc97UZg0EfevoFNm8zEBCumShyphenhyphen7B6Y0OaEL2uK61BlWPVJHAJ3me93R817JW8Ucl-4ZEolTxNe2-Ecy2z9wC6U4wMuUopAzrX2Sep30sP-6FI/s1600/IMG_0998.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHZCOyYrt1UF5jwAQKiIdU1rOPvnLdofWc97UZg0EfevoFNm8zEBCumShyphenhyphen7B6Y0OaEL2uK61BlWPVJHAJ3me93R817JW8Ucl-4ZEolTxNe2-Ecy2z9wC6U4wMuUopAzrX2Sep30sP-6FI/s640/IMG_0998.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">USB 3.0 hub disconnected</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU3uBWHLBg_DG2vdAXC_XvZZcYL4LiaYC1JEDoxXOvbHDos5XUm1sXnfk-vJx9ZwNU2uhLHapS7tF6pfCxQ9RWADwRFgnkQMd9-PVBHwubBbqgs81jazc76yqmMzb0iy6fV1FXYy-S0Ss/s1600/IMG_8957.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU3uBWHLBg_DG2vdAXC_XvZZcYL4LiaYC1JEDoxXOvbHDos5XUm1sXnfk-vJx9ZwNU2uhLHapS7tF6pfCxQ9RWADwRFgnkQMd9-PVBHwubBbqgs81jazc76yqmMzb0iy6fV1FXYy-S0Ss/s640/IMG_8957.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">USB hub connected</td></tr>
</tbody></table>
I've replicated the problem every other laptop I've in the house and the problem is completely reproducible. The amount of reduction varies between 20%-100% (100% mean no data moves from or to the laptop). USB 3.0/3.1 devices work fine when directly connected to my laptop. <b>The problem happens when a hub is added.</b><br />
<h2 style="text-align: left;">
Solution: Turn off the 2.4GHZ channel on your Wifi</h2>
<div>
Most people have their WIFI access points sending BOTH 2.4GHZ and 5GHZ. 2.4GHZ Wifi has a longer range at a slower maximum speed. 5GHZ Wifi has over double the speed but degrades over range faster than 2.4GHZ. This means that most people who buy Wifi routers, let the routers use both channels (2.4 and 5 GHZ) in which case their laptop will shift between the two channels as needed. <br />
<br />
<u>But this is a bad idea when using 3.0 or 3.1 USB devices.</u><br />
<u><br /></u></div>
<div>
Configuring your Wifi router to turn off the 2.4GHZ radio solves the problem with the laptop. But if you're like me, you've a few things like printers, Xbox 360, or home automation that only connect on 2.4ghz. So now what?</div>
<h2 style="text-align: left;">
Solution 2: Differentiation 2.4 and 5 GHZ with different SIDs</h2>
<div>
Go back to your Wifi router and rename the SSID for the 2.4 GHZ channel to ... <blah blah="">2.4, and don't tell your laptop about that other SSID. Then you're laptop stays off of 2.4GHZ and your older devices can use the 2.4GHZ channel.</blah><br />
<h2 style="text-align: left;">
Solution 3: Use a Wired Internet Connection</h2>
<div>
I've not tested this, but I expect that a hardwire connection into the laptop will solve the problem whether or not a hub is plugged in, as long as your laptop doesn't suddenly decide to grab Wifi.</div>
<br /></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-24316228493614055212019-12-19T06:45:00.002-08:002019-12-19T06:45:34.625-08:00TDD and mocking a Google Application Script with GAST<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="text-align: left;"><br /></span></h2>
<h2 style="text-align: left;">
<span style="text-align: left;">What is TDD</span></h2>
<div class="separator" style="clear: both; text-align: left;">
Motivation for writing this: teach the TDD workflow with GAST. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Why do TDD? Expected outcomes. Reference GAST. Mention other tools. W</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h2 style="text-align: left;">
TDD Workflow</h2>
<div class="separator" style="clear: both; text-align: left;">
Look at the code or do some white boarding/thinking about what code change you want to make. Once you identify your opening move--change existing code or create something brand new, start the TDD flow.</div>
<h2 style="text-align: left;">
Tutorial</h2>
<h2 style="text-align: left;">
Step 0, Setup GAS Project</h2>
<h2 style="text-align: left;">
Step 1, Define the next simplest step in functionality</h2>
<div>
Describe what is meant by simplest. And not necessarily complete. Just a step.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8u-jUfHn-PYBByew9VPikrzVLYeyFq0VW0EOMxlr3oZ25osXfidrew5NIeNpRGnz5PHK9iXo-RyhMWWyS32PSlLGVAVvy6LpTcmPtmWtED_-iK6SmcgxrEWjp_Z3k6diRpDqzv7Pa8dU/s1600/Screen+Shot+2019-01-10+at+9.33.44+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="262" data-original-width="874" height="95" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8u-jUfHn-PYBByew9VPikrzVLYeyFq0VW0EOMxlr3oZ25osXfidrew5NIeNpRGnz5PHK9iXo-RyhMWWyS32PSlLGVAVvy6LpTcmPtmWtED_-iK6SmcgxrEWjp_Z3k6diRpDqzv7Pa8dU/s320/Screen+Shot+2019-01-10+at+9.33.44+AM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYXgfDlabRX8LZxXq_sZGLhXZqtNf5mq0rDydxe3Qb__ePI_LaIgPTKbyCdL0qj26vCw4D2VfOM-BwPkAVkPWmJ4w5ugpuqpMkqhAjBt1itfTWCv6gGNTHDraqPr1v6Gi3LYXdpZcmULc/s1600/Screen+Shot+2019-01-10+at+9.33.53+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="940" data-original-width="1438" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYXgfDlabRX8LZxXq_sZGLhXZqtNf5mq0rDydxe3Qb__ePI_LaIgPTKbyCdL0qj26vCw4D2VfOM-BwPkAVkPWmJ4w5ugpuqpMkqhAjBt1itfTWCv6gGNTHDraqPr1v6Gi3LYXdpZcmULc/s320/Screen+Shot+2019-01-10+at+9.33.53+AM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGl3xgDore7qIcO5y8wkOo93PxN1OpNGW2zI6LN3NqTn91y_sWokgAjG7_X2KslmP-HPvem2Ej37l3J08cd7Gmafp_DDqbWFB6ICCzu_YiguLrevTpG2Sryt_KOw9_mjEo07nKzs7H7V8/s1600/Screen+Shot+2019-01-10+at+9.34.15+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="399" data-original-width="1600" height="79" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGl3xgDore7qIcO5y8wkOo93PxN1OpNGW2zI6LN3NqTn91y_sWokgAjG7_X2KslmP-HPvem2Ej37l3J08cd7Gmafp_DDqbWFB6ICCzu_YiguLrevTpG2Sryt_KOw9_mjEo07nKzs7H7V8/s320/Screen+Shot+2019-01-10+at+9.34.15+AM.png" width="320" /></a></div>
<br />
<h2 style="text-align: left;">
Step 2, Work on Test until it has a fully functioning FAILURE</h2>
<div>
The test should and what it's testing should compile but not comprise of the product code yet. Said another way, the test should be completely formed (meaning no errors or exceptions occurring in the test code) and successfully calling your product code (which is simply a stub at the beginning).</div>
<div>
<br /></div>
<h2 style="text-align: left;">
Step 3, Add functionality to get the test to PASS</h2>
<h2 style="text-align: left;">
Step 4, Refactor</h2>
<h2 style="text-align: left;">
Step 5, Go Back to Step 1 or Check in if Finished</h2>
<div>
<h2 style="text-align: left;">
By the way: learn more about TDD and micro testing</h2>
Check out the <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/">Agile Thoughts podcast</a>. This podcast gets the Confessions Of An Agile Coach's endorsement (naturally, since the same people produce it) of quality materials for developers and teams trying to get coding done in a way that avoids dealing with legacy issues such as bugs and hard to maintain code. Agile Thoughts has a lot of great TDD conceptual content along with a radio drama about the difficulties to getting a TDD initiative started. <b>Click the podcast cover below or <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" target="_blank">here</a> for more information.</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Agile Thoughts podcast" border="0" data-original-height="1024" data-original-width="1024" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaaNk_KY2gesIbqVamAc8uqQuQLm3Y5hG59YEAGFyAdcyX1z73ADGxBVP6y66irUWgY90Vm5C5gDfq1C2YlME-gfsSR-Yv9nGq8pCn0oX6y4K3-mvzpfpkOZTIwAdHcBydjSXDbFw1kPg/s400/UNADJUSTEDNONRAW_thumb_39.jpg" title="" width="400" /></a></div>
</div>
<div>
<br /></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-21101456258931812632019-12-18T17:39:00.004-08:002020-05-08T16:06:56.319-07:00Creating a Coding Story (Coding Stories) with STS or Eclipse<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDPRDNkQjGRTMKEq-hKnGL661FCYad4njA6IAHo4jSV08aSdsIB6cTJUDVTo0lT6oLiY-ikqW0wExropsrdMN_neLEdLXM1Q3e8p9Ag3lO9pJb6jVoPKGiuHZunVidELiP-5URjD5kjR4/s1600/Screen+Shot+2019-12-18+at+3.00.08+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="72" data-original-width="412" height="54" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDPRDNkQjGRTMKEq-hKnGL661FCYad4njA6IAHo4jSV08aSdsIB6cTJUDVTo0lT6oLiY-ikqW0wExropsrdMN_neLEdLXM1Q3e8p9Ag3lO9pJb6jVoPKGiuHZunVidELiP-5URjD5kjR4/s320/Screen+Shot+2019-12-18+at+3.00.08+PM.png" width="320" /></a><a href="http://codingstories.io/">CodingStories.io</a> is a wonderful tool for senior developers to spread the knowledge of craftsmanship through an organization. Here is a good tool chain for doing this with STS/Eclipse:</div>
<ul style="text-align: left;">
<li>Maven</li>
<li>JUnit 5</li>
<li>Git</li>
</ul>
<div>
Why Maven? Unless you want to assume all your Coding Stories readers will be using your IDE of choice (or let them work out setting up the dependencies), use Maven to abstract away your IDE for dependency management.</div>
<div>
Why JUnit5? Because it's the most modern and is now well integrated into IDEs.</div>
<div>
Why Git? Because that's what Coding Stories uses for integration.</div>
<h2 style="text-align: left;">
Maven</h2>
<div>
Using STS's wizard, build a Maven project.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhs7xrSoCdrMJOYT1HE2moiEPzo5S4lt-PORqcVdN8170wrhGiUClJdK2rSBnHvMza0bWuHmjy4Bs1DNc8MGkWSjFJUHE7eUBhEJNuh7VOukOldbKa5VHU95Y5sHCD34xXqsoDP-QC12Vg/s1600/Screen+Shot+2019-12-15+at+9.24.06+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="774" data-original-width="1286" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhs7xrSoCdrMJOYT1HE2moiEPzo5S4lt-PORqcVdN8170wrhGiUClJdK2rSBnHvMza0bWuHmjy4Bs1DNc8MGkWSjFJUHE7eUBhEJNuh7VOukOldbKa5VHU95Y5sHCD34xXqsoDP-QC12Vg/s640/Screen+Shot+2019-12-15+at+9.24.06+AM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD1ifK2NT-Uysc1ix2PQo_uAU4En7Liy5U2isJ6y1j_ntEiDDULC-VRDFZhsFTbbBo_5z4ULe0UFV8snmm__OqQgK-qA6BV5-bOMAr1-uX2RJrzSrk54vS1eK0IO-h6rsQyqkTf6aRQEM/s1600/Screen+Shot+2019-12-15+at+9.24.22+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1020" data-original-width="1060" height="612" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD1ifK2NT-Uysc1ix2PQo_uAU4En7Liy5U2isJ6y1j_ntEiDDULC-VRDFZhsFTbbBo_5z4ULe0UFV8snmm__OqQgK-qA6BV5-bOMAr1-uX2RJrzSrk54vS1eK0IO-h6rsQyqkTf6aRQEM/s640/Screen+Shot+2019-12-15+at+9.24.22+AM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPa65dTr-aj0s8b32v50gM-fZ56zI-ol_mW6xWq-3sQkf6go1hfqBZpTuH1yDSKLYkMRrn_i1z1FVccA7xqvX2B1XnAF-tE8LvVYCP5O_QXZKKjZSyWpIQcogZGj-Gts2L1CLAj5VJoCk/s1600/Screen+Shot+2019-12-15+at+9.24.36+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1100" data-original-width="1248" height="563" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPa65dTr-aj0s8b32v50gM-fZ56zI-ol_mW6xWq-3sQkf6go1hfqBZpTuH1yDSKLYkMRrn_i1z1FVccA7xqvX2B1XnAF-tE8LvVYCP5O_QXZKKjZSyWpIQcogZGj-Gts2L1CLAj5VJoCk/s640/Screen+Shot+2019-12-15+at+9.24.36+AM.jpg" width="640" /></a></div>
<br />
The below seems to be the simplest POM type.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwmrmUD1rzzkm0DWboCGGXxLFW6ryKRpjcpRkFvGKqAzdyJgxujjSZlMbHnAwoEcDuo-XnMZz4weDJlKbygZ9jWkOMXIavpYPGtbhZP9k2A2qevOEjh6Y_m8Y539aTrWm7yrwZHxkdiNM/s1600/Screen+Shot+2019-12-15+at+9.25.04+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1072" data-original-width="1248" height="547" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwmrmUD1rzzkm0DWboCGGXxLFW6ryKRpjcpRkFvGKqAzdyJgxujjSZlMbHnAwoEcDuo-XnMZz4weDJlKbygZ9jWkOMXIavpYPGtbhZP9k2A2qevOEjh6Y_m8Y539aTrWm7yrwZHxkdiNM/s640/Screen+Shot+2019-12-15+at+9.25.04+AM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlnk0T42_QoZbYYMya4liPz_fHu8lOigHKLLRXw8T_Rc8F4vywGpqUdEXEzYjC2s70iByoELGn36h__D-qPWXqQB0ZM5dN0KLuLUZwblgNJSVv_oFgO04gwG0n345FDIZlfrN9JXE3DJY/s1600/Screen+Shot+2019-12-15+at+9.25.56+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1236" height="556" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlnk0T42_QoZbYYMya4liPz_fHu8lOigHKLLRXw8T_Rc8F4vywGpqUdEXEzYjC2s70iByoELGn36h__D-qPWXqQB0ZM5dN0KLuLUZwblgNJSVv_oFgO04gwG0n345FDIZlfrN9JXE3DJY/s640/Screen+Shot+2019-12-15+at+9.25.56+AM.jpg" width="640" /></a></div>
<br />
Run the maven build and at should successfully build the Maven HelloWorld application. (See troubleshooting if not.)<br />
<br />
Rename the "project" folder per Coding Stories convention. (I needed to add "-java" onto the end.) Later, this will be our Git repository.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKR5ei4elMGiKy54VwfijxCb9ZP3qYfjMGuTIOoTG5ev0vto_hAsYN42JC3dy8xpyRQQZkaFR7OBsmYBJuoR4pMBD0kiFOsEc6DmdIB7jNi6Qzn-Ww8lfUw6Nl9KLrg6V21bryws6EdWc/s1600/Screen+Shot+2019-12-13+at+3.30.46+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="282" data-original-width="1600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKR5ei4elMGiKy54VwfijxCb9ZP3qYfjMGuTIOoTG5ev0vto_hAsYN42JC3dy8xpyRQQZkaFR7OBsmYBJuoR4pMBD0kiFOsEc6DmdIB7jNi6Qzn-Ww8lfUw6Nl9KLrg6V21bryws6EdWc/s1600/Screen+Shot+2019-12-13+at+3.30.46+PM.jpg" /></a></div>
<div>
<br /></div>
<div>
The next section describes how to adjust the POM file and IDE to handle JUnit 5.</div>
<h2 style="text-align: left;">
JUnit 5</h2>
<div>
Although IDEs have JUnit 5 integration, many of them default to JUnit 4. The following POM describes how to use JUnit 5 in your Maven project:</div>
<!-- To make the below work, I'm using a custom CSS which I installed into Blogger via Blogger->Theme, Edit Theme, then injected before the end of the Head section.-->
<!-- See http://www.craftyfella.com/2010/01/syntax-highlighting-with-blogger-engine.html -->
<!-- The XML still must be encoded. It still won't look great in "Compose" editor, but looks fine in preview and when live. I used this to encode: https://www.opinionatedgeek.com/codecs/htmlencoder -->
<br />
<!--<pre class="brush: Xml"> -->
<pre class="Language" name="code"><project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>biz.agilenoir</groupId>
<artifactId>helloworldmocking</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>TDD going from Hello World to Mocking</name>
<url>http://AgileNoir.biz</url>
<dependencies>
<!--JUnit tests depend on the engine artifact to build-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<!-- Maven by default doesn't support Java 13 so the below sets that up -->
<configuration>
<source>13</source>
<target>13</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>
</pre>
<pre class="Language" name="code"></pre>
<div>
What this does:<br />
<br />
<ul style="text-align: left;">
<li>POM boiler plate: The first paragraph tells XML parsers about the schema to use for validation and declares the namespace.</li>
<li>project identity: your project information here</li>
<li>package the build as a Jar (required to package it into something)</li>
<li>dependencies: JUnit Jupiter is declared here</li>
<li>build/plugins: </li>
<ul>
<li>Maven plugin: </li>
<ul>
<li>Since I'm building with Java 13, I declare a modern version of the maven plugin to be used when building, 3.8.1 (you see, maven itself is a plugin). The default maven plugin doesn't support newer versions of Java.</li>
<li>Configure the maven plugin to use Java 13</li>
</ul>
<li>Surefire plugin: used to execute unit tests.</li>
</ul>
</ul>
</div>
<div>
Configure the IDE's project to use JUnit 5:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBeGAW0wqBhPUMBaHwuJ7Wd5jN6mb1WwiChRZ-aBt05gsu4ENywgrE8Tj8PGcDxdELdlU_CFFoJetTQtTdvUUacGGWl0wvV7Vj-h3QTbmhaE6VZ96nbRVvRVy7KeQIb-vQno4RP1LhPpQ/s1600/Screen+Shot+2019-12-18+at+4.49.09+PM.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1168" data-original-width="633" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBeGAW0wqBhPUMBaHwuJ7Wd5jN6mb1WwiChRZ-aBt05gsu4ENywgrE8Tj8PGcDxdELdlU_CFFoJetTQtTdvUUacGGWl0wvV7Vj-h3QTbmhaE6VZ96nbRVvRVy7KeQIb-vQno4RP1LhPpQ/s400/Screen+Shot+2019-12-18+at+4.49.09+PM.jpg" width="216" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjczqZP9kwNFOVfIurXS2pVt6uZrH9MOwbTjspP_GpQm5szIn6XPhuxvfbvUKLHvGcBPN1Lil4qMbc_K0z7KOB8Npq58xgBEq04uMmpfBXUelXPFbA2AkMfXGFVnRX0b-0n4e5KD4mk62E/s1600/Screen+Shot+2019-12-18+at+4.49.32+PM.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="945" data-original-width="1600" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjczqZP9kwNFOVfIurXS2pVt6uZrH9MOwbTjspP_GpQm5szIn6XPhuxvfbvUKLHvGcBPN1Lil4qMbc_K0z7KOB8Npq58xgBEq04uMmpfBXUelXPFbA2AkMfXGFVnRX0b-0n4e5KD4mk62E/s640/Screen+Shot+2019-12-18+at+4.49.32+PM.jpg" width="640" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjykXZNzcxQ7PhqGpXdBYgMQtuzTCu03zZ9_mx9NCKiQoceydulh6fzD8m3p5aDveSmP0Rr45wnTfF9GlU64Z7mP6NnDnWX5IyaeIH4us-W-aKvx8u_oI7LZVC_eV7QoSrxMKtzjM_ALP0/s1600/Screen+Shot+2019-12-18+at+4.49.43+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="828" data-original-width="1080" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjykXZNzcxQ7PhqGpXdBYgMQtuzTCu03zZ9_mx9NCKiQoceydulh6fzD8m3p5aDveSmP0Rr45wnTfF9GlU64Z7mP6NnDnWX5IyaeIH4us-W-aKvx8u_oI7LZVC_eV7QoSrxMKtzjM_ALP0/s400/Screen+Shot+2019-12-18+at+4.49.43+PM.jpg" width="400" /></a></div>
<br />
Add a simple JUnit 5 test to test the configuration and run it from Eclipse. <br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBZ6OeDE8N34WwHyqxVrm4C7HaWiNpaZJEr0uAAGqY85PKlu60MK9q1N9PkG1_Lk5B5ROSylQvdtNZineFsx5Mq6IYkMASOArXhPLsOI1PgIk-hFDdxxYtgUbYjWchbbp0zGyZOEaMGuA/s1600/Screen+Shot+2019-12-18+at+4.56.53+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="680" data-original-width="1374" height="315" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBZ6OeDE8N34WwHyqxVrm4C7HaWiNpaZJEr0uAAGqY85PKlu60MK9q1N9PkG1_Lk5B5ROSylQvdtNZineFsx5Mq6IYkMASOArXhPLsOI1PgIk-hFDdxxYtgUbYjWchbbp0zGyZOEaMGuA/s640/Screen+Shot+2019-12-18+at+4.56.53+PM.jpg" width="640" /></a></div>
<br /></div>
<div>
<blockquote class="tr_bq">
package biz.agilenoir.helloworldmocking;<br />
import static org.junit.jupiter.api.Assertions.*;<br />
import org.junit.jupiter.api.Test;<br />
class HelloWorldTest {<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>@Test<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>void test() {<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>fail("Not yet implemented");<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br />
}</blockquote>
Run the test and observe that it fails.<br />
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7_r-652L9jwQzscLxa5VIYXCq7HWlH2EMlpNhAiDdWuv6ye2J1NS4awXnc1ie_f1NF-r9aIsfrDonuh9XJ8GzpwSSSCsaiWdThWrzheg5Jzoi3ztgAw4dpvquIFoHL7v4rlb7lKYKta4/s1600/Screen+Shot+2019-12-16+at+6.46.21+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="1398" height="366" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7_r-652L9jwQzscLxa5VIYXCq7HWlH2EMlpNhAiDdWuv6ye2J1NS4awXnc1ie_f1NF-r9aIsfrDonuh9XJ8GzpwSSSCsaiWdThWrzheg5Jzoi3ztgAw4dpvquIFoHL7v4rlb7lKYKta4/s640/Screen+Shot+2019-12-16+at+6.46.21+PM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw1Wn2cAEJ_t85ykhj_Ma4N0G9P6TS64jGeI1wP7DWdsYIR2FAFfaiZWlhPdhF5sX500DF93ERGky0-svg7IUnyjMgD4xlpdiT-KTB7PMKdpfGdE_Xocr1nj9dGDZfMrazfkhbaiM83gY/s1600/Screen+Shot+2019-12-18+at+5.00.46+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="572" data-original-width="1538" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw1Wn2cAEJ_t85ykhj_Ma4N0G9P6TS64jGeI1wP7DWdsYIR2FAFfaiZWlhPdhF5sX500DF93ERGky0-svg7IUnyjMgD4xlpdiT-KTB7PMKdpfGdE_Xocr1nj9dGDZfMrazfkhbaiM83gY/s640/Screen+Shot+2019-12-18+at+5.00.46+PM.jpg" width="640" /></a></div>
<br />
<br />
Run the test from Maven. Watch the console for the test report.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPg3vVwjSrSJtNuwzcQGZK3JcVbb9tRbNGlZGhtPffpoam2gGzZDekpIxpQlaIU8cIXlJhcOfALLt_bvQYmqwVtUHJ_1PRNAPF8aItIoo2SVxqYQaSv6sNU1qsQn2E0JsWom2EZOBbGos/s1600/Screen+Shot+2019-12-18+at+5.02.50+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="702" data-original-width="1274" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPg3vVwjSrSJtNuwzcQGZK3JcVbb9tRbNGlZGhtPffpoam2gGzZDekpIxpQlaIU8cIXlJhcOfALLt_bvQYmqwVtUHJ_1PRNAPF8aItIoo2SVxqYQaSv6sNU1qsQn2E0JsWom2EZOBbGos/s640/Screen+Shot+2019-12-18+at+5.02.50+PM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR9zvUkcPKU06DZBYaDdKxSNF8p89Qx34mFA-JqRC2PWAutLKi0kGMiEL0064BajQA4AppfZnxDSxSWZXbTj0Xb5E5sgWbZ5jXOY38GAAAu0Nfk8WVtAZEDbXgytqCRNKL7895zTkQ0x4/s1600/Screen+Shot+2019-12-18+at+5.03.18+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="568" data-original-width="1566" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR9zvUkcPKU06DZBYaDdKxSNF8p89Qx34mFA-JqRC2PWAutLKi0kGMiEL0064BajQA4AppfZnxDSxSWZXbTj0Xb5E5sgWbZ5jXOY38GAAAu0Nfk8WVtAZEDbXgytqCRNKL7895zTkQ0x4/s640/Screen+Shot+2019-12-18+at+5.03.18+PM.jpg" width="640" /></a></div>
<br />
If you want to see the test pass, make a change, then run the unit test. All that's left is to configure Git for CodingStories.<br />
<br />
<h2 style="text-align: left;">
By the way: regarding TDD</h2>
Check out the <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/">Agile Thoughts podcast</a>. This podcast gets the Confessions Of An Agile Coach's endorsement (naturally, since the same people produce it) of quality materials for developers and teams trying to get coding done in a way that avoids dealing with legacy issues such as bugs and hard to maintain code. Agile Thoughts has a lot of great TDD conceptual content along with a radio drama about the difficulties to getting a TDD initiative started. <b>Click the podcast cover below or <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" target="_blank">here</a> for more information.</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;">
<img alt="Agile Thoughts podcast" border="0" data-original-height="1024" data-original-width="1024" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaaNk_KY2gesIbqVamAc8uqQuQLm3Y5hG59YEAGFyAdcyX1z73ADGxBVP6y66irUWgY90Vm5C5gDfq1C2YlME-gfsSR-Yv9nGq8pCn0oX6y4K3-mvzpfpkOZTIwAdHcBydjSXDbFw1kPg/s400/UNADJUSTEDNONRAW_thumb_39.jpg" title="" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2 style="text-align: left;">
Configure Git</h2>
<div>
Using STS, create a Git repository inside your workspace (there is some controversy over whether this is the best use of Git, but as there is controversy and "no one right way" I think it's alright in this case.)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI5KW4KPpnLt4nHlASoAxguP1PAfrtE8Ee5RR6qRoYsAgc-sHRxvntZfynoHeMBl1bxDueq6OSYCbItwWSCzHNO_o6gwrRwiDO5TaGPZ5IspyyjrW8bVHyf2I08omdNsmKfRsq6SExNU0/s1600/Screen+Shot+2019-12-18+at+5.22.43+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1164" data-original-width="1302" height="571" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI5KW4KPpnLt4nHlASoAxguP1PAfrtE8Ee5RR6qRoYsAgc-sHRxvntZfynoHeMBl1bxDueq6OSYCbItwWSCzHNO_o6gwrRwiDO5TaGPZ5IspyyjrW8bVHyf2I08omdNsmKfRsq6SExNU0/s640/Screen+Shot+2019-12-18+at+5.22.43+PM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtToBexhlp1zMimmEGPv9HQdgEbAV5Z40j8sh-uXR6tXbCTtXOq-JXpR3xkQ_qv95l80Hg_VZP-FvE12PfXfj8t0qdESPvuEpkMOj1aV6ZMF7J6W-LwG3hQ07xhsDT4B-KGopkqi7gWM8/s1600/Screen+Shot+2019-12-18+at+5.22.54+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1002" data-original-width="1064" height="601" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtToBexhlp1zMimmEGPv9HQdgEbAV5Z40j8sh-uXR6tXbCTtXOq-JXpR3xkQ_qv95l80Hg_VZP-FvE12PfXfj8t0qdESPvuEpkMOj1aV6ZMF7J6W-LwG3hQ07xhsDT4B-KGopkqi7gWM8/s640/Screen+Shot+2019-12-18+at+5.22.54+PM.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu8egXE-9HkJF9wjaLPA1Hr1A1aNyjJTyz_geLcpdRuZ_plkniEJqr0mDGZre4rYxGrCsNNqt035dgpL7zETTtLq0ZB5Kl6xBHQImWeXYDmBMA1F0gM6nouILMhPj5oyF7bReATjcqqSo/s1600/Screen+Shot+2019-12-18+at+5.23.20+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1002" data-original-width="1060" height="603" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu8egXE-9HkJF9wjaLPA1Hr1A1aNyjJTyz_geLcpdRuZ_plkniEJqr0mDGZre4rYxGrCsNNqt035dgpL7zETTtLq0ZB5Kl6xBHQImWeXYDmBMA1F0gM6nouILMhPj5oyF7bReATjcqqSo/s640/Screen+Shot+2019-12-18+at+5.23.20+PM.jpg" width="640" /></a></div>
<br />
<br />
The project now has "badges" for M-maven, J-java, and a little gold storage icon for Git.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv_A8YQq-6bmiCnMEushI7ry1NJQW6cHDwAvAknkhxyu7fFF4OKKVPy10WgzGOPLFa5DZANiaBC3u_j40y2goU-EOi1FBf0PMUjr3xGGtNbjdrdnkkLMCwA6u3BcJ5ynNO0Rrxr7GNwmA/s1600/Screen+Shot+2019-12-18+at+5.27.10+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="38" data-original-width="492" height="30" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv_A8YQq-6bmiCnMEushI7ry1NJQW6cHDwAvAknkhxyu7fFF4OKKVPy10WgzGOPLFa5DZANiaBC3u_j40y2goU-EOi1FBf0PMUjr3xGGtNbjdrdnkkLMCwA6u3BcJ5ynNO0Rrxr7GNwmA/s400/Screen+Shot+2019-12-18+at+5.27.10+PM.jpg" width="400" /></a></div>
</div>
<h2 style="text-align: left;">
Write your Coding Story</h2>
<div>
Now that your environment is all tested out. It's time to build your coding story. Follow <a href="https://codingstories.io/#head-section1" target="_blank">"how to" at CodingStories</a> to create your story. When you're finished, tell all your software developer friends and co-workers all about it. Send them links. Brag to your boss. You know, socialize your tools to help others.<br />
<br />
XXX Link to my own coding story coming soon to here XXX</div>
<div>
<h3 style="text-align: left;">
Tweet me if you get stuck: @LancerKind</h3>
</div>
<h1 style="text-align: left;">
References</h1>
<div>
<div>
https://howtodoinjava.com/junit5/junit5-maven-dependency/<br />
https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html</div>
</div>
<h1 style="text-align: left;">
Troubleshooting</h1>
<h4 style="text-align: left;">
Problem: Maven build gives me the following:</h4>
<div>
[ERROR] Source option 5 is no longer supported. Use 7 or later.</div>
<div>
[ERROR] Target option 5 is no longer supported. Use 7 or later.</div>
<div>
[INFO] 2 errors </div>
<div>
[INFO] -------------------------------------------------------------</div>
<div>
[INFO] ------------------------------------------------------------------------</div>
<div>
[INFO] BUILD FAILURE</div>
<h4 style="text-align: left;">
Solution: Maven defaults to Java 1.6 and will default to a version of the maven plugin that won't work with java 9 or higher unless changes are made in "configuration" (https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html).</h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvAsNghlvaA3Y6T_-D85sEiezs4EDY0qwsQYEMAS_c4FkZSH2EtUR7PpXQy0e0pUJhL3VAD0bLF_lhIe3rkAfyqv54QYLkgdo384-zMT4eNXTld-blir5k2o-SkR3CduCfZGT6am2dgd0/s1600/Screen+Shot+2019-12-18+at+5.34.27+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="526" data-original-width="1012" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvAsNghlvaA3Y6T_-D85sEiezs4EDY0qwsQYEMAS_c4FkZSH2EtUR7PpXQy0e0pUJhL3VAD0bLF_lhIe3rkAfyqv54QYLkgdo384-zMT4eNXTld-blir5k2o-SkR3CduCfZGT6am2dgd0/s640/Screen+Shot+2019-12-18+at+5.34.27+PM.jpg" width="640" /></a></div>
<div>
<br /></div>
<div>
<h4 style="text-align: left;">
Problem: Can't execute unit test in STS</h4>
</div>
</div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhphnUmnRDd9sMrKwWsCUTt190NNeT04XhccbkOhva-G_gTSosdaQF95bvw2oEUpVMTXI8CnOvWQuqommA_iBHHVSoO1gPf1GsQX8aAFKvayLbIUDEXWwDRWc8AUGNmf-dGTAXARU2zvhA/s1600/Screen+Shot+2019-12-16+at+6.46.34+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="276" data-original-width="1048" height="168" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhphnUmnRDd9sMrKwWsCUTt190NNeT04XhccbkOhva-G_gTSosdaQF95bvw2oEUpVMTXI8CnOvWQuqommA_iBHHVSoO1gPf1GsQX8aAFKvayLbIUDEXWwDRWc8AUGNmf-dGTAXARU2zvhA/s640/Screen+Shot+2019-12-16+at+6.46.34+PM.jpg" width="640" /></a><br />
<h4 style="text-align: left;">
Solution: Click "OK" and setup a run configuration for JUnit 5.</h4>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqX6zcBNqwRjVUf4PkYy7xBGJOThKzS1m8l9_wZsp4l0_2HQZIN0jsGNhKlGcVFU1lclTcTvmxWHWEjMs049LFIwkw_N-WXkxTifhmFRzdT5cY9hCEvSw-Mxxt0qHMSu-qF7LoUbOT9Y/s1600/Screen+Shot+2019-12-16+at+6.46.49+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="1024" data-original-width="1600" height="408" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqX6zcBNqwRjVUf4PkYy7xBGJOThKzS1m8l9_wZsp4l0_2HQZIN0jsGNhKlGcVFU1lclTcTvmxWHWEjMs049LFIwkw_N-WXkxTifhmFRzdT5cY9hCEvSw-Mxxt0qHMSu-qF7LoUbOT9Y/s640/Screen+Shot+2019-12-16+at+6.46.49+PM.jpg" width="640" /></a><br />
<br />
<h4 style="text-align: left;">
Problem: "$ mvn test" (Maven) doesn't execute find my unit tests.</h4>
<h4 style="text-align: left;">
Solution: Use the Surfire plugin. See it as the second plugin declared below.</h4>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvAsNghlvaA3Y6T_-D85sEiezs4EDY0qwsQYEMAS_c4FkZSH2EtUR7PpXQy0e0pUJhL3VAD0bLF_lhIe3rkAfyqv54QYLkgdo384-zMT4eNXTld-blir5k2o-SkR3CduCfZGT6am2dgd0/s1600/Screen+Shot+2019-12-18+at+5.34.27+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="526" data-original-width="1012" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvAsNghlvaA3Y6T_-D85sEiezs4EDY0qwsQYEMAS_c4FkZSH2EtUR7PpXQy0e0pUJhL3VAD0bLF_lhIe3rkAfyqv54QYLkgdo384-zMT4eNXTld-blir5k2o-SkR3CduCfZGT6am2dgd0/s640/Screen+Shot+2019-12-18+at+5.34.27+PM.jpg" width="640" /></a><br />
<br /></div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-7549859336861711122019-12-13T10:42:00.001-08:002019-12-19T06:44:09.041-08:00Getting the JDK version correct in STS4 / Eclipse2019 on MacOs<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi69fUY42PckiJ1KYv3YGBwzH3z_hrKs38ifJwaKCd3GwtX-ph6Kt5_Vpyb6ekRqp7wx413HvG-BB2I68eEsNn8XsOE9TLTDs-aJQd2CNJF1ktCDWEczPj0xyAEhqlqxfPJ8fuhDaxWoIs/s1600/java-spring-logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="446" data-original-width="851" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi69fUY42PckiJ1KYv3YGBwzH3z_hrKs38ifJwaKCd3GwtX-ph6Kt5_Vpyb6ekRqp7wx413HvG-BB2I68eEsNn8XsOE9TLTDs-aJQd2CNJF1ktCDWEczPj0xyAEhqlqxfPJ8fuhDaxWoIs/s320/java-spring-logo.png" width="320" /></a></div>
After installing the latest STS (version 4 and built on Eclipse 2019) and JDK13, I made a new project and had some difficulty in directing STS how to find the new JDK. The following is the "new Java project" wizard.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOmqdb7uJejDhGlOucnA4ych8tUgKSDp28FYsDlZOPhpGLV_iwKOTFQZXvFTLzfPP4a1nPfQSsYDy_r3wOiGQfjti1xWnMnuXwLEjlJ3CAjK2DG6dVgVV9w39qiMI3itXZMBTFMuOduIM/s1600/Screen+Shot+2019-12-13+at+10.58.09+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1450" data-original-width="1542" height="600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOmqdb7uJejDhGlOucnA4ych8tUgKSDp28FYsDlZOPhpGLV_iwKOTFQZXvFTLzfPP4a1nPfQSsYDy_r3wOiGQfjti1xWnMnuXwLEjlJ3CAjK2DG6dVgVV9w39qiMI3itXZMBTFMuOduIM/s640/Screen+Shot+2019-12-13+at+10.58.09+AM.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmdNLx7Ygn8OtyYqQnMUso3q4CT3fgTvg7o8pWYWKZZ-MLs06qw72Ei3vACHJEjxwN9FpYgEMOHueYe76JJAhbR787Aa3LVc9N2e2uDeKH4O-IJuAJ6Q1DgYv1JLeq_Qar_NRbXmrqSNc/s1600/Screen+Shot+2019-12-13+at+10.57.49+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1058" data-original-width="1078" height="628" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmdNLx7Ygn8OtyYqQnMUso3q4CT3fgTvg7o8pWYWKZZ-MLs06qw72Ei3vACHJEjxwN9FpYgEMOHueYe76JJAhbR787Aa3LVc9N2e2uDeKH4O-IJuAJ6Q1DgYv1JLeq_Qar_NRbXmrqSNc/s640/Screen+Shot+2019-12-13+at+10.57.49+AM.png" width="640" /></a></div>
<br />
But the problem is, where is the JRE_HOME directory? Over the past two decades, where MacOS puts the JDK has evolved. Then I discovered that Java on MacOS installs with a command to find it!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ5d07TJrF6Z5JsAB-Qf00RWFvQltLY9ONxgUkXiik0k5MpRSml29K7obTSIQWxnE5B2Y0L_8tVPTIQkwEOaeK9u6cBti6RmvxsF9QimbzD-I6Y9IwtFaztd-d6x4iRSmmos7EfGwdjl0/s1600/Screen+Shot+2019-12-13+at+10.36.17+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="258" data-original-width="928" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ5d07TJrF6Z5JsAB-Qf00RWFvQltLY9ONxgUkXiik0k5MpRSml29K7obTSIQWxnE5B2Y0L_8tVPTIQkwEOaeK9u6cBti6RmvxsF9QimbzD-I6Y9IwtFaztd-d6x4iRSmmos7EfGwdjl0/s640/Screen+Shot+2019-12-13+at+10.36.17+AM.jpg" width="640" /></a></div>
<br />
The java_home command also takes a "-v" argument to specify what version of Java you seek.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUjYxYug21zxUiEftQGAyk_a0Bk503oode28F8DZYJ_R_T3P6eaxmkxavXgLq7wKYdArfBeHIFwfqIK6YnO2H0g6iJYuKVdHTUkQ5qP7-XvIwVcksQ7Bxf7uWQyzhYQ4rhdYj-PYrU3h0/s1600/many+command+calls.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="1042" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUjYxYug21zxUiEftQGAyk_a0Bk503oode28F8DZYJ_R_T3P6eaxmkxavXgLq7wKYdArfBeHIFwfqIK6YnO2H0g6iJYuKVdHTUkQ5qP7-XvIwVcksQ7Bxf7uWQyzhYQ4rhdYj-PYrU3h0/s640/many+command+calls.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Using this command tells you what directory your JDK is and you are off and coding!</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9SS09hdPUuwKEv8BJYfv7ZAQBF92o0gDx28hzFHnhWcNAq43MDg1Ci1LWegyZDiXlyy8GD1julKbwmjjsIrnC_hkEc5c9-Nchp0cbYH7_qj5DOl1sbnpWmDMR0MPNxdb-bfnB53It27Y/s1600/Screen+Shot+2019-12-13+at+10.17.31+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1048" data-original-width="1036" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9SS09hdPUuwKEv8BJYfv7ZAQBF92o0gDx28hzFHnhWcNAq43MDg1Ci1LWegyZDiXlyy8GD1julKbwmjjsIrnC_hkEc5c9-Nchp0cbYH7_qj5DOl1sbnpWmDMR0MPNxdb-bfnB53It27Y/s640/Screen+Shot+2019-12-13+at+10.17.31+AM.jpg" width="632" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilnPe7Gm_vzl0C3r19q0YwN-zo6OtHHpklXYUOp0_YJnB1fltk8MKKeTfjFByMkgKxcAd40Gbiwi41XqhGzaTqgvD9idGBEOP5SjeKJmXGb00kVpe3_r3uc_5PCKnWusB4JG0UOPxvw20/s1600/Screen+Shot+2019-12-13+at+10.17.47+AM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1094" data-original-width="1408" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilnPe7Gm_vzl0C3r19q0YwN-zo6OtHHpklXYUOp0_YJnB1fltk8MKKeTfjFByMkgKxcAd40Gbiwi41XqhGzaTqgvD9idGBEOP5SjeKJmXGb00kVpe3_r3uc_5PCKnWusB4JG0UOPxvw20/s640/Screen+Shot+2019-12-13+at+10.17.47+AM.jpg" width="640" /></a></div>
<br />
<h2 style="text-align: left;">
By the way: learn more about TDD, micro testing, macro testing</h2>
Check out the <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/">Agile Thoughts podcast</a>. This podcast gets the Confessions Of An Agile Coach's endorsement (naturally, since the same people produce it) of quality materials for developers and teams trying to get coding done in a way that avoids dealing with legacy issues such as bugs and hard to maintain code. Agile Thoughts has a lot of great TDD conceptual content along with a radio drama about the difficulties to getting a TDD initiative started. <b>Click the podcast cover below or <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" target="_blank">here</a> for more information.</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Agile Thoughts podcast" border="0" data-original-height="1024" data-original-width="1024" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaaNk_KY2gesIbqVamAc8uqQuQLm3Y5hG59YEAGFyAdcyX1z73ADGxBVP6y66irUWgY90Vm5C5gDfq1C2YlME-gfsSR-Yv9nGq8pCn0oX6y4K3-mvzpfpkOZTIwAdHcBydjSXDbFw1kPg/s400/UNADJUSTEDNONRAW_thumb_39.jpg" title="" width="400" /></a></div>
<h2 style="text-align: left;">
References:</h2>
<div>
java_home command: <a href="https://docs.oracle.com/javase/9/install/installation-jdk-and-jre-macos.htm#JSJIG-GUID-7EB4F697-F3D1-40EA-ACDF-07FA90F02D57">https://docs.oracle.com/javase/9/install/installation-jdk-and-jre-macos.htm#JSJIG-GUID-7EB4F697-F3D1-40EA-ACDF-07FA90F02D57</a></div>
<div>
<br /></div>
<div>
<br /></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-54747825009924798102019-10-03T15:01:00.001-07:002019-10-17T14:30:15.070-07:00Remote desktop via VNC from MacOS to EC2 Ubuntu 18 with XFCE<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSxvFHB0cpqhCpy1L_LlRJrpm78hq990FUdU1wFYbtrlS9zSUasa6IHmAt8CdYEFPTfiPSd3_4L0VN-ep96JeO2PLxdNQBlRkpl5pncWJk0j-9rZD7VjwJB8kuKqc6OcDcs7n66jb7dJQ/s1600/EC2+VNC+Mac2.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="255" data-original-width="720" height="141" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSxvFHB0cpqhCpy1L_LlRJrpm78hq990FUdU1wFYbtrlS9zSUasa6IHmAt8CdYEFPTfiPSd3_4L0VN-ep96JeO2PLxdNQBlRkpl5pncWJk0j-9rZD7VjwJB8kuKqc6OcDcs7n66jb7dJQ/s400/EC2+VNC+Mac2.jpg" width="400" /></a></div>
As a technical coach, I teach live online courses on various topics (TDD, ATDD, BDD, full stack Agile) and my tool of choice is EC2 instances.<br />
<a href="https://youtu.be/ljvgwmJCUjw" target="_blank">This video</a> I found pretty much tells the story and got me up and running but I wanted a document to store my learnings for future reference. Below are the steps, and I wanted to use a more enjoyable minimal window manager (Xfce) than Gnome (Gnome is pretty odd to work with for the uninitiated where Xfce is intuitive, especially for MacOS users). See the below side-by-side comparison and judge for yourself.<br />
Note: If you find the below pictures difficult to read, selecting them will bring them into full size view.<br />
<br />
<h2 style="text-align: left;">
Create and Provision your EC2 Instance</h2>
Go into AWS and create an Ubuntu 18 (or even newer). Older versions of Ubuntu are tricky to get working with a desktop manager (at least 14 had a lot of threads generated in forums of people trying to get this to work).<br />
<h3 style="text-align: left;">
GUI Software Stack</h3>
These are the parts we need: VNC, a window manager, and a desktop manager.<br />
<br />
There's another piece called a Display Manager which handles user login, but as we aren't exporting X11 directly, we don't need this. VNC will handle our password, which means the security is reduced somewhat (the hacker only needs to get one thing correct rather than two (username & password). But for my purpose of creating temporary dev environments, it's good enough.<br />
<br />
So SSH into your instance and install the below:<br />
<blockquote class="tr_bq">
$ssh -i path_to_your_pub_priv_key <your and="" aws="" key="" pair="" private="" public="">ubuntu@some_ip_address<your address="" instance="" public="" s=""></your></your></blockquote>
<blockquote class="tr_bq">
$sudo apt install vnc4server</blockquote>
<blockquote class="tr_bq">
$sudo apt install xfce4 </blockquote>
<blockquote class="tr_bq">
$sudo apt install xfce4-terminal</blockquote>
What these are: vnc4server is how we'll connect from our Mac to the remote linux instance. xfce4 will bring a window manager and a set of default applications (web browser, LibreOffice,...). xfce-terminal will give us a terminal that will work with our wind-manager.<br />
<br />
To make your ubuntu desktop happy create the following directories:<br />
<ul style="text-align: left;">
<li>mkdir $HOME/Desktop </li>
</ul>
Next, let's get VNC ready to go. Setup the password for VNC:<br />
<blockquote class="tr_bq">
$ vncserver </blockquote>
Kill the server for now:<br />
<blockquote class="tr_bq">
$ vncserver -kill :1</blockquote>
The world of VNC is a little different than the world of X11 (if you are familiar with that). A "xstartup" file is executed by the VNC server to get the windowing and desktop setup for the user who logs into the VNC. Go into $HOME/.vnc, and replace what's in xstartup with the following:<br />
<blockquote class="tr_bq">
#!/bin/bash<br />
xrdb $HOME/.Xresources<br />
startxfce4 & <br />
vncconfig -nowin &</blockquote>
These commands do the following:<br />
<br />
<ul style="text-align: left;">
<li>xrdb : give X a location to store property settings.</li>
<li>startxfce4 : starts the xfce session</li>
<li>vncconfig : adds copy/paste support. I found it's solid going from VNC session to host, in my setup going from host to VNC session, I had to "slow down" and hold down the copy-paste button a bit longer or it wouldn't take.</li>
</ul>
<br />
Tidy up things by doing the following:<br />
<blockquote class="tr_bq">
$chmod 755 xstartup<br />
$touch $HOME/.Xresources </blockquote>
restart VNC:<br />
<blockquote class="tr_bq">
$ vncserver </blockquote>
Do "ps -eaf | grep vnc" to see what display port you will be using (it's usually ":1"):<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA5mIZXHzPEf0JQJz7F4dwDfkGUGfNhQXtdvRuGuZoa7bRuJUZeLBlvIK5lcXimZnvqB1IsaWTqBmKlb9pYg0wW7DyV7dRjuxmmSAvb_WbjhQrIwvECm0QqkprhQbUutfWrxligHdquE/s1600/Screen+Shot+2019-09-27+at+6.33.53+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="496" data-original-width="974" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA5mIZXHzPEf0JQJz7F4dwDfkGUGfNhQXtdvRuGuZoa7bRuJUZeLBlvIK5lcXimZnvqB1IsaWTqBmKlb9pYg0wW7DyV7dRjuxmmSAvb_WbjhQrIwvECm0QqkprhQbUutfWrxligHdquE/s640/Screen+Shot+2019-09-27+at+6.33.53+PM.jpg" width="640" /></a><br />
<br />
In the above, I've opened two display ports, ":1" and ":2". If you ssh again and run vncserver for that session, it'll allocate get the next free display port. More about display ports mapping to network servers will be covered later in this document but this general idea is enough to get you going.<br />
Do a "ps -eaf | grep xfce" and you'll see that upon startup, the server executed your user's xstartup.<br />
<br />
If you reboot the EC2 instance, your VNC server will stop. If you want VNC to always be active, you'll need to setup a "service." There is a link with instructions at the end of this article.<br />
<h2 style="text-align: left;">
Connecting to your Instance</h2>
You can't yet connect directly to your instance as it's walled behind a virtual private cloud that is, by default, refusing all connections. Two typical ways to get to your server are using SSH to create a tunnel to your server, which is great for ec2 instances you really don't want to allow connections from outside their virtual private cloud (VPC). The other way is to change the security group so the VPC allows inbound connections so that you can frequently and conveniently connect to it with minimal fuss.<br />
A big problem with X11 is that it doesn't communicate using a secure channel unless you use the SSH tunnel. So bear in mind that unless you're using an SSH tunnel, all those words your passing back and forth with your X client will be traveling un-encoded. It's not clear to me that VNC4Server makes things any more secure.<br />
<h3 style="text-align: left;">
Using an SSH Tunnel</h3>
<div>
Remember the display port discovered by running "ps -eaf | grep vnc"? If it's a one, then that maps to network port 5901. A display port of 2 will map to network port 5902, and so on.</div>
<div>
<br /></div>
$ ssh -L 5902:localhost:5902 -i -path_to_your_pub_priv_key ubuntu@ip_address_here<br />
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Then launch MacOS's Screen Sharing app (go to spotlight and enter "screen sharing"), enter in "localhost:5902" which is <b>your side</b> of the SSH tunnel.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16s4m0K1yZrw71BNLZL_xg93P6qwXdSuLlglZPu8g21e5Sf8XtgUPmqvNIE4hSepv1u7it14ZJlv05SHb0JF3f-0vjOO9wV7n_X3BSgK6pegjUWijl4igDv-_du_ubasl6Ux62yyXN28/s1600/Screen+Shot+2019-09-27+at+6.17.35+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="260" data-original-width="866" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16s4m0K1yZrw71BNLZL_xg93P6qwXdSuLlglZPu8g21e5Sf8XtgUPmqvNIE4hSepv1u7it14ZJlv05SHb0JF3f-0vjOO9wV7n_X3BSgK6pegjUWijl4igDv-_du_ubasl6Ux62yyXN28/s320/Screen+Shot+2019-09-27+at+6.17.35+PM.png" width="320" /></a></div>
You'll be prompted for your <b>VNC password</b> which you setup on the server when you ran "vnc4server" the first time.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSUMYQIqdTRIZidiQCSBtb-nxrY_0wwTc10Dl6REhB3u_3NNq0aBp4kDc1gdRXu8aw4fzh8hgjycyzrAYeoemX0qj0ToTo9YAOKRKbt8PJvbOwWIGwDsISN7QRp0W_SifG3I8JzLebrYM/s1600/Screen+Shot+2019-09-27+at+6.15.55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="382" data-original-width="858" height="142" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSUMYQIqdTRIZidiQCSBtb-nxrY_0wwTc10Dl6REhB3u_3NNq0aBp4kDc1gdRXu8aw4fzh8hgjycyzrAYeoemX0qj0ToTo9YAOKRKbt8PJvbOwWIGwDsISN7QRp0W_SifG3I8JzLebrYM/s320/Screen+Shot+2019-09-27+at+6.15.55+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: left;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB6APAiJaQlLDCfS9_Hv2MgvYweZNAL8lRZBFoXsZKuqPSNm6A15zwAy053R_b94coljHe2VkRUIF3Z5sONd0qPEZUgvwdmZCeLnu82ba_rZT_ip42H1TfCjqA8uXyjXykTloysZ0zIzw/s1600/Screen+Shot+2019-10-07+at+2.21.28+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1207" data-original-width="1600" height="301" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB6APAiJaQlLDCfS9_Hv2MgvYweZNAL8lRZBFoXsZKuqPSNm6A15zwAy053R_b94coljHe2VkRUIF3Z5sONd0qPEZUgvwdmZCeLnu82ba_rZT_ip42H1TfCjqA8uXyjXykTloysZ0zIzw/s400/Screen+Shot+2019-10-07+at+2.21.28+PM.jpg" width="400" /></a></div>
<br /></div>
<h3 style="text-align: left;">
Adjusting the Security Group to allow Direct Connect</h3>
<div>
If you aren't operating with sensitive information and don't want too be hassled by creating a SSH tunnel every time you want to connect to this instance, then changing the VPC security group thusly will give you access to your EC2 instance.<br />
Refer to your instance's meta-information to get it's security group:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWmelGl-9cNc93WXGi6yrb8wuG14BBk7GHo76Z5SpKpevMJdT_CMmfZYPPHeqwiqx7TGoDQz-xAotUWeSO9XzpLQ1i7W8KGFEL9D2cYji97JCSVKAvrLtzK56hhkgda5kHOPU6BJd1_-g/s1600/Screen+Shot+2019-09-27+at+6.28.36+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="747" data-original-width="1600" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWmelGl-9cNc93WXGi6yrb8wuG14BBk7GHo76Z5SpKpevMJdT_CMmfZYPPHeqwiqx7TGoDQz-xAotUWeSO9XzpLQ1i7W8KGFEL9D2cYji97JCSVKAvrLtzK56hhkgda5kHOPU6BJd1_-g/s640/Screen+Shot+2019-09-27+at+6.28.36+PM.jpg" width="640" /></a></div>
Then open the security group and add an inbound rule:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghgfvGkHelAol59wz_IOfOFxbQ6CFdVTepcyBlS_8UCTJ8kSGROA0bavdBK0QvQieZ2dMpWkGPdewFsN2Z4qZPu2nmci4e4MuRk2uJXmsVxj_vf6zYsOMbpV66jo2GDF-pXsxpbQuU7Z4/s1600/Screen+Shot+2019-09-27+at+6.29.30+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="498" data-original-width="1600" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghgfvGkHelAol59wz_IOfOFxbQ6CFdVTepcyBlS_8UCTJ8kSGROA0bavdBK0QvQieZ2dMpWkGPdewFsN2Z4qZPu2nmci4e4MuRk2uJXmsVxj_vf6zYsOMbpV66jo2GDF-pXsxpbQuU7Z4/s640/Screen+Shot+2019-09-27+at+6.29.30+PM.jpg" width="640" /></a></div>
<h3 style="text-align: left;">
More about Display Ports mapping to Network Ports</h3>
VNC servers typically use the range of 5800 to 5899 or 5900 to 5999, depending on configuration. The server will use port 5800 (or 5900) for internal reasons and any new displays added will be incremented from 5800 or 5900. VNC4 uses 5900 and so the first time I launch VNC and left it running, it got display 1 (5901). The next connection I created with my SSH tunnel (if you did that) got display 2, or port 5902. You can see what displays/ports are being used by inspecting your system with the "ps" command.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA5mIZXHzPEf0JQJz7F4dwDfkGUGfNhQXtdvRuGuZoa7bRuJUZeLBlvIK5lcXimZnvqB1IsaWTqBmKlb9pYg0wW7DyV7dRjuxmmSAvb_WbjhQrIwvECm0QqkprhQbUutfWrxligHdquE/s1600/Screen+Shot+2019-09-27+at+6.33.53+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="496" data-original-width="974" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA5mIZXHzPEf0JQJz7F4dwDfkGUGfNhQXtdvRuGuZoa7bRuJUZeLBlvIK5lcXimZnvqB1IsaWTqBmKlb9pYg0wW7DyV7dRjuxmmSAvb_WbjhQrIwvECm0QqkprhQbUutfWrxligHdquE/s640/Screen+Shot+2019-09-27+at+6.33.53+PM.jpg" width="640" /></a></div>
<br />
Remember, when changing your security groups and if you plan to keep the VNC running all the time, you'll only be protected by a password, not even a <b>username</b> and password. You'll really be much safer from attack if you adjust the inbound rule to accept the source of only a specific IP or set of IPs rather than 0.0.0.0/0 which means from any IP address on the internet.</div>
<div>
<h2>
For Further Study</h2>
</div>
<div>
To learn more about VNC configuration: https://www.stuartellis.name/articles/vnc-on-linux/#understanding-vnc<br />
Create a .vncrc to preset screen geometry (resolution) and .... : https://stackoverflow.com/questions/26489895/how-do-i-change-the-vncserver-default-geometry </div>
<div>
To make the VNC server run as a service: https://www.binarytides.com/remote-ubuntu-desktop-via-vnc/</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-14691427798882610972019-10-01T20:30:00.004-07:002019-10-07T14:36:05.177-07:00Remote desktop via VNC from MacOS to EC2 Ubuntu 18<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSxvFHB0cpqhCpy1L_LlRJrpm78hq990FUdU1wFYbtrlS9zSUasa6IHmAt8CdYEFPTfiPSd3_4L0VN-ep96JeO2PLxdNQBlRkpl5pncWJk0j-9rZD7VjwJB8kuKqc6OcDcs7n66jb7dJQ/s1600/EC2+VNC+Mac2.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="255" data-original-width="720" height="141" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSxvFHB0cpqhCpy1L_LlRJrpm78hq990FUdU1wFYbtrlS9zSUasa6IHmAt8CdYEFPTfiPSd3_4L0VN-ep96JeO2PLxdNQBlRkpl5pncWJk0j-9rZD7VjwJB8kuKqc6OcDcs7n66jb7dJQ/s400/EC2+VNC+Mac2.jpg" width="400" /></a></div>
As a technical coach, I teach live online courses on various topics (TDD, ATDD, BDD, full stack Agile) and my tool of choice is EC2 instances.<br />
<a href="https://youtu.be/ljvgwmJCUjw" target="_blank">This video</a> I found pretty much tells the story and got me up and running but I wanted a document to store my learnings for future reference. Below are the steps. If you find the images difficult to read, you can click them to bring them into full size view.<br />
<h2 style="text-align: left;">
Create and Provision your EC2 Instance</h2>
Go into AWS and create an Ubuntu 18 (or even newer). Older versions of Ubuntu are tricky to get working with a desktop manager (at least 14 had a lot of threads generated in forums of people trying to get this to work).<br />
<h3 style="text-align: left;">
GUI Software Stack</h3>
These are the parts we need: VNC, a window manager, and a desktop manager. <br />
<br />
There's another piece called a Display Manager which handles user login, but as we aren't exporting X11 directly, we don't need this. VNC will handle our password, which means the security is reduced somewhat (the hacker only needs to get one thing correct rather than two (username & password). But for my purpose of creating temporary dev environments, it's good enough.<br />
<br />
SSH into your instance and install the below:<br />
<blockquote class="tr_bq">
$ssh -i path_to_private_public_key <your and="" aws="" key="" pair="" private="" public=""> ubuntu@<your address="" instance="" public="" s=""></your></your></blockquote>
<blockquote class="tr_bq">
$sudo apt-get install ubuntu-desktop<br />
$sudo apt-get install vnc4server<br />
$sudo apt-get install gnome-panel</blockquote>
To make your ubuntu desktop happy create the following directories:<br />
<br />
<ul style="text-align: left;">
<li>mkdir $HOME/Desktop </li>
</ul>
<br />
Next, let's get VNC ready to go. Setup the password for VNC:<br />
<blockquote class="tr_bq">
$ vncserver </blockquote>
Kill the server so and adjust it's configuration files:<br />
<blockquote class="tr_bq">
$ vncserver -kill :1<br />
$ vi $HOME/.vnc/xstartup</blockquote>
Modify xstartup thusly:<br />
<br />
<br />
<li>unset SESSION_MANAGER</li>
<li>after "<span style="background-color: #f9f9f9; color: #0d0d0d; font-family: "roboto" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;"> # exec /etc/X11/xinit/xinitrc" add the following two lines:</span></li>
<br />
<blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;">
<br />
<br />
<br />
<li style="text-align: left;"><span style="background-color: #f9f9f9; caret-color: rgb(13, 13, 13); font-size: 14px; white-space: pre-wrap;"><span style="color: #0d0d0d; font-family: "roboto" , "arial" , sans-serif;"> gnome-session –session=gnome-classic &</span></span> </li>
</blockquote>
<blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;">
<br />
<br />
<br />
<li style="text-align: left;"><span style="background-color: #f9f9f9; caret-color: rgb(13, 13, 13); font-size: 14px; white-space: pre-wrap;"><span style="color: #0d0d0d; font-family: "roboto" , "arial" , sans-serif;"> gnome-panel &</span></span></li>
</blockquote>
<br />
gnome-session is the <a href="https://linux.die.net/man/1/gnome-session" target="_blank">gnome desktop manager</a>. gnome-panel is the <a href="https://en.wikipedia.org/wiki/GNOME_Panel" target="_blank">bar that contains buttons for selecting desktop applications</a>.<br />
<br />
xstartup must be executable. If you made your own, you'll need to do this:<br />
$ chmod 744 xstartup<br />
<br />
restart VNC:<br />
<blockquote class="tr_bq">
$ vncserver </blockquote>
(If you reboot the system, your VNC server will stop. If you want it to be always on, you'll need to work with the "service" to get that setup)<br />
<h2 style="text-align: left;">
Connecting to your Instance</h2>
You can't yet connect directly to your instance as it's walled behind a virtual private cloud that is, by default, refusing all connections. Two typical ways to get to your server are using SSH to create a tunnel to your server, which is great for ec2 instances you really don't want to allow connections from outside their virtual private cloud (VPC). The other way is to change the security group so the VPC allows inbound connections so that you can frequently and conveniently connect to it with minimal fuss. <br />
A big problem with X11 is that it doesn't communicate using a secure channel unless you use the SSH tunnel. So bear in mind that unless you're using an SSH tunnel, all those words your passing back and forth with your X client will be traveling un-encoded. It's not clear to me that VNC4Server makes things any more secure.<br />
<h3 style="text-align: left;">
Using an SSH Tunnel</h3>
$ ssh -L 5902:localhost:5902 -i <private amp="" file="" key="" public=""> ubuntu@<public ip=""></public></private><br />
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Then launch MacOS's Screen Sharing app (go to spotlight and enter "screen sharing"), enter in "localhost:5902" which is <b>your side</b> of the SSH tunnel.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16s4m0K1yZrw71BNLZL_xg93P6qwXdSuLlglZPu8g21e5Sf8XtgUPmqvNIE4hSepv1u7it14ZJlv05SHb0JF3f-0vjOO9wV7n_X3BSgK6pegjUWijl4igDv-_du_ubasl6Ux62yyXN28/s1600/Screen+Shot+2019-09-27+at+6.17.35+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="260" data-original-width="866" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16s4m0K1yZrw71BNLZL_xg93P6qwXdSuLlglZPu8g21e5Sf8XtgUPmqvNIE4hSepv1u7it14ZJlv05SHb0JF3f-0vjOO9wV7n_X3BSgK6pegjUWijl4igDv-_du_ubasl6Ux62yyXN28/s320/Screen+Shot+2019-09-27+at+6.17.35+PM.png" width="320" /></a></div>
You'll be prompted for your <b>VNC password</b> which you setup on the server when you ran "vnc4server" the first time.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSUMYQIqdTRIZidiQCSBtb-nxrY_0wwTc10Dl6REhB3u_3NNq0aBp4kDc1gdRXu8aw4fzh8hgjycyzrAYeoemX0qj0ToTo9YAOKRKbt8PJvbOwWIGwDsISN7QRp0W_SifG3I8JzLebrYM/s1600/Screen+Shot+2019-09-27+at+6.15.55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="382" data-original-width="858" height="142" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSUMYQIqdTRIZidiQCSBtb-nxrY_0wwTc10Dl6REhB3u_3NNq0aBp4kDc1gdRXu8aw4fzh8hgjycyzrAYeoemX0qj0ToTo9YAOKRKbt8PJvbOwWIGwDsISN7QRp0W_SifG3I8JzLebrYM/s320/Screen+Shot+2019-09-27+at+6.15.55+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrGh5i7HlE1VYPMiS6uy7tNdAJjZg1bxULVDHIGrnwJVMqWPpfBE3F5l2N_Ef-YplmXkizLIM2Hrb4t-rOsFRyvQleEH0Jqm1cO7h1hn1OEjdEJKDYz2YLPRNVpEDaUyS_lniIwBujT5I/s1600/Screen+Shot+2019-09-27+at+6.21.02+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="672" data-original-width="1600" height="267" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrGh5i7HlE1VYPMiS6uy7tNdAJjZg1bxULVDHIGrnwJVMqWPpfBE3F5l2N_Ef-YplmXkizLIM2Hrb4t-rOsFRyvQleEH0Jqm1cO7h1hn1OEjdEJKDYz2YLPRNVpEDaUyS_lniIwBujT5I/s640/Screen+Shot+2019-09-27+at+6.21.02+PM.jpg" width="640" /></a></div>
<div style="text-align: left;">
<br /></div>
<h3 style="text-align: left;">
Direct Connect after adjusting the Security Group</h3>
<div>
If you aren't operating with sensitive information and don't want too be hassled by creating a SSH tunnel every time you want to connect to this instance, then changing the VPC security group thusly will give you access to your EC2 instance.<br />
Refer to your instance's meta-information to get it's security group:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWmelGl-9cNc93WXGi6yrb8wuG14BBk7GHo76Z5SpKpevMJdT_CMmfZYPPHeqwiqx7TGoDQz-xAotUWeSO9XzpLQ1i7W8KGFEL9D2cYji97JCSVKAvrLtzK56hhkgda5kHOPU6BJd1_-g/s1600/Screen+Shot+2019-09-27+at+6.28.36+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="747" data-original-width="1600" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWmelGl-9cNc93WXGi6yrb8wuG14BBk7GHo76Z5SpKpevMJdT_CMmfZYPPHeqwiqx7TGoDQz-xAotUWeSO9XzpLQ1i7W8KGFEL9D2cYji97JCSVKAvrLtzK56hhkgda5kHOPU6BJd1_-g/s640/Screen+Shot+2019-09-27+at+6.28.36+PM.jpg" width="640" /></a></div>
Then open the security group and add an inbound rule:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghgfvGkHelAol59wz_IOfOFxbQ6CFdVTepcyBlS_8UCTJ8kSGROA0bavdBK0QvQieZ2dMpWkGPdewFsN2Z4qZPu2nmci4e4MuRk2uJXmsVxj_vf6zYsOMbpV66jo2GDF-pXsxpbQuU7Z4/s1600/Screen+Shot+2019-09-27+at+6.29.30+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="498" data-original-width="1600" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghgfvGkHelAol59wz_IOfOFxbQ6CFdVTepcyBlS_8UCTJ8kSGROA0bavdBK0QvQieZ2dMpWkGPdewFsN2Z4qZPu2nmci4e4MuRk2uJXmsVxj_vf6zYsOMbpV66jo2GDF-pXsxpbQuU7Z4/s640/Screen+Shot+2019-09-27+at+6.29.30+PM.jpg" width="640" /></a></div>
VNC servers typically need the range of 5800 to 5899 or 5900 to 5999, depending on configuration. The server will use port 5800 for internal reasons and any new displays added will be incremented from 5800 or 5900. VNC4 uses 5900 and so the first time I launch VNC and left it running, it got display 1 (5901). The next connection I created with my SSH tunnel (if you did that) got display 2, or port 5902. You can see what displays/ports are being used by inspecting your system with the "ps" command.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA5mIZXHzPEf0JQJz7F4dwDfkGUGfNhQXtdvRuGuZoa7bRuJUZeLBlvIK5lcXimZnvqB1IsaWTqBmKlb9pYg0wW7DyV7dRjuxmmSAvb_WbjhQrIwvECm0QqkprhQbUutfWrxligHdquE/s1600/Screen+Shot+2019-09-27+at+6.33.53+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="496" data-original-width="974" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA5mIZXHzPEf0JQJz7F4dwDfkGUGfNhQXtdvRuGuZoa7bRuJUZeLBlvIK5lcXimZnvqB1IsaWTqBmKlb9pYg0wW7DyV7dRjuxmmSAvb_WbjhQrIwvECm0QqkprhQbUutfWrxligHdquE/s640/Screen+Shot+2019-09-27+at+6.33.53+PM.jpg" width="640" /></a></div>
<br />
Remember, when changing your security groups and if you plan to keep the VNC running all the time, you'll only be protected by a password, not even a username and password. You could adjust the inbound rule to accept the source of only a specific IP rather than 0.0.0.0/0 which means from any IP address on the internet.</div>
<div>
<br /></div>
<div>
<br /></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com1tag:blogger.com,1999:blog-284399302764909932.post-18063176638613908042019-08-09T14:38:00.001-07:002019-09-27T17:17:05.840-07:00Orchestration: What it is and its Importance to DevOps<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhULieZDse8DwbXO6nP5oG5w1l0NwFJnY1YxCJYbBvW5nYoVna8Blchw2DXDBvBVMndz35WMccgp0E_XHH6PrOxClZ2JBkShpkV5_hkX7uww0PVtYwyVDpn-ig4iARtPB1TeVz0AhU9mQA/s1600/maxresdefault.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1280" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhULieZDse8DwbXO6nP5oG5w1l0NwFJnY1YxCJYbBvW5nYoVna8Blchw2DXDBvBVMndz35WMccgp0E_XHH6PrOxClZ2JBkShpkV5_hkX7uww0PVtYwyVDpn-ig4iARtPB1TeVz0AhU9mQA/s400/maxresdefault.jpg" width="400" /></a></div>
A symphony, unlike a jazz band, requires orchestration because it has even greater specialization with expertise in using different musical instruments. (It's been said that, "the violinist section thinks that the french horn section are a bunch of cowboys.") DevOps in an enterprise is in a similar situation (developers, sysadmins, network ops). Each group has difference skill sets and tools. To add to the complexity, within each group there is a variance in tool preference.<br />
<h2 style="text-align: left;">
Working together effectively</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlhn2aj_XHj0yWYSO0_x1t8td0cRNe86bYhRKFXuAQJObZ90oMpQc0jwK5dQCYLL237_WtZS_3sUQN5ru0N2bz54fSyAEGdB_P2hL2ImNMKkdQfr8U8lS5leIcBhkJ8c_6RMRDiKKo9yA/s1600/51q8ZBVvv4L_1024x1024%25402x.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="352" data-original-width="500" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlhn2aj_XHj0yWYSO0_x1t8td0cRNe86bYhRKFXuAQJObZ90oMpQc0jwK5dQCYLL237_WtZS_3sUQN5ru0N2bz54fSyAEGdB_P2hL2ImNMKkdQfr8U8lS5leIcBhkJ8c_6RMRDiKKo9yA/s320/51q8ZBVvv4L_1024x1024%25402x.jpg" width="320" /></a></div>
Standardizing the toolset across groups (let's all play tuba) isn't really practical. The people doing the work are effective with the tools they know. While there can be movement *within* groups toward some standards (the brass section settles on trumpet), selecting a single tool *across* these groups is likely to fail as some tools are better suited for certain contexts than others.<br />
<br />
For example, we can't just all use spreadsheets to solve our problems as they don't address the ability to configure the networking domain. Another example is that it's not practical that we all become software developers as the cost is high to build new network management products when there are already apps that address some of our needs. Some tools are well suited to solving one, targeted problem, and it'd be a pity to not use it in those situations.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE50MbRnMH_dnS5cZ6lHBvc-S_KW7YJ4sZrXKl85zW8_jHAK8lHr9dAbKdhsAq-S4vRS1YHtI7meLbmQ9SRkLkcZeQ7QUOchQgh9h_tiifF_llLmSrNDB8vwA3D3qgEkShfRYPyVkVWPY/s1600/flexiblity+versus+specific+and+roles.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="410" data-original-width="857" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE50MbRnMH_dnS5cZ6lHBvc-S_KW7YJ4sZrXKl85zW8_jHAK8lHr9dAbKdhsAq-S4vRS1YHtI7meLbmQ9SRkLkcZeQ7QUOchQgh9h_tiifF_llLmSrNDB8vwA3D3qgEkShfRYPyVkVWPY/s640/flexiblity+versus+specific+and+roles.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The sweet spot for devops is in script automation. In this realm, developer practices such as the use of source control can be used to create "<a href="https://medium.com/the-mighty-programmer/code-communication-ac4a8a4a4f9a" target="_blank">communication through code</a>" or a "single source of truth." Most script automation is open source with broad community support and frequently there paid support is available too. Alternatively, macro recording strategies typically target a specific vendor's hardware and their configurations aren't driven from source code.<br />
<br />
Script automation without being used with orchestration is subject to the following:<br />
<ol style="text-align: left;">
<li>hardcoded values</li>
<li>applicable to only one piece of hardware</li>
<li>hard to maintain beyond the original script developer</li>
<li>once there are hundreds of scripts with hundreds of lines, it's difficult to:</li>
</ol>
<ul style="text-align: left;"><ul>
<li>get a view to what scripts are applied to <u>what</u> hardware and </li>
<li><u>how frequently</u> (applied by schedule or continuously or ...)</li>
</ul>
</ul>
<div>
Points 1, 2, and the second bullet of 4 can be mitigated by the scripts being driven by orchestration. Points 3 and the first bullet of 4 is aided by the use of source control.</div>
<h2>
Communication through Code</h2>
<div>
Just as development teams preserve their efforts in source control so everyone on the team or organization has visibility, placing the configuration of devices into source code should:</div>
<div>
<ul style="text-align: left;">
<li>make visible what automation is affecting what production devices</li>
<li>become a system of record</li>
</ul>
<div>
That resolves the first part of the above fourth bullet.</div>
</div>
<h2 style="text-align: left;">
Orchestration</h2>
<div class="separator" style="clear: both; text-align: left;">
Wouldn't it be nice if you learned to play ONE instrument well, you could operate thousands of others? Then later, if you had to add a new one, perhaps a voxophone, everyone else who could play piano could immediately GET, at some level, how to work with the voxophone? Then later, if something were to go wrong with the voxophone, everyone knew the scope of the problem and where to start, even if they weren't the voxophone player.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This is orchestration. One of its benefits is that there is a layer of standardization across a diverse toolset, without ruining the ability to get things done with the best specific tool at hand. </div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTnE8JBQ4SujksANJeutVOmielT2tpmBmhyphenhyphenSwVXVP4BMfr7KdKUXMTidszaphUiApWLfIndojyxl_BMh6dkBq6CZ9wHtT63HN61slfvBwDXd4I3WefCUpAjdD2DRTFaiN6LqsivUVBMRU/s1600/musical-instrument-insurance-1-small.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="540" data-original-width="960" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTnE8JBQ4SujksANJeutVOmielT2tpmBmhyphenhyphenSwVXVP4BMfr7KdKUXMTidszaphUiApWLfIndojyxl_BMh6dkBq6CZ9wHtT63HN61slfvBwDXd4I3WefCUpAjdD2DRTFaiN6LqsivUVBMRU/s400/musical-instrument-insurance-1-small.jpg" width="400" /></a></div>
<br />
This ability to "normalize" interacting with a variety of automation (python, tcl, CLI, webAPIs,...) through one or a few well known tools would:<br />
<ul style="text-align: left;">
<li>simplify the effort to get a high level understanding of what's happening</li>
<li>allow others (those who have less experience with the automation script or tooling) to make orchestration adjustments to a script without requiring digging into the body of the script</li>
<li>anyone can understand <b>how</b>, <b>when</b>, and <b>what</b> (devices) is being managed</li>
<li>greater ownership, beyond the script author, of what's happening across all the tools</li>
</ul>
<h2 style="text-align: left;">
For example</h2>
<div>
One way Ansible can orchestrate scripts is through the use of the Scripts module and then Ansible facilitates execution by:</div>
<div>
<ul style="text-align: left;">
<li>transfers the script to the remote node for execution</li>
<li>passing arguments from the playbook to the script</li>
<li>executes the script in the shell environment on the remote node.</li>
</ul>
</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com2tag:blogger.com,1999:blog-284399302764909932.post-81363220324661003182019-06-17T16:49:00.008-07:002019-08-02T16:36:36.884-07:00Ansible Playbooks and Modules<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: left;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ2-gkDn7brp3KnuweIPqb2ZwMoBbenylztiWrmfkfvFbprX-9ca26Hmfq4pRe5Ex9x-VA3ppdHBhZ4VUnnVdReXoPL5_Gt5R2VG1Fc5Ep6rK5t7Zmd2Un10f2eJaeJdOszZ8EmN-S6zA/s1600/Ansible+Playbook.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ2-gkDn7brp3KnuweIPqb2ZwMoBbenylztiWrmfkfvFbprX-9ca26Hmfq4pRe5Ex9x-VA3ppdHBhZ4VUnnVdReXoPL5_Gt5R2VG1Fc5Ep6rK5t7Zmd2Un10f2eJaeJdOszZ8EmN-S6zA/s320/Ansible+Playbook.jpg" width="320" /></a></div>
Playbooks are the primary way to produce "infrastructure as code" when using Ansible. <a href="https://en.wikipedia.org/wiki/Infrastructure_as_code" target="_blank">Infrastructure as code is the aesthetic</a> of having all (or as much as you know how to) infrastructure setup, configuration changes, monitoring, rollback, and tear down represented as automated code/scripts. Code for the demos are in a <a href="https://github.com/lancerkind/ansible_getting_started/tree/playbook_tutorial" target="_blank">GitHub branch here</a>.</div>
<h2 style="text-align: left;">
Playbooks</h2>
<div>
The goal of a "play" is, like a sports team (let's say American football) to map players to their role on the field, and to an activity. In the context of IT, routers, servers, clouds,... are players. Roles are routing, firewall, blog, CMS, .... And activities could be: Update, migrate, re-route, copy file, ....</div>
<div>
<br /></div>
<div>
The IT professional writes playbooks to handle activities they care about. Playbooks are checked into source control and become the system of record. </div>
<div>
Playbooks are:</div>
<div>
<ul style="text-align: left;">
<li>are human readable</li>
<li>combines the act of writing notes to document configuration with the act of declaring configuration, and with act of confirming/testing configuration.</li>
<li>describe a policy you want your remote system to enforce</li>
<li>a set of steps in a general IT process</li>
<li>manage configuration and deployment to remote machines</li>
<li>(advanced) sequence multi tier rollouts involving rolling updates, delegate actions to other hosts, interact with monitors and load balancers</li>
</ul>
</div>
Although one could get by using ansible to "execute" scripts to multiple hosts via Ansible modules, Playbooks are a structured way to organize interaction between equipment, their roles, and activities. Playbooks are defined in a .yml file and start with "---". For each play in a playbook, you get to choose what to target in your infrastructure and what remote user executes the tasks. <br />
The metaphors connect like this: playbook -> {play -> {tasks -> modules}*}*<br />
Where a playbook may have zero, one, or many plays. And a play may have zero, one, or many tasks. A task requires a call to a module in order to affect some change.<br />
<br />
A single play is in verify-apache.yml playbook:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhLIzYYM1s4tr5drLZrVHwIftyGIVhCte-hIaypGHZrDK2dl4EtnSu4g71x3suK13k1t8qSnJslxGmPtHCaOI30-hg2R9bnbTSwGf7rulplhipC8AcTiwkUxmO73prX25wtxDs0_iBr5U/s1600/Screen+Shot+2019-06-20+at+10.09.28+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="762" data-original-width="1012" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhLIzYYM1s4tr5drLZrVHwIftyGIVhCte-hIaypGHZrDK2dl4EtnSu4g71x3suK13k1t8qSnJslxGmPtHCaOI30-hg2R9bnbTSwGf7rulplhipC8AcTiwkUxmO73prX25wtxDs0_iBr5U/s400/Screen+Shot+2019-06-20+at+10.09.28+AM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Two plays are in this playbook:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo5f2l3rKqlczmGwGfkTlS-mdsTgD4Xm5-BFJG4S9opGQrHxl8SSTLkI55agNFmKU1_ow9-Jtin0JnaFI-CZuBO5W-bUbKOcun2MO2bpx2XNzSKkIo2rlPuYncYn5EeZgYrppXX1Cq1ow/s1600/Screen+Shot+2019-06-20+at+10.11.40+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="756" data-original-width="781" height="386" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo5f2l3rKqlczmGwGfkTlS-mdsTgD4Xm5-BFJG4S9opGQrHxl8SSTLkI55agNFmKU1_ow9-Jtin0JnaFI-CZuBO5W-bUbKOcun2MO2bpx2XNzSKkIo2rlPuYncYn5EeZgYrppXX1Cq1ow/s400/Screen+Shot+2019-06-20+at+10.11.40+AM.png" width="400" /></a></div>
<br />
The above examples are just one way to declare remote_user after hosts. The <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#hosts-and-users" target="_blank">remote_user can be declared in many different contexts</a>. Here is a short document that covers <a href="https://docs.ansible.com/ansible/latest/YAMLSyntax.html" target="_blank">YAML syntax</a>.<br />
<h2 style="text-align: left;">
Modules</h2>
<div>
A task is a call to an ansible module. Here the connections to the metaphors: task -> module<br />
In Yaml, tasks are started with "-name." Modules are started on the next line.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-21w0KAaJdmB8i7H2PfssTT5JktbCpRvkaxEYB0MmuWkMSpRAxkd5MqfdrPNEZSRlJbdnOPjYehYW5sXpsmflF18CZUL544BzXshHmrUtFLqBIJ5Pff42txjTi3r9FotiCGG07me63-Y/s1600/Screen+Shot+2019-06-20+at+10.11.40+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="756" data-original-width="781" height="309" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-21w0KAaJdmB8i7H2PfssTT5JktbCpRvkaxEYB0MmuWkMSpRAxkd5MqfdrPNEZSRlJbdnOPjYehYW5sXpsmflF18CZUL544BzXshHmrUtFLqBIJ5Pff42txjTi3r9FotiCGG07me63-Y/s320/Screen+Shot+2019-06-20+at+10.11.40+AM.png" width="320" /></a></div>
<br />
The Yaml after the module name are key=value arguments, space delimited. "yum:", "template:", "service:" are references to <a href="https://docs.ansible.com/ansible/latest/modules/modules_by_category.html" target="_blank">modules</a> which are packages of features you can use with Ansible. Ansible will pass whatever is after the module, into the module as attributes and value pairs. In Play 1, "yum:" module will get the following list as arguments {name=httpd, state=latest}. The <a href="https://docs.ansible.com/ansible/latest/modules/yum_module.html?highlight=yum" target="_blank">Yum module documentation</a> explains what it does with arguments such as "name" and "state." When Yum receives "state: latest" it will check that the latest httpd is installed and if not, upgrade it.</div>
<div>
<br /></div>
<div>
<a href="https://docs.ansible.com/ansible/latest/user_guide/modules_intro.html">https://docs.ansible.com/ansible/latest/user_guide/modules_intro.html</a></div>
<div>
Modules are a way of re-using code in Ansible. They can also directly invoked from the ansible command line via "-m":</div>
<div>
<div>
$ ansible webservers -m service -a "name=httpd state=started"</div>
<div>
$ ansible webservers -m ping</div>
<div>
$ ansible webservers -m command -a "/sbin/reboot -t now"</div>
</div>
<div>
<br /></div>
<div>
The modules used above are: service, ping, and command. Here is a reboot activity done in a playbook:<br />
---<br />
- hosts: webservers</div>
<div>
<div>
- name: reboot the servers</div>
<div>
action: command /sbin/reboot -t now</div>
<div>
<br /></div>
<div>
Rather than on the basic "action" module, we can use a more specific module, "command:"</div>
<div>
<br />
<div>
</div>
<br />
<div>
---<br />
- hosts: webservers</div>
- name: reboot the servers</div>
<div>
command: /sbin/reboot -t now</div>
<div>
<br /></div>
<div>
Here is the "restart httpd activity" done in a playbook:</div>
<div>
<div>
<br />
<div>
</div>
<br />
<div>
---<br />
- hosts: webservers</div>
- name: restart webserver</div>
<div>
service:</div>
<div>
name: httpd</div>
<div>
state: restarted</div>
</div>
<div>
<br /></div>
<div>
Notice how playbooks connect the activities to a name so the playbook writer is organizing their scripts by giving them names. "restart httpd" becomes "restart webserver." Having good names is important to supporting maintainability and human readability.</div>
<div>
<br /></div>
<div>
Here are <a href="https://github.com/ansible/ansible-examples" target="_blank">example playbooks that do "real worlwork."</a> However, I found the use of roles and how the files are organized confusing for getting started. And there is the real possibility that there is a better, less confusing way to organize an enterprises inventory, roles, and files. </div>
</div>
<div>
<br />
Playbooks are executed by the <b>ansible-playbook</b> command.<br />
<h3>
Write a playbook</h3>
Let's build a "compliance" playbook that establishes the state "no errors should be in system logs." The biggest complication I had in learning Ansible was writing YAML and understanding its implicit syntax between Lists and Dictionary. These problems go away with experience and since writing YAML is foundational, it's important to get this working "for you" rather than "against you." So to get some experience, I found slowly "growing" (iterating on) the simplest playbook into something useful, helped me learn how to work with YAML. Jumping straight to the example playbooks listed above didn't give me the experience with YAML that I needed.<br />
<br />
Here is the simplest possible playbook:<br />
---<br />
...<br />
Run it:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Y05VAoEDZKihr3HAgauN88OmypAU06jprQ3Rxa2kMCzc7VAJvOhhx4TDMuQKQmiuo4GyD2ZLeVGD553FXGM-PGwfKHF-k2-xl2ySNHgLSdG_68HKCTeq1kSbcYWHnQ1yr9jUlkUReNE/s1600/Screen+Shot+2019-07-08+at+10.44.57+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="228" data-original-width="1060" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Y05VAoEDZKihr3HAgauN88OmypAU06jprQ3Rxa2kMCzc7VAJvOhhx4TDMuQKQmiuo4GyD2ZLeVGD553FXGM-PGwfKHF-k2-xl2ySNHgLSdG_68HKCTeq1kSbcYWHnQ1yr9jUlkUReNE/s400/Screen+Shot+2019-07-08+at+10.44.57+AM.png" width="400" /></a></div>
<br />
Let's connect to a host by adding it as a list item.<br />
---<br />
- hosts: blogs<br />
...<br />
<br />
From a YAML syntax perspective, "- " means list. "hosts: blog" is a key (hosts) and value (blog).<br />
Run it:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1f-VG-ooPyASGsQEeSR8WS-T1wDD6cBmgKDrQakH7495uuG8um9sPIRDdQhj3cZuZrL8jO6bdsOnsv20xjpTEslPyyS7E8HVILU27dbgrpOTLf4M0O9-hG4iFo79bTvTOovRitgTXhrc/s1600/Screen+Shot+2019-07-08+at+3.28.14+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="374" data-original-width="1102" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1f-VG-ooPyASGsQEeSR8WS-T1wDD6cBmgKDrQakH7495uuG8um9sPIRDdQhj3cZuZrL8jO6bdsOnsv20xjpTEslPyyS7E8HVILU27dbgrpOTLf4M0O9-hG4iFo79bTvTOovRitgTXhrc/s400/Screen+Shot+2019-07-08+at+3.28.14+PM.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Since I didn't add the inventories, it couldn't resolve what "blogs" meant. So I added the inventory file (-i <file>) just like I did in the <a href="https://confessionsofanagilecoach.blogspot.com/2019/05/enterprise-network-configuration-as.html" target="_blank">Enterprise Infrastructure as Code with Ansible</a> article.</file><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsnhCabZEZH6k4hrlxCDJ19v_Y8O0jBLuBpeeOvvJm1Q0t2r2LrKdjUnMA_Fy2-iWrLtv9-AL69Usxkmb05YMVvr6mvn8ZRBEMO0noWzJlW8mZPuEX4TirTLYwNer7-0Vq3gbGx0Q4dmo/s1600/Screen+Shot+2019-07-08+at+3.28.28+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="380" data-original-width="1104" height="137" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsnhCabZEZH6k4hrlxCDJ19v_Y8O0jBLuBpeeOvvJm1Q0t2r2LrKdjUnMA_Fy2-iWrLtv9-AL69Usxkmb05YMVvr6mvn8ZRBEMO0noWzJlW8mZPuEX4TirTLYwNer7-0Vq3gbGx0Q4dmo/s400/Screen+Shot+2019-07-08+at+3.28.28+PM.jpg" width="400" /></a></div>
Now it is resolving the definition of "blogs" to a hostname. But it says the host is unreachable because I need to pass along the username via (-u <user name="">). (You can also embed that in the inventory or even playback. I chose to use the command line to pass the username so I can put my files into public source control without exposing my username.)</user><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZFxB9UpqpX0j4kJcWg2YhhFYflaIYeUGdKlbP2exKrJnkTofRspTdWeGugHpnkj2e4E08zPkuE87yg7Ei0PN2CPrpAK9K3DxTdoyytPu5wadoQk1LI4BgALuaUNOnUiFYPd-nSTSPu5E/s1600/Screen+Shot+2019-07-08+at+3.29.31+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="372" data-original-width="1106" height="133" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZFxB9UpqpX0j4kJcWg2YhhFYflaIYeUGdKlbP2exKrJnkTofRspTdWeGugHpnkj2e4E08zPkuE87yg7Ei0PN2CPrpAK9K3DxTdoyytPu5wadoQk1LI4BgALuaUNOnUiFYPd-nSTSPu5E/s400/Screen+Shot+2019-07-08+at+3.29.31+PM.jpg" width="400" /></a></div>
Success! Our first end-to-end playbook execution. You can get the code on <a href="https://github.com/lancerkind/ansible_getting_started/tree/playbook_tutorial" target="_blank">GitHub at this branch</a>.<br />
<br />
In YAML, "-hosts ..." is how a list is declared (dash with a space). Then "hosts : blogs" is a key and value pair. So Ansible loaded the YAML and accesses the list and looks for the key named "hosts." It then tried to resolve the value "blogs." Since there isn't a hostname literally named blog, it couldn't ignored that host until an inventory was referenced which defined "blogs."<br />
<h3>
Let's start checking logs</h3>
<div>
As you develop a playbook, keep the <a href="https://docs.ansible.com/ansible/latest/modules/modules_by_category.html" target="_blank">Ansible Module documentation</a> handy. Since we'll need to execute a shell script on our remote servers, let's do something easy to "kick the tires" of the shell module. (For tips for deciding between command, shell, and script module see the module notes <a href="https://docs.ansible.com/ansible/latest/modules/shell_module.html#notes" target="_blank">here</a> and <a href="https://docs.ansible.com/ansible/latest/modules/command_module.html#notes" target="_blank">here</a>. In this case, we could start with the Command module but since we'll eventually want to use pipes we need to use the Shell module. ) Make the changes needed to have simplest_playbook.yml as the following:</div>
<div>
<br /></div>
<div>
<div>
---</div>
<div>
- hosts: blogs</div>
<div>
tasks:</div>
<div>
- shell: echo "hello" > hello.txt</div>
<div>
- shell: grep "hello" hello.txt </div>
<div>
...</div>
</div>
<div>
And test the file for errors:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia3XV0otSkrM92LgHq97fsH532SkiEc-NKtSbuS1nNYe-RFZq8CWBrUF6roI9u8CC4PDXU0yhrTfRX3ysaWugM-NfPpoDRlHkE7Yl4cV1HAEPPAD-mjddl5jDBKUGuIXsOPFC4yNiMOF8/s1600/Screen+Shot+2019-07-15+at+1.27.15+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="430" data-original-width="1104" height="155" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia3XV0otSkrM92LgHq97fsH532SkiEc-NKtSbuS1nNYe-RFZq8CWBrUF6roI9u8CC4PDXU0yhrTfRX3ysaWugM-NfPpoDRlHkE7Yl4cV1HAEPPAD-mjddl5jDBKUGuIXsOPFC4yNiMOF8/s400/Screen+Shot+2019-07-15+at+1.27.15+PM.png" width="400" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
It's happy with the YAML.</div>
<div>
<br /></div>
<div>
<b>Syntax Sidebar: </b></div>
<div>
"- " declares a sequence. "hosts: blogs" is a key and value pair. </div>
<div>
"tasks: " maps the tasks to whatever follows, a sequence of more key values: "- " shell: ...:</div>
<div>
Ansible requires the key following "tasks: " to be either "name: " or a module.</div>
<div>
<br /></div>
<b>Note about YAML style:</b><br />
In YAML the following two playbooks are equivalent. Normally, I advise people to use the most succinct style but I had a lot of confusion separating the sequence marks "- " from the mappings. Otherwise, at least to me, "- hosts: blogs" and "tasks:" don't seem to both be mappings. My brain keeps seeing the "- " and that keeps interfering with my understanding. If you agree with me, great. If you don't then reformat it to how you like. This article about YAML describes how to work with sequences and mappings in a general sense. It also was less confusing than the others.<br />
<br />
<b>This:</b><br />
<div>
<div>
---</div>
<div>
- hosts: blogs</div>
<div>
tasks:</div>
<div>
- shell: echo "hello" > hello.txt</div>
<div>
- shell: grep "hello" hello.txt </div>
<div>
...</div>
</div>
<div>
<br /></div>
<div>
<b>Verses this:</b></div>
<div>
<div>
---</div>
<div>
- </div>
<div>
hosts: blogs</div>
<div>
tasks:</div>
<div>
- </div>
<div>
shell: echo "hello" > hello.txt</div>
<div>
- </div>
<div>
shell: grep "hello" hello.txt </div>
<div>
...</div>
</div>
<div>
<b>Are equivalent.</b></div>
<div>
<br /></div>
<div>
Let's execute this against the remote host by telling ansible-playbook about our inventory and user:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbr6Rtr42vilx7bubkQFeRiDIdnQe1PhZ75Ca2uzOpyWQuwH-lxUNDlFuXqcXfV4FRFYZrx0mK5EVQdzNQy4nUj4VUvUJIzuOF5l9EJ14PAR44bFh5z17UbQtBHrW7sHsEbd6PvlDDUo4/s1600/Screen+Shot+2019-07-15+at+1.39.08+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="536" data-original-width="1100" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbr6Rtr42vilx7bubkQFeRiDIdnQe1PhZ75Ca2uzOpyWQuwH-lxUNDlFuXqcXfV4FRFYZrx0mK5EVQdzNQy4nUj4VUvUJIzuOF5l9EJ14PAR44bFh5z17UbQtBHrW7sHsEbd6PvlDDUo4/s400/Screen+Shot+2019-07-15+at+1.39.08+PM.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
Notice that it reports "OK." Notice the TASK [shell] which echoes back what module executed. More information can be sent to the console by describing what is happing with the call to the modules using the "name :" attribute before the call to the module:</div>
<div>
---</div>
<div>
- </div>
<div>
hosts: blogs</div>
<div>
tasks:</div>
<div>
- </div>
<div>
name: creating file</div>
<div>
shell: echo "hello" > hello.txt</div>
<div>
- </div>
<div>
name: confirming it worked</div>
<div>
shell: grep "hello" hello.txt </div>
<div>
...</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIg_4MYlE1Yu3Nzj7DGucOiiFVaN-BhZLvzf5vg4uvUTq0XOjIeI8YXmEXOlG9dBKoehTWhzr8tnJrJC0bslbexHfXAlYWXmaV5tbRMGXzpM-bwOrxj9Jh4lFhDqB5rmv0aBC_3IBvkI0/s1600/Screen+Shot+2019-07-15+at+1.49.52+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="540" data-original-width="1100" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIg_4MYlE1Yu3Nzj7DGucOiiFVaN-BhZLvzf5vg4uvUTq0XOjIeI8YXmEXOlG9dBKoehTWhzr8tnJrJC0bslbexHfXAlYWXmaV5tbRMGXzpM-bwOrxj9Jh4lFhDqB5rmv0aBC_3IBvkI0/s400/Screen+Shot+2019-07-15+at+1.49.52+PM.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
Notice the output about TASK contains what was mentioned by the "name: "mapping.</div>
<div>
<br /></div>
<div>
The play can be more DRY (Do not Repeat Yourself) by declaring a variable.</div>
<div>
<div>
---</div>
<div>
- </div>
<div>
hosts: blogs</div>
<div>
vars:</div>
<div>
message: hello</div>
<div>
<br /></div>
<div>
tasks:</div>
<div>
-</div>
<div>
name: creating file</div>
<div>
shell: echo {{message}} > hello.txt</div>
<div>
-</div>
<div>
name: confirming it worked</div>
<div>
shell: grep {{message}} hello.txt</div>
<div>
...</div>
</div>
<h4>
Check server logs</h4>
Now we can write a new playbook that checks that our system logs are error free. First, I developed the following by working and testing in a terminal window:<br />
<blockquote class="tr_bq">
find /var/log -name "*.log" -type f -exec grep -i "error" {} + | grep -v "error_log" | wc -l | grep "0"</blockquote>
If Shell detects a command that returns a none zero code, it will signal there was an error.<br />
(See <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html" target="_blank">Ansible and error codes</a> if you want the details.) The above is designed to return a 0 if there aren't any errors, or a non-zero if there are errors found in the log files.<br />
<br />
---<br />
-<br />
hosts: blogs<br />
<br />
tasks:<br />
-<br />
shell: find /var/log -name "*.log" -type f -exec grep -i "error" {} + | grep -v "error_log" | wc -l | grep "0"<br />
<br />
And run it:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV4g-5lLRIn4XjZk1De2gWRuA5jREsmLmzU-RFhX08rLO98JAiGeW1T8Vz5NeyNzik_3aukQWlPw4SPiTyxcvOkXaebIop9bScO3Nb22uGzx9z_n-oG53Kt7pNDQo8XH3DfxYTAk2w7uI/s1600/Screen+Shot+2019-07-16+at+4.36.43+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="713" data-original-width="984" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV4g-5lLRIn4XjZk1De2gWRuA5jREsmLmzU-RFhX08rLO98JAiGeW1T8Vz5NeyNzik_3aukQWlPw4SPiTyxcvOkXaebIop9bScO3Nb22uGzx9z_n-oG53Kt7pNDQo8XH3DfxYTAk2w7uI/s400/Screen+Shot+2019-07-16+at+4.36.43+PM.jpg" width="400" /></a></div>
If your logs aren't clean, then the PLAY RECAP will tell you the results, which in this case is "failed." Ansible dumps a json file containing what was sent to Shell, what was sent to stderror, and so on. After cleaning the logs up (or you can cheat and chang the script to look for something more "unique" than error), run the command again.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvXXq6TYMAzC5Znft_z-CtzyXngBTJxlclffzJ4Kw4T5ReA36cyKfe-v47CH4fgguJ14qFBdyQFaNKdG2Olncr1lmhPP1wJOjW6GxcWPYv9smYq876TxZ3DVakEcbt2981HvC7ZI0r704/s1600/Screen+Shot+2019-07-16+at+4.36.57+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="978" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvXXq6TYMAzC5Znft_z-CtzyXngBTJxlclffzJ4Kw4T5ReA36cyKfe-v47CH4fgguJ14qFBdyQFaNKdG2Olncr1lmhPP1wJOjW6GxcWPYv9smYq876TxZ3DVakEcbt2981HvC7ZI0r704/s400/Screen+Shot+2019-07-16+at+4.36.57+PM.jpg" width="400" /></a></div>
<div>
When running the playbook, which runs top to bottom, hosts with failed tasks are taken out of the rotation for the entire playbook. If things fail, simply correct the playbook file and rerun.<br />
Adding a "name: " mapping would make TASK output more sensible. "TASK [shell] ******" isn't a very useful message:<br />
<br />
---<br />
-<br />
hosts: blogs<br />
<br />
tasks:<br />
-<br />
<b> name: Scanning Logs for error.</b><br />
shell: find /var/log -name "*.log" -type f -exec grep -i "error" {} + | grep -v "error_log" | wc -l | grep "0"<br />
...<br />
<br />
Notice the yellow box below contains more meaningful output.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSXp54sRU9nheqzT4TTXt6tGfAOXBvr-qK9nSbXrM_Uo0Yyx3kjdHUo7-MDzTJV1eKCOvYovp9NrvKh_fdvUUb3QP-J75mZW28PXRBq2LCPr9ro931zU-1hOAKDL-VF8qVN6BkDpzouoM/s1600/Screen+Shot+2019-07-17+at+4.42.57+PM.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="254" data-original-width="972" height="103" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSXp54sRU9nheqzT4TTXt6tGfAOXBvr-qK9nSbXrM_Uo0Yyx3kjdHUo7-MDzTJV1eKCOvYovp9NrvKh_fdvUUb3QP-J75mZW28PXRBq2LCPr9ro931zU-1hOAKDL-VF8qVN6BkDpzouoM/s400/Screen+Shot+2019-07-17+at+4.42.57+PM.jpg" width="400" /></a></div>
</div>
<h2>
File Organization</h2>
A very basic organization is a directory that's checked into source control and containing:<br />
<ul>
<li>directory of staging inventory</li>
<li>directory of production inventory</li>
<li>playbooks</li>
</ul>
This is the organization used in the example code used in this tutorial. Building on that idea, most IT departments will need to add:<br />
<ul>
<li>directory of roles</li>
<li>directory of group_vars</li>
<li>directory of host_vars</li>
</ul>
And eventually, an organization with a mature use of Ansible will be creating their own modules for code reuse and will need directories for these files:<br />
<ul>
<li>library <- code="" li="" lives="" module="" the="" where="">
<!-----></li>
<li>module_utils <- common="" for="" li="" libraries="" live="" modules="" those="" where="">
<!-----></li>
<li>filter_plugins</li>
</ul>
<div>
Modules "should be idempotent" meaning running the module many times should have the same affect as running it once. They should be designed to check that the final state has already been achieved and if so, exit without performing any actions. </div>
<div>
<br /></div>
<br />
If you're curious to dig deeper into learning more about the more advanced levels of organization, <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#content-organization" target="_blank">more details here</a>.<br />
<h2>
Next Level Ansible</h2>
<div>
Once you've got some experience making a few playbooks, these topics that will take your Ansible work to the next level. Each other the below are an article by themselves.</div>
<div>
<ul>
<li><a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html" target="_blank">Organizing inventory by roles</a></li>
<li><a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html" target="_blank">Jinja templating</a></li>
<li><a href="https://docs.ansible.com/ansible/latest/modules/group_by_module.html#group-by-module" target="_blank">use Ansible to derive inventory groups at runtime</a></li>
<li><a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_special_topics.html" target="_blank">playbook debugging</a> and other advanced features </li>
</ul>
</div>
<h2>
Resources:</h2>
<a href="https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html" target="_blank">YAML syntax</a>. The syntax doc is a short and sweet read.</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com5tag:blogger.com,1999:blog-284399302764909932.post-71106167568948813352019-05-31T17:24:00.000-07:002019-08-06T14:05:14.155-07:00Enterprise Infrastructure as Code with Ansible<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: left;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiohkRJ1tjm1q7fNZi2zx5WwdVnlkFx-HJ8IVWBwVeunqkO569yeZt3VoCYFNmqUBFPifRqNe96A-MM5US-Fd-7jshK7MCLseiRpmLBmxfH-0odYAyJuA0GcZLell3CnQYxXWvbbwjQ7bE/s1600/Ansible.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiohkRJ1tjm1q7fNZi2zx5WwdVnlkFx-HJ8IVWBwVeunqkO569yeZt3VoCYFNmqUBFPifRqNe96A-MM5US-Fd-7jshK7MCLseiRpmLBmxfH-0odYAyJuA0GcZLell3CnQYxXWvbbwjQ7bE/s320/Ansible.jpg" width="320" /></a></div>
Keeping up on enterprise network configuration with spreadsheets, CAB meetings, scripts, and a swiss army knife of configuration software isn't sustainable. Normalizing network administration to a single tool brings focus and effective standardization. Ansible is one of the many ways to do this. Ansible is lightweight and easily extensible to administer any equipment that allows SSH. I perfromed the below on MacOS Mojave. Here is the code on <a href="https://github.com/lancerkind/ansible_getting_started" target="_blank">GitHub</a>. </div>
<h2 style="text-align: left;">
Get Ansible on MacOS</h2>
<div>
To run Ansible on Mac install the following: Python 2 (version 2.7) or Python 3 (versions 3.5 and higher), Open SSL, and Ansible ( https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#control-node-requirements ) This will allow your Mac to be a "control node" and send commands out to many machines. MacOS is limited to 15 open files so you may need to adjust this if you're controlling a lot of nodes. (See https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#control-node-requirements)<br />
<br /></div>
<div>
It's a bit "sporty" to build and install OpenSSL on a Mac. If you use Brew or MacPorts, do one of the following and skip to install Ansible:</div>
<div>
<br /></div>
<div>
If you're like me and not a user of brew or MacPorts, then see the following steps.</div>
<h3 style="text-align: left;">
Update Python </h3>
<div>
The Python that comes stock on MacOS is likely pretty old. Go to the terminal and check:</div>
<div>
(If you happen to accidentally type "python -v" use ctrl-D to close the REPL.)<br />
$ python -V </div>
<div>
Or </div>
<div>
$ python3 -V</div>
<div>
<br /></div>
<div>
The later option checks for a common name that the newer python is installed as. You can also check /usr/local/bin and see if there are other versions of python already sitting there. The stock version of python is at /usr/bin.</div>
<div>
<br /></div>
<div>
If you don't have python 3.5 or greater, install a newer version. Here are some instructions:</div>
<div style="text-align: left;">
https://www.macobserver.com/analysis/how-to-upgrade-your-mac-to-python-3-2017-update/</div>
<h3 style="text-align: left;">
Build and Install Open SSL</h3>
<div>
Go <a href="https://github.com/openssl/openssl" target="_blank">here</a> and follow the instructions (https://github.com/openssl/openssl/blob/master/INSTALL) to build, execute tests, and install OpenSSL. </div>
<h3 style="text-align: left;">
Install Ansible</h3>
<div>
Follow the instructions for MacOS here: https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#latest-releases-on-macos<br />
<br />
Test it with:<br />
$ ansible --version<br />
<br />
Notice what version of python it reports back. This is the version of python your control node is using. If it's not using the Python 3 that you intended, keep this into account before doing something important with ansible. If you notice some of the ansible commands not working, then you'd better attend to this. (You can't simply change /user/bin/python as MacOS keeps this immutable for a number of good reasons.)</div>
<h2 style="text-align: left;">
Your first configuration</h2>
Since we want to use network configuration as code, make a directory for storing configurations so they can be checked in. In my case, I used Git for source control and VI for editing files:<br />
$ mkdir ansible_repository<br />
$ cd ansible_repository<br />
$ git init<br />
$ mkdir hosts<br />
$ vi lancer_kind_com.ini<br />
<br />
Here is background on creating inventory files. For this example, it's simple. <a href="https://docs.ansible.com/ansible/2.3/intro_inventory.html">https://docs.ansible.com/ansible/2.3/intro_inventory.html</a><br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHaZMWJ-c8BgMOGibGfD971PIMTAD40XZRJA8rusxL0xVriMsM-szm8SmxnkWnwtIlmxwY0f7sWtGc0vAq4gvR0rOLHrap-UHrvsQGIdOxtHRVXnmigAD6foPwjBro_b8zNqfpRn4VANw/s1600/Screen+Shot+2019-05-31+at+5.03.49+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="126" data-original-width="976" height="81" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHaZMWJ-c8BgMOGibGfD971PIMTAD40XZRJA8rusxL0xVriMsM-szm8SmxnkWnwtIlmxwY0f7sWtGc0vAq4gvR0rOLHrap-UHrvsQGIdOxtHRVXnmigAD6foPwjBro_b8zNqfpRn4VANw/s640/Screen+Shot+2019-05-31+at+5.03.49+PM.png" width="640" /></a></div>
<br />
<br />
Test it by invoking the ping module:<br />
$ ansible macattack -i lancer_kind_com.ini -m ping<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisvFltVRsZJYoBv9YRwWUX50gOya9LuiuzALoGlToh25KWrgIB4MH-UwXJFg2osbA7QdGIUVZ9RNeYvJGRmnRjcmdROvsZXY1aXsZVz9fyH4A8kF9SVazo-FuwRc3LFB9K8GRwNCGi0vc/s1600/Screen+Shot+2019-05-31+at+5.03.25+PM.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="442" data-original-width="986" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisvFltVRsZJYoBv9YRwWUX50gOya9LuiuzALoGlToh25KWrgIB4MH-UwXJFg2osbA7QdGIUVZ9RNeYvJGRmnRjcmdROvsZXY1aXsZVz9fyH4A8kF9SVazo-FuwRc3LFB9K8GRwNCGi0vc/s640/Screen+Shot+2019-05-31+at+5.03.25+PM.png" width="640" /></a><br />
<h2 style="text-align: left;">
Managing Remote Objects</h2>
<div>
Great. You can administer your workstation with Ansible. Time for the next level: managing a single remote site.</div>
<h3 style="text-align: left;">
Configure SSH, Passwords, and Security</h3>
<div>
Let's all agree that it doesn't make any sense to type in SSH passwords as that's not a scalable or very useful automation strategy. If you're new to using ssh public/private keys, and want to set it up by hand, <a href="https://www.tecmint.com/ssh-passwordless-login-using-ssh-keygen-in-5-easy-steps/" target="_blank">here is a good article</a>. Once you've done that, test your configuration:</div>
<div>
$ ssh <i>username</i><user><i>@hostname</i><hostname> ls</hostname></user></div>
<div>
<br /></div>
<div style="text-align: left;">
<i>(When you generated a key with ssh-keygen, you set a pass phrase, you'll still need to type in the pass phrase whenever ssh needs to work with the key you created. If you don't like that, set the pass phrase to empty--press return. You can use "ssh-keygen -p -f <keyfile_in_ .ssh_directory="">" to change the pass phrase of an existing file.)</keyfile_in_></i></div>
<div>
<br /></div>
<div>
To configure SSH for all your thousands of nodes in a scalable fashion, <a href="https://everythingshouldbevirtual.com/ansible-ssh-key-distribution-for-password-less-ssh/" target="_blank">read how to do key distribution.</a></div>
<h3 style="text-align: left;">
Ansible</h3>
<div>
Add the remote host to the inventory file. For more details on configuration file, see the Ansible docs: <a href="https://docs.ansible.com/ansible/2.3/intro_inventory.html">https://docs.ansible.com/ansible/2.3/intro_inventory.html</a>.</div>
<div>
</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9aDZk8a6MgBRUq4i01smHhsiwMM3kUuJTWMXkP6n0DP0GvhW2lndnbAH85PNipHGZ39cW7uQoOpvNUSw5JTwNv_qmqOxi-YFu9R02jgY8nCX2ad0HooLs5GXdNDtvFfRvwxW4ib2TyUk/s1600/Screen+Shot+2019-06-10+at+10.46.39+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="278" data-original-width="670" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9aDZk8a6MgBRUq4i01smHhsiwMM3kUuJTWMXkP6n0DP0GvhW2lndnbAH85PNipHGZ39cW7uQoOpvNUSw5JTwNv_qmqOxi-YFu9R02jgY8nCX2ad0HooLs5GXdNDtvFfRvwxW4ib2TyUk/s400/Screen+Shot+2019-06-10+at+10.46.39+AM.png" width="400" /></a></div>
<br /></div>
<div>
[] is a grouping mechanism. With [blogs] for example, many sites can be listed and managed as a group. Execute the ping module on [blogs]. "-u <username>" has been redacted. It is the SSH user that ansible will use.</username></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmMlh8MHO5UG1hSZJj5HoJK2vzLS_qkz0zG5HUNCG5xi_Z9gfs9XWi6Yls7f-2d1hHxFeR9elvlpBUNMFfFF6EpPUIS61K-uEUZy9PcRvshztt5iH8k4GjI_wK4f55bLCBdKQOfVU5LBY/s1600/Screen+Shot+2019-06-10+at+10.48.06+AM.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="428" data-original-width="978" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmMlh8MHO5UG1hSZJj5HoJK2vzLS_qkz0zG5HUNCG5xi_Z9gfs9XWi6Yls7f-2d1hHxFeR9elvlpBUNMFfFF6EpPUIS61K-uEUZy9PcRvshztt5iH8k4GjI_wK4f55bLCBdKQOfVU5LBY/s640/Screen+Shot+2019-06-10+at+10.48.06+AM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<h2 style="text-align: left;">
Where to put your Ansible user name?</h2>
<div>
You can put the username in the inventory file, or in the roles (I'll go over this in an article about Ansible Playbooks), or at the command line. I chose the command line so that I could check in files and still keep my ansible user name secret from the internet. Here is a <a href="https://stackoverflow.com/questions/24095807/ansible-get-the-username-from-the-command-line" target="_blank">StackOverflow</a> thread about these three options.<br />
<h2 style="text-align: left;">
Executing ad hoc CLI</h2>
</div>
<div>
A simple yet powerful feature is that now a command can be sent to many nodes in a SIMD manner. Since there is no "-m" argument, ansible uses the default which is the "command" module. Command module takes a "-a" argument which means what is after "-a" is executed by the endpoint. So "hostname" will be executed it every machine in the "blogs" group, in parallel execution. Using -f tells ansible to do them in sets of 20 rather than ALL the endpoints grouped under "blogs."</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-x79-pe9D4L7gxYV5E1EYEsNopw7CytewI_qb8_5RMT_UXZ6RpSCcF2KpQCxBT1Ysf0FBWqYflR0p7qSEn1uxuxVscl5P138sxh0tqGIZibancLHiY7EuHnquSPIIMlfk3A-CJwu21ow/s1600/ansible+hostname.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="700" data-original-width="974" height="457" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-x79-pe9D4L7gxYV5E1EYEsNopw7CytewI_qb8_5RMT_UXZ6RpSCcF2KpQCxBT1Ysf0FBWqYflR0p7qSEn1uxuxVscl5P138sxh0tqGIZibancLHiY7EuHnquSPIIMlfk3A-CJwu21ow/s640/ansible+hostname.jpg" width="640" /></a></div>
<div>
<br /></div>
<h2 style="text-align: left;">
Conclusion</h2>
<div>
Ansible can be used to manage any system which can handle SSH (not only computers but routers, ...). Keeping your private key secure and distributing keys is a bit more work. What's left to learn is to organize your inventory files (or integrate an inventory service into Ansible) in a maintainable way. The good news is that you can start small and grow your Ansible skills.<br />
<br />
Here are two pathways for continued education:<br />
<br />
<ul style="text-align: left;">
<li><a href="https://docs.ansible.com/ansible/latest/user_guide/command_line_tools.html" target="_blank">Other Ansible utilities</a>:</li>
<ul>
<li>ansible - the command used in this article</li>
<li>ansible-config - list configurations that ansible has access too</li>
<li>ansible-console - a REPL environment to practice your ansible commands</li>
<li>ansible-doc - list plugins and documents</li>
<li>ansible-galaxy - install modules from ansible galaxy</li>
<li>ansible-inventory - display or dump configuration information as ansible sees it</li>
<li>ansible-playbook - execute playbooks</li>
<li>ansible-pull - pulls a playbook from a VCS repo</li>
<li>ansible-vault - encrypt a structured data file used by Ansible.</li>
</ul>
<li><a href="https://docs.ansible.com/ansible/latest/network/" target="_blank">Ansible for network administration</a></li>
</ul>
</div>
<h2 style="text-align: left;">
References</h2>
Nice getting started video: <a href="https://www.ansible.com/resources/videos/quick-start-video?extIdCarryOver=true&sc_cid=701f2000001OH7YAAW">https://www.ansible.com/resources/videos/quick-start-video?extIdCarryOver=true&sc_cid=701f2000001OH7YAAW</a><br />
<br />
Current suggested practices: <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html">https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html</a></div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com3tag:blogger.com,1999:blog-284399302764909932.post-40814568771454986152019-04-08T18:08:00.000-07:002019-04-08T18:12:05.762-07:00Designers and Agile Teams<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKXY2JQKfSsBApxSBP5qBeAKQuv6BzV1s2DdER78_8u9HySAKda6naSgAIKUbGqHetTzIlf5wqCIoPhPp_LOP6wh1qZ2swqVrapkqaTjoT75M85TVDjxZETrEGhd6z8Q_K5D_8vBknxuU/s1600/Royalties.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="431" data-original-width="620" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKXY2JQKfSsBApxSBP5qBeAKQuv6BzV1s2DdER78_8u9HySAKda6naSgAIKUbGqHetTzIlf5wqCIoPhPp_LOP6wh1qZ2swqVrapkqaTjoT75M85TVDjxZETrEGhd6z8Q_K5D_8vBknxuU/s320/Royalties.jpg" width="320" /></a></div>
It's the WILD WEST out there when it comes to how designers work with Agile teams. Sometimes they are part of the team and go to all the team events such as daily standup and planning. Other designers work on their own as a "team of designers" and stretch themselves across multiple Agile teams. <br />
<br />
I'm collecting experiences of these "guardians of the hip and beautiful"to learn what they found worked well and what didn't. If you'd like to add your own ideas, use the comments or contact <a href="https://twitter.com/LancerKind" target="_blank">Agile Thoughts</a> podcast and get on our interview schedule.<br />
<br />
Here is a link to the <a href="https://agilenoir.biz/series/agile-thoughts/" target="_blank">Agile Thoughts episode archive</a>.</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-64179503335428368632019-02-04T19:16:00.001-08:002019-02-05T07:47:27.658-08:00Mocking with Google Application Scripts (Javascript)<div dir="ltr" style="text-align: left;" trbidi="on">
This article covers why you need to do mocking and how to do it using examples using Google Application Script (a form of Javascript). In each case, we'll start with a code example (not following the TDD so we can focus on mocking) then write a micro test. From there, we'll focus on the steps to <b>Implement a Mock Object:</b><br />
<ol style="text-align: left;">
<li>find a way to inject the mock</li>
<li>design a minimal mock object that achieves the necessary goals</li>
<li>use the mock in an automated micro test</li>
</ol>
<h2 style="text-align: left;">
What is Mocking</h2>
Mocking is the practice of using simple objects in place of *real* objects in order to have full control of product code for which you want to write an automated test. (Product code is the code you put into production as it gives you the functionality you or your users want. Test code is Javascript code that tests the product code.)<br />
<br />
Because Javascript is loaded and executed at runtime and isn't strict about types, it is an extremely flexible language. Yet, when writing micro tests there will be code that correctly does its job in production, yet needs to be mocked or return fake data for unit testing.<br />
<h2 style="text-align: left;">
Situations that require a Mock object</h2>
Mock objects are used in place of real objects are doing the following "no nos."<br />
<h4 style="text-align: left;">
Micro Testing No Nos</h4>
<ul style="text-align: left;">
<li>write to our computer screen (ui widgets)</li>
<li>communicate with a network (databases, webservices, cloud services,...)</li>
<li>connect to a system service such as the system clock</li>
<li>operate the file system</li>
<li>in general: bring complication to your micro test that slows it down or makes it reliant on something at adds complexity.</li>
</ul>
<h3 style="text-align: left;">
Example:</h3>
<div>
Copy the below code into a GAS script file called Alert.gs:</div>
<blockquote class="tr_bq">
<blockquote class="tr_bq">
function showAlert() {</blockquote>
<blockquote class="tr_bq">
var ui = SpreadsheetApp.getUi(); // Same variations.</blockquote>
<blockquote class="tr_bq">
var result = ui.alert(</blockquote>
<blockquote class="tr_bq">
'Please confirm',</blockquote>
<blockquote class="tr_bq">
'Are you sure you want to continue?',</blockquote>
<blockquote class="tr_bq">
ui.ButtonSet.YES_NO);</blockquote>
<blockquote class="tr_bq">
// Process the user's response.</blockquote>
<blockquote class="tr_bq">
if (result == ui.Button.YES) {</blockquote>
<blockquote class="tr_bq">
// User clicked "Yes".</blockquote>
<blockquote class="tr_bq">
ui.alert('Confirmation received.');</blockquote>
<blockquote class="tr_bq">
} else {</blockquote>
<blockquote class="tr_bq">
// User clicked "No" or X in the title bar.</blockquote>
<blockquote class="tr_bq">
ui.alert('Permission denied.');</blockquote>
<blockquote class="tr_bq">
}</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
<br />
Run the function and you'll get the following in the script editor:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv2sEpuj6r6HbS3faL2OxKlMKjYiRKf6gO98AbOmZ_bGff1sYUoRktWlf82PVvPnY2XGcyELdODS7lrB9-FsU7WWNgAeMik55IZtPSL6ocNs5D2gaInxD0_TBaZeR-b43payyCFFnN1mY/s1600/Screen+Shot+2019-02-04+at+3.34.11+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="834" data-original-width="810" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv2sEpuj6r6HbS3faL2OxKlMKjYiRKf6gO98AbOmZ_bGff1sYUoRktWlf82PVvPnY2XGcyELdODS7lrB9-FsU7WWNgAeMik55IZtPSL6ocNs5D2gaInxD0_TBaZeR-b43payyCFFnN1mY/s400/Screen+Shot+2019-02-04+at+3.34.11+PM.png" width="387" /></a></div>
Note the script editor's prompt about: "Cancel Dismiss." What has happened is the widget is showing in the Google Docs application to which the script is attached. In this case, I clicked on the tab for the Google Sheet.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp_lI-5mTrzDpzAbx2Ysj50YPbGyOb8hUhfi9R04v0qqctI2MnB-PPaNuXlgDR3Un7ZRwS8fjRsOsdXDbKv-1e5Sd9XmGHDLAVZ4eEWkQiufbVe_R73qlGRbORbSQbx6g5VrFHYyQu74U/s1600/Screen+Shot+2019-02-04+at+3.39.45+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="342" data-original-width="662" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp_lI-5mTrzDpzAbx2Ysj50YPbGyOb8hUhfi9R04v0qqctI2MnB-PPaNuXlgDR3Un7ZRwS8fjRsOsdXDbKv-1e5Sd9XmGHDLAVZ4eEWkQiufbVe_R73qlGRbORbSQbx6g5VrFHYyQu74U/s320/Screen+Shot+2019-02-04+at+3.39.45+PM.png" width="320" /></a></div>
Take a look at the above and think about what is important to check with an automated test. For this type of functionality, the below is a typical list of "checks" or test case:<br />
<h4 style="text-align: left;">
Checks for Alert</h4>
<ol style="text-align: left;">
<li>The prompt text correct. In this case, the prompt is "Please confirm."</li>
<li>The question you're asking the user is correct. In this case "Are you sure you want to continue?"</li>
<li>the dialog box is of the correct type. In this case, has a Yes and No.</li>
<li>When Yes is clicked, a "yes message" is returned to the caller. When No is clicked, a "no message" is returned to the caller.</li>
</ol>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4g5pQl7yvifG4ulmafA_7_9JZTUHxppfnugJIjhN_WS-pwTgX4VyrGFs1jU_ZmpVMqLVw6b-1e6DhLMmKAsy6cRg3lZqewSnUdrgmA-ylpl9_Kyq7pU3c30e80KrvXX5T0MTCSiIKRkI/s1600/Lance+headshot+development+effort+1200x.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1200" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4g5pQl7yvifG4ulmafA_7_9JZTUHxppfnugJIjhN_WS-pwTgX4VyrGFs1jU_ZmpVMqLVw6b-1e6DhLMmKAsy6cRg3lZqewSnUdrgmA-ylpl9_Kyq7pU3c30e80KrvXX5T0MTCSiIKRkI/s320/Lance+headshot+development+effort+1200x.jpg" width="320" /></a></div>
If we test the above checks in an automated micro test, we avoid paying the biggest (by 80%) cost of software development, the cost of maintenance.<br />
<ul style="text-align: left;">
<li>No manual labor is used every iteration to confirm that this code is working as designed.</li>
<li>It takes essentially no time to execute a test.</li>
<li>We can use this test countless of times before shipping our product to confirm that everything is working as we designed it.</li>
<li>No manual labor will be necessary to later debug the code to track down why it stopped working.</li>
<li>The micro test acts as documentation of our code. And if we break our code, our documentation will tell us there is a problem.</li>
</ul>
Create the following micro test in AlertTest.gs. Google Application Script Test <a href="https://github.com/lancerkind/gast/blob/master/README.md" target="_blank">GAST</a> is used in this article (you'll need to install it or copy and paste the library into a script in your GAS project). It's an alright framework whose main advantage is that it's simple. <a href="https://github.com/simula-innovation/qunit/tree/gas/gas" target="_blank">QUnit for GAS</a> is a more "feature-ful" alternative for doing Google Application Script (GAS) work. (Due to a unique situation, simpler was better for the GAS development I was involved with.)<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
function alertTest(){</blockquote>
<blockquote class="tr_bq">
test('methodToTest and the test scenario.<method test="" to=""><test scenario="">', function(assert){</test></method></blockquote>
<blockquote class="tr_bq">
// Arrange what we must to get the test ready</blockquote>
<blockquote class="tr_bq">
// Act on the code that must be tested</blockquote>
<blockquote class="tr_bq">
// Assert what the results should be </blockquote>
<blockquote class="tr_bq">
})</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
Run allertTest and check the logs (in the scripts editor select View->Logs) for the output. You should see the following:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ1RQvx7Pu-hvKo9sOSo6wEl9FFTMH02rKvVVFzsYulHl6idTTMUpvzMKuhlAAWkEM2zJ_esO85fYbPuhd3rT3nVR5OxMbcNC3I336dylJzaGUrSMvSqcLqBFBXAVMaS9wEyEv5RQ2BaA/s1600/Screen+Shot+2019-02-04+at+5.23.03+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="334" data-original-width="1060" height="125" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ1RQvx7Pu-hvKo9sOSo6wEl9FFTMH02rKvVVFzsYulHl6idTTMUpvzMKuhlAAWkEM2zJ_esO85fYbPuhd3rT3nVR5OxMbcNC3I336dylJzaGUrSMvSqcLqBFBXAVMaS9wEyEv5RQ2BaA/s400/Screen+Shot+2019-02-04+at+5.23.03+PM.png" width="400" /></a></div>
If the above is essentially you see, then your environment is in good shape. Now we start doing the three steps to implementing a mock object.<br />
<h4 style="text-align: left;">
Step 1, find a way to inject a mock</h4>
<div style="text-align: left;">
The fragment from Alert.gs has our mocking target highlighted.</div>
<blockquote class="tr_bq">
function showAlert() {</blockquote>
<blockquote class="tr_bq">
var ui = <b>SpreadsheetApp</b>.getUi(); </blockquote>
<blockquote class="tr_bq">
...</blockquote>
Our code is using the SpreadsheetApp global object (a Singleton) could be a problem. So I write down on a piece of paper: worry about SpreadsheetApp. Since we don't know if this is a big deal yet, we continue reading the code.<br />
<blockquote class="tr_bq">
var result = <b>ui.alert</b>(</blockquote>
<blockquote class="tr_bq">
'Please confirm',</blockquote>
<blockquote class="tr_bq">
'Are you sure you want to continue?',</blockquote>
<blockquote class="tr_bq">
ui.ButtonSet.YES_NO);</blockquote>
Now we find our UI code which is what we want to test. So the question in my mind is: how to inject a mock ui object here? It's being accessed via a variable called "ui." Where did "ui" come from?<br />
<br />
<blockquote class="tr_bq" style="text-align: left;">
</blockquote>
<br />
<blockquote class="tr_bq" style="text-align: left;">
<div style="margin: 0px;">
var ui = <b>SpreadsheetApp</b>.getUi(); </div>
</blockquote>
It came from our Singleton. OK. On my paper I underline SpreadSheetApp and add the following comment.<br />
<blockquote class="tr_bq">
var ui = <b>SpreadsheetApp.getUi</b>(); // inject a mock for UI here!</blockquote>
Great! Now how to do it? It turns out that javascript being a dynamic language lets us do this in many ways. Simply refactoring the code to the following will not bother any of the callers of the showAlert function.<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
function showAlert(<b>uiMock</b>) {</blockquote>
<blockquote class="tr_bq">
var ui = <b>(uiMock == null ? SpreadsheetApp.getUi() : uiMock); </b></blockquote>
<div>
...</div>
</blockquote>
As with any refactoring, you should test it. Since we don't yet have a micro test you'll need to do a manual test. Execute showAlert function, switch back to the Google Sheet you embedded the script into so you can see the dialog and click "yes" or "no."<br />
<h4 style="text-align: left;">
Step 2, Design a minimal mock object that achieves the necessary goals </h4>
We don't want our micro tests to be anywhere as complicated as our product code, so they must test only one scenario and do it as simply as possible. It's best to not need mock objects at all, but we need SOMETHING to take place of the real UI object which SpreadSheetApp normally returns.<br />
<div>
So we are committed to that. Now what would be the minimal functions that this mock object must provide? Go read the code and see what is called upon the <b><i>ui</i></b> variable.</div>
<div>
<blockquote class="tr_bq">
<blockquote class="tr_bq">
var result = ui.<b>alert</b>(</blockquote>
<blockquote class="tr_bq">
'Please confirm',</blockquote>
<blockquote class="tr_bq">
'Are you sure you want to continue?',</blockquote>
<blockquote class="tr_bq">
ui.<b>ButtonSet</b>.<b>YES_NO</b>);</blockquote>
<blockquote class="tr_bq">
<br /></blockquote>
<blockquote class="tr_bq">
// Process the user's response.</blockquote>
<blockquote class="tr_bq">
if (result == ui.<b>Button.YES</b>) {</blockquote>
<blockquote class="tr_bq">
// User clicked "Yes".</blockquote>
<blockquote class="tr_bq">
ui.<b>alert</b>('Confirmation received.');</blockquote>
<blockquote class="tr_bq">
} else {</blockquote>
<blockquote class="tr_bq">
// User clicked "No" or X in the title bar.</blockquote>
<blockquote class="tr_bq">
ui.<b>alert</b>('Permission denied.');</blockquote>
<blockquote class="tr_bq">
}</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
On a piece of paper I write down the items bolded above. Our mock ui needs to have a function for alert, return a ButtonSet property, and a Button property. These are things we need to put into the <b>Arrange</b> section of our micro test. We are ready to implement our first micro test case.<br />
<h4 style="text-align: left;">
Step 3, use the mock in an automated micro test</h4>
<div>
Let's build this micro test capability one simple test case at a time. (Each function named "test" is a "test case.") Among our list of Checks for Alert, let's select the first, which is to check the prompt text. Roughly, the following is what we want to do.</div>
<blockquote class="tr_bq">
<blockquote class="tr_bq">
function alertTest(){</blockquote>
<blockquote class="tr_bq">
test('<method test="" to=""> <test scenario="">', function(assert){</test></method></blockquote>
<blockquote class="tr_bq">
// Arrange what we must to get the test ready</blockquote>
<blockquote class="tr_bq">
var mockUI = {}</blockquote>
<blockquote class="tr_bq">
// Act on the code that must be tested</blockquote>
<blockquote class="tr_bq">
showAlert(mockUI)</blockquote>
<blockquote class="tr_bq">
// Assert what the results should be</blockquote>
<blockquote class="tr_bq">
<b>// assert.equal(, 'Please confirm','')</b></blockquote>
<blockquote class="tr_bq">
})</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
To finish what's bolded, the assert line, we need to get our mock object to "spy on" what happens when showAlert does the highlighted section.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcaGcbcNtuxMtU0dm-y1X9x0nDEK438TVxsu3j9WO8FfINbhrJG1idsEq21nNnkls1u8rP0kmY3xu0Iyam0jVsox8u0BeMdrrvR0ApFZ4wcBbPIuipOZvxIfHWsDqML3nLsjwYpSbE7v4/s1600/Screen+Shot+2019-02-04+at+6.41.41+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="570" data-original-width="914" height="399" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcaGcbcNtuxMtU0dm-y1X9x0nDEK438TVxsu3j9WO8FfINbhrJG1idsEq21nNnkls1u8rP0kmY3xu0Iyam0jVsox8u0BeMdrrvR0ApFZ4wcBbPIuipOZvxIfHWsDqML3nLsjwYpSbE7v4/s640/Screen+Shot+2019-02-04+at+6.41.41+PM.png" width="640" /></a></div>
We can implement a spy in our mock object thusly and finish up our assert:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4b9wor2rTlEhEFblnO6u-5vaP6OUxEpji5vTi8Icr23cPyfAzxerSn7jTacCrZhWL00KbdmhDuWRCvlGxzr3kIOkcF1IYoA0De80MEQD3LrUsOrVtW4V_0KgSs_nwCtL3zGtvC0tsWY8/s1600/micro+test+with+spies.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="648" data-original-width="1114" height="371" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4b9wor2rTlEhEFblnO6u-5vaP6OUxEpji5vTi8Icr23cPyfAzxerSn7jTacCrZhWL00KbdmhDuWRCvlGxzr3kIOkcF1IYoA0De80MEQD3LrUsOrVtW4V_0KgSs_nwCtL3zGtvC0tsWY8/s640/micro+test+with+spies.jpg" width="640" /></a></div>
Update the first argument to the test function (method_to_test and test scenario) with what we are testing: 'showAlert prompt message is correct.'<br />
Go back and look at our list of items that are called upon our ui variable. Although we've only implemented one of the three, simply run the micro test and discover via the error message or log to see what that function needs. (I run the micro test now rather than assume my list is correct because sometimes I discover something new.)<br />
I confirm it does need the ButtonSet implemented, and so I add it.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_-BbJvGexgP6raRMtJ33zPQmpOMwFFpTqFJS3U-ylo1_rdUPVduDz7IC7J1V2UhS5CRJduc44cGJCoKUt2BPurwXEPc54RQicNausm0rOZ6iTP6MhvjV2Mx2mZbdQRbEecPXsjJ9B0sU/s1600/Screen+Shot+2019-02-04+at+7.03.37+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="202" data-original-width="874" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_-BbJvGexgP6raRMtJ33zPQmpOMwFFpTqFJS3U-ylo1_rdUPVduDz7IC7J1V2UhS5CRJduc44cGJCoKUt2BPurwXEPc54RQicNausm0rOZ6iTP6MhvjV2Mx2mZbdQRbEecPXsjJ9B0sU/s640/Screen+Shot+2019-02-04+at+7.03.37+PM.png" width="640" /></a></div>
Running the test again fails with a TypeError because it's trying to read someone's YES property. The logs reveal the "someone" is on line 10 of Alert which for me is the following:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgatLvB6NpNI7I2-DofB7FKT8Cf87n8a-0exoFtUaHJ0y-_MeyPHi9ubFibWOEA1v5G8MgOYRkZpwF8zerYpObeZVld7gWOrWgvFdjOjv8PygfHy79GZvqU7jAoICDymvadC0CykP2qVHo/s1600/Screen+Shot+2019-02-04+at+7.07.16+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="158" data-original-width="566" height="111" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgatLvB6NpNI7I2-DofB7FKT8Cf87n8a-0exoFtUaHJ0y-_MeyPHi9ubFibWOEA1v5G8MgOYRkZpwF8zerYpObeZVld7gWOrWgvFdjOjv8PygfHy79GZvqU7jAoICDymvadC0CykP2qVHo/s400/Screen+Shot+2019-02-04+at+7.07.16+PM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
So we add the "Button" property to our mock object.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ_a3faRRBh8yu802bjaYdPWnb_GpcenRS7CN5i_N3MPiHKT8STo5U6agtBKpDEvcsFL-rpuVDXyakU2bG0i_ghEXNMlVFayHhE-IQXdAtMJ2PQ4_95z9Pmm1iPLZ7J3TqId3a-9f5afw/s1600/Screen+Shot+2019-02-04+at+7.10.12+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="240" data-original-width="886" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ_a3faRRBh8yu802bjaYdPWnb_GpcenRS7CN5i_N3MPiHKT8STo5U6agtBKpDEvcsFL-rpuVDXyakU2bG0i_ghEXNMlVFayHhE-IQXdAtMJ2PQ4_95z9Pmm1iPLZ7J3TqId3a-9f5afw/s640/Screen+Shot+2019-02-04+at+7.10.12+PM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
Run the test and now it passes. To confirm it is working correctly, inject a bug by change the highlighted line in the product code and observe the test fail:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi31xMTg9nrHRbZ9ER9csA_qqgrI7SbAAuPL5ndOR1G-W9WumqZC2TkFkMkuUp503YAgXweAR3-L3gnJWSAdR_UxHF_liKKjaieQjXLN_XGBW670zd-Kt61kyWb2GrmSWLcXhSp4d1HTZ4/s1600/Screen+Shot+2019-02-04+at+7.12.10+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="168" data-original-width="608" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi31xMTg9nrHRbZ9ER9csA_qqgrI7SbAAuPL5ndOR1G-W9WumqZC2TkFkMkuUp503YAgXweAR3-L3gnJWSAdR_UxHF_liKKjaieQjXLN_XGBW670zd-Kt61kyWb2GrmSWLcXhSp4d1HTZ4/s400/Screen+Shot+2019-02-04+at+7.12.10+PM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWy5Z0TLfyYaprbPPy97g_nsvCVuhC2UUzwWJ49hgjMs66_W_IV9y9vTMdbL_tdFqE5D8Xy3GhZhZ2i6p8YRVCyqVeeliDjW4KJTp2U6EITIF31yBf5qXW9Amq1-i5w-4uKJmCQQeP_wo/s1600/Screen+Shot+2019-02-04+at+7.12.52+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="176" data-original-width="1162" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWy5Z0TLfyYaprbPPy97g_nsvCVuhC2UUzwWJ49hgjMs66_W_IV9y9vTMdbL_tdFqE5D8Xy3GhZhZ2i6p8YRVCyqVeeliDjW4KJTp2U6EITIF31yBf5qXW9Amq1-i5w-4uKJmCQQeP_wo/s640/Screen+Shot+2019-02-04+at+7.12.52+PM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Go ahead and remove the bug, run the test and observe it passes.</div>
<h2 style="text-align: left;">
Continue and Share</h2>
<div>
Go ahead and implement all the checks for this test by adding more asserts and spies. At some point you'll need to add another "test" function because the "arrange" section you have will be focused on one scenario and you'll need to "arrange" a different scenario for some of the checks.<br />
<br />
Share your solution and I'll be happy to give you feedback. Take a picture of the test automation you built and tweet it to: @LancerKind. I'll give feedback. Go for it because your solution may be better than mine.<br />
<br />
Cheers,<br />
Coach</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />
<br />
<br />
<br />
<br /></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-69377525611345248642019-01-09T13:51:00.001-08:002019-06-11T10:53:03.779-07:00Learning JavaScript<div dir="ltr" style="text-align: left;" trbidi="on">
JavaScript is the world's most popular programming language. With the advent of ECMA standard 6 (ES6), the JavaScript language also got a pretty modern facelift. Since JavaScript integrates with a lot of different concerns, from web pages to servers, there are a lot of ways to get started.<br />
<h2 style="text-align: left;">
Here are a few pathways along with tutorials or readings</h2>
http://www.tothenew.com/blog/how-to-test-google-apps-script-using-qunit/<br />
https://github.com/simula-innovation/qunit/tree/gas/gas<br />
<h3>
Useful Intro Reference for All JavaScript paths</h3>
<div>
Everyone works with strings, arrays, sets, and functions. Become familiar with these libraries:</div>
<div>
<ul>
<li><a href="https://codeburst.io/javascript-double-equals-vs-triple-equals-61d4ce5a121a" target="_blank">What Kind of Equivalence do you mean?: == versus ===</a></li>
<li><a href="https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/" target="_blank">The 6 ways to declare a function</a></li>
<li><a href="https://www.blogger.com/:%20https://dev.to/frugencefidel/10-javascript-string-methods-you-should-know-4l76" target="_blank">Javascript built in String functions</a></li>
<li><a href="https://www.thoughtco.com/javascript-by-example-use-of-the-ternary-operator-2037394" target="_blank">ternary operator</a></li>
<li><a href="https://www.blogger.com/:%20http://frugencefidel.com/blogs/10-javascript-array-methods-you-should-know" target="_blank">Arrays</a></li>
<li>Concept: <a href="https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0" target="_blank">What is a reference, what is a value</a>.</li>
</ul>
</div>
<h3 style="text-align: left;">
Google Docs Automation:</h3>
<div>
Google uses a version of JavaScript for automating Google applications. This language has become known as GAS.</div>
<div>
<ul>
<li>Your first time: https://www.benlcollins.com/spreadsheets/starting-gas/</li>
<li>https://developers.google.com/apps-script/overview</li>
</ul>
<div>
Here is a simple and easy to get started micro test library so you can do Test Driven Development or micro testing of your GAS code:</div>
</div>
<div>
<ul>
<li>https://github.com/huan/gast</li>
</ul>
</div>
<h4>
Advanced:</h4>
<div>
Sending your form data into a database:</div>
<div>
<ul>
<li>https://www.youtube.com/watch?v=or78bBOeFU0</li>
<li>https://www.dataeverywhere.com/use-database-forms</li>
</ul>
</div>
<h3 style="text-align: left;">
Basic HTML and JavaScript route:</h3>
<ul>
<li>https://dev.to/programliftoff/create-a-basic-webpage-with-css-and-javascript--104i</li>
<li>https://medium.com/@blondiebytes/how-to-create-interactive-websites-with-javascript-627a6d998fed</li>
</ul>
<h3 style="text-align: left;">
Advanced Web App development</h3>
Pick a framework and start learning. I recommend going "the long road" by investing in learning NodeJS instead of going straight to a web UI library such as ReactJS. Learning how to build and create with NodeJS pays dividends for everything else you learn with javascript. The information below suggests how to "discover" a link because most direct links I can provide will get out of date in twelve to twenty-four months.<br />
<br />
NodeJS: for building a service<br />
<ul style="text-align: left;">
<li><a href="https://nodejs.org/en/download/" target="_blank">Install a NodeJS environment</a> </li>
<li>Build something: search on YouTube "build first nodejs app"</li>
<li>Build a single page app or a web service </li>
</ul>
<br />
ReactJS: for building a web UI<br />
<ul style="text-align: left;">
<li><a href="https://reactjs.org/docs/getting-started.html" target="_blank">Install React JS environment</a> </li>
<li>Learn how to make a single page application. </li>
<ul>
<li><a href="https://reactjs.org/docs/getting-started.html" target="_blank">ReactJS getting started</a> and get something working that creates a simple UI. If you get stuck in installing your own development environment, you can also use <a href="https://reactjs.org/docs/getting-started.html#online-playgrounds" target="_blank">online Playgrounds</a> until you work out what's wrong with your own environment.</li>
<li>Another source of tutorials is to search youtube for "Single Page App React" and <u>find something less than two years old.</u></li>
</ul>
<li>Learn how to connect your single page application to a web service. You can find tutorials on Youtube by searching for "react connect to webservice." You can connect to existing web services. NodeJS is a good way to build your own web service.</li>
<li>Learn how to connect your application to a datasource. Search Youtube for : React connect to database getting started.</li>
<li>Redux for managing your view updates with a datasource. Search Youtube for : React Redux getting started and <u>find something less than two years old.</u></li>
</ul>
<div>
<h3 style="text-align: left;">
General JavaScript programming:</h3>
<div>
Unless you're writing Google Application Scripts, learn how to form your code with the latest standard (ES6 at this moment). I'm a fan of using NodeJS for general JavaScript programming.</div>
<ul>
<li><a href="https://nostarch.com/ecmascript6" target="_blank">ES6 reference</a> </li>
<li><a href="https://stackoverflow.com/questions/8363564/ways-to-create-a-set-in-javascript" target="_blank">Working with Sets</a></li>
<li><a href="https://javascript.info/constructor-new" target="_blank">How to create an object constructor</a></li>
<li><a href="https://toddmotto.com/deprecating-the-switch-statement-for-object-literals/" target="_blank">Use Object literals rather than Switch statements</a></li>
<li><a href="https://nodejs.org/en/" target="_blank">NodeJS</a></li>
<li><a href="https://nodejs.org/en/docs/guides/getting-started-guide/" target="_blank">Getting started with NodeJS</a></li>
<li><a href="https://toddmotto.com/deprecating-the-switch-statement-for-object-literals/" target="_blank">Fixing common legacy code issues due to switch statements</a></li>
<li><a href="https://toddmotto.com/deprecating-the-switch-statement-for-object-literals/" target="_blank">Enums</a> (caution, enums tend to create poorly designed code that uses switch statements)</li>
</ul>
</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com15tag:blogger.com,1999:blog-284399302764909932.post-46044858736414479952018-01-07T15:19:00.000-08:002019-12-19T06:46:32.512-08:00Getting JUnit 5 into your Eclipse 4.7 Oxygen<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
Good Things Merit New Releases</h2>
Like Java, JUnit versions continue their onward march. JUnit 5 is a more modularized version of JUnit 4, and supports and requires Java 8.<br />
<br />
This is what entails a bundle of JUnit 5:<br />
<br />
<ul style="text-align: left;">
<li>JUnit Platform – which enables launching testing frameworks on the JVM</li>
<li>JUnit Jupiter – which contains new features for writing tests in JUnit 5</li>
<li>JUnit Vintage – which provides support for running JUnit 3 and JUnit 4 tests on the JUnit 5 platform</li>
</ul>
https://www.eclipse.org/modeling/downloads/build-types.php<br />
<br />
My situation was more complicated as I am using Spring Tool Suite rather than straight Eclipse.<br />
<br />
Updated my build using second bullet at: https://marketplace.eclipse.org/content/junit-5-support-oxygen#group-details<br />
Notice the bit about clearing a checkbox regarding categories.<br />
<blockquote class="tr_bq">
Update your Eclipse 4.7 build using this update site: http://download.eclipse.org/eclipse/updates/4.7-U-builds.<br />
In Eclipse, go to Help > Install New Software... an<b>d uncheck "Group items by category". </b>Select "Eclipse SDK" from the list of items and proceed with the installation.</blockquote>
If you were using Spring STS3.9, your splash screen will change. I couldn't use my usual MacOS icon and instead had to go to a terminal, navigate to my Spring install, and launch it with "./springsource/STS.app/Contents/MacOS/eclipse" For me, my STS was stored in my home directory so navigate there and find a "springsource" directory.<br />
<br />
It takes a few minutes for the IDE to move beyond the splash screen. Be aware there may be a modal popup about updating your workspace hiding somewhere.<br />
Don't be alarmed. It's still STS and as far as I could tell, everything was working with the exception of the Mac launch application and the splash screen change.<br />
<br />
(XXX Did I really need to go and install JUnit 5 Support or was it already in that 4.7-U-builds bundle?)<br />
<br />
After doing this, I was able to install "JUnit 5 Support for Oxygen" plugin by using STS to go to the marketplace, and then dragging the "install button" from https://marketplace.eclipse.org/content/junit-5-support-oxygen#group-details and dropping it onto the marketplace.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEYrgPl0Ku34_7Gzx91URAQoxAH_8bHxh_y3JLct9Am62ZbF-rhiWshIGusSMYsz1LZDiYQtCFoaDp7vwPZC3hNoqfziUPA1Oiv5jPHGjgPoPfJF8tAhbjmDjjZPlKnT4PT4xLaAv8xlE/s1600/Screen+Shot+2017-09-20+at+3.38.51+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="612" data-original-width="1012" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEYrgPl0Ku34_7Gzx91URAQoxAH_8bHxh_y3JLct9Am62ZbF-rhiWshIGusSMYsz1LZDiYQtCFoaDp7vwPZC3hNoqfziUPA1Oiv5jPHGjgPoPfJF8tAhbjmDjjZPlKnT4PT4xLaAv8xlE/s320/Screen+Shot+2017-09-20+at+3.38.51+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
And then:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ZreUiiL7z7qzqm752Oe5RHHSkxLb4qnsFinLtnUznItH8qCoW26_Pd95STwOyaa_jz9AVrRJs0wBI1e8q_XcC9T6NADgP6HhsNuv4WWEvnv4bjtiSSIZ250QtnZJHR5wjmSd5KZW5vY/s1600/Screen+Shot+2017-09-20+at+3.37.40+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="584" data-original-width="1168" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ZreUiiL7z7qzqm752Oe5RHHSkxLb4qnsFinLtnUznItH8qCoW26_Pd95STwOyaa_jz9AVrRJs0wBI1e8q_XcC9T6NADgP6HhsNuv4WWEvnv4bjtiSSIZ250QtnZJHR5wjmSd5KZW5vY/s320/Screen+Shot+2017-09-20+at+3.37.40+PM.png" width="320" /></a></div>
<br />
After rebooting, check that it was installed by going back to the Marketplace and selecting the installed filter.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigabJkE9P3hMS8NaXUJUU_PZatpgz0IcHrkhtu5-61nVEB9d0sdxIswzFmETfPDz2BzJCBbBHHauVTfFMFYD1-0SC_nipEzE1KMZidb8wkDpseQu6QTVkZxF3G6SjGhd5cUi7CB4gsa_Y/s1600/Screen+Shot+2017-09-20+at+3.49.00+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="798" data-original-width="1172" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigabJkE9P3hMS8NaXUJUU_PZatpgz0IcHrkhtu5-61nVEB9d0sdxIswzFmETfPDz2BzJCBbBHHauVTfFMFYD1-0SC_nipEzE1KMZidb8wkDpseQu6QTVkZxF3G6SjGhd5cUi7CB4gsa_Y/s320/Screen+Shot+2017-09-20+at+3.49.00+PM.png" width="320" /></a></div>
<br />
<br />
<br />
Add a Junit Test case to your source and you'll see JUnit Jupiter below as one of the radio buttons.<br />
(XXX At the beginning introduce the JUnit5 ecosystem: JUnit5, Jupiter, ....)<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzFUCRi1cOUH4VO2yO_CG09iDZ2CST3-_04cCDGttuKIRanHyRAidMqIbgGWIr9r7_amHD0n7UkrvaGrAC5C-jbT9hzshdwaE2CWu-GpGNqFzyTORGYYj20Vl-qUlAJXkK18oW9x3Dx64/s1600/Screen+Shot+2017-09-20+at+4.26.04+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="474" data-original-width="870" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzFUCRi1cOUH4VO2yO_CG09iDZ2CST3-_04cCDGttuKIRanHyRAidMqIbgGWIr9r7_amHD0n7UkrvaGrAC5C-jbT9hzshdwaE2CWu-GpGNqFzyTORGYYj20Vl-qUlAJXkK18oW9x3Dx64/s320/Screen+Shot+2017-09-20+at+4.26.04+PM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkiNEGg7oi6gy5DOJXwqSqUVlK2NEKhRqg6gA4RkdzFOMfQ4TzKhSBSdksuAcnzyLN4S6qu526gYauUUUhuEw_lU94ouLKZC81tVO0Zvl7YE3YS0hNxXGCgY4LDhbMd8OckYvIFLZ98f8/s1600/Screen+Shot+2017-09-20+at+4.26.15+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1000" data-original-width="640" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkiNEGg7oi6gy5DOJXwqSqUVlK2NEKhRqg6gA4RkdzFOMfQ4TzKhSBSdksuAcnzyLN4S6qu526gYauUUUhuEw_lU94ouLKZC81tVO0Zvl7YE3YS0hNxXGCgY4LDhbMd8OckYvIFLZ98f8/s320/Screen+Shot+2017-09-20+at+4.26.15+PM.png" width="204" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD8-sq8gqRYwMfFYiFfZwRHJiWLNpIc-u-4bjFpNgEkw6RD48QIvAQfTXXb8Np9bizR-_6XMGJkvWDq8l1kBsMWaEKsbcOi6GC1U5uKMYa8wxoYhs-85t4-Q0G606sJIr8nG82_p47lww/s1600/Screen+Shot+2017-09-20+at+3.55.51+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1196" data-original-width="1048" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD8-sq8gqRYwMfFYiFfZwRHJiWLNpIc-u-4bjFpNgEkw6RD48QIvAQfTXXb8Np9bizR-_6XMGJkvWDq8l1kBsMWaEKsbcOi6GC1U5uKMYa8wxoYhs-85t4-Q0G606sJIr8nG82_p47lww/s320/Screen+Shot+2017-09-20+at+3.55.51+PM.png" width="280" /></a></div>
Upon clicking finish you'll be asked about adding JUnit5 to your build path.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgSYYKsJ0KmtnRg3M6fHBqMcu5pcc2ufdRLZ1W70jbuCpwTar2OORa62FopBr2-pXn5cvZKCoL7H9iaQq47lbf0hQ4ZiM1xYIcYVgHCeNXPNqnsoa3Xjg4IlmhLyFiQvOLWQrTZ9ze-7M/s1600/Screen+Shot+2017-09-20+at+4.26.52+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="782" data-original-width="1052" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgSYYKsJ0KmtnRg3M6fHBqMcu5pcc2ufdRLZ1W70jbuCpwTar2OORa62FopBr2-pXn5cvZKCoL7H9iaQq47lbf0hQ4ZiM1xYIcYVgHCeNXPNqnsoa3Xjg4IlmhLyFiQvOLWQrTZ9ze-7M/s320/Screen+Shot+2017-09-20+at+4.26.52+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This is the template test case it inserted. Notice the org.junit.jupiter namespace. Let's run it and see if it fails.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOzlh6mnAIWEEdIwR_T77-RiQe4u30WV-o2c4SDT16S6B9fFI4XUTZJrXA8kM7L8f_wCSefb0dmI1iO_WDRlEuT-fNatknaMeCpIud2pHDmer0guZ6tm7hHxnS08lsdRWuGAdrKo1E9OU/s1600/Screen+Shot+2017-09-20+at+4.28.13+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="464" data-original-width="690" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOzlh6mnAIWEEdIwR_T77-RiQe4u30WV-o2c4SDT16S6B9fFI4XUTZJrXA8kM7L8f_wCSefb0dmI1iO_WDRlEuT-fNatknaMeCpIud2pHDmer0guZ6tm7hHxnS08lsdRWuGAdrKo1E9OU/s320/Screen+Shot+2017-09-20+at+4.28.13+PM.png" width="320" /></a></div>
<br />
Like you've always done with JUnit, you can select the project name or the single test case, then execute tests with the context menu.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7gXcykRMmcLSE0wTI2rW_nSaQU1OB9qya-02Xc2ktQvu42mHHBLsWn0i8sXbrZVMOk9KL-gcHSSJoyd0doXco7TazyQ73OV5EvfOmCxS1uz1_J5XCyAwkApCE2MwKgHfl6nK6cAmwqz8/s1600/Screen+Shot+2017-09-20+at+4.27.58+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="850" data-original-width="1138" height="239" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7gXcykRMmcLSE0wTI2rW_nSaQU1OB9qya-02Xc2ktQvu42mHHBLsWn0i8sXbrZVMOk9KL-gcHSSJoyd0doXco7TazyQ73OV5EvfOmCxS1uz1_J5XCyAwkApCE2MwKgHfl6nK6cAmwqz8/s320/Screen+Shot+2017-09-20+at+4.27.58+PM.png" width="320" /></a></div>
And you should see the below red bar since the test case template has a "fail()" statement.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja8EvjftBgMXENKNGeNdGeP2eBQSyTdItvoz2V3z5a9iiWCU815ojLO12sW9r7FQ1Nj3ZmaSR-8QJjuoBYz7pYliR9HJ88uV7cyKUeAQv4WmYXJOKM2pNagH2LLGzmCCUift_YljoVUUY/s1600/Screen+Shot+2017-09-20+at+4.28.25+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="400" data-original-width="1600" height="80" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja8EvjftBgMXENKNGeNdGeP2eBQSyTdItvoz2V3z5a9iiWCU815ojLO12sW9r7FQ1Nj3ZmaSR-8QJjuoBYz7pYliR9HJ88uV7cyKUeAQv4WmYXJOKM2pNagH2LLGzmCCUift_YljoVUUY/s320/Screen+Shot+2017-09-20+at+4.28.25+PM.png" width="320" /></a></div>
<h2 style="text-align: left;">
By the way: regarding TDD</h2>
Check out the <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/">Agile Thoughts podcast</a>. This podcast gets the Confessions Of An Agile Coach's endorsement (naturally, since the same people produce it) of quality materials for developers and teams trying to get coding done in a way that avoids dealing with legacy issues such as bugs and hard to maintain code. Agile Thoughts has a lot of great TDD conceptual content along with a radio drama about the difficulties to getting a TDD initiative started. <b>Click the podcast cover below or <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" target="_blank">here</a> for more information.</b><br />
<br />
<span id="goog_2100212554"></span><span id="goog_2100212555"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Agile Thoughts podcast" border="0" data-original-height="1024" data-original-width="1024" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaaNk_KY2gesIbqVamAc8uqQuQLm3Y5hG59YEAGFyAdcyX1z73ADGxBVP6y66irUWgY90Vm5C5gDfq1C2YlME-gfsSR-Yv9nGq8pCn0oX6y4K3-mvzpfpkOZTIwAdHcBydjSXDbFw1kPg/s400/UNADJUSTEDNONRAW_thumb_39.jpg" title="" width="400" /></a></div>
<br />
<h2 style="text-align: left;">
References</h2>
A Look at JUnit 5’s Core Features & New Testing Functionality https://stackify.com/junit-5/<br />
JDT UI/JUnit 5 https://wiki.eclipse.org/JDT_UI/JUnit_5<br />
<br />
<br /></div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com1tag:blogger.com,1999:blog-284399302764909932.post-81125041797465558332017-08-12T21:33:00.000-07:002019-12-19T07:05:27.297-08:00Developers, confess why you don't TDD<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="font-family: '.sf ui text'; font-size: 17px; line-height: normal;">
<div class="separator" style="clear: both; color: #454545; text-align: center;">
</div>
<div class="separator" style="clear: both; color: #454545; text-align: center;">
<a href="https://agilenoir.biz/wp-content/uploads/2017/08/IMG_2918.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="430" data-original-width="570" height="150" src="https://AgileNoir.biz/wp-content/uploads/2017/08/IMG_2918.jpg" width="200" /></a></div>
<div style="text-align: left;">
<span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;">Dish me the dirt and give it to me straight. Don't hide anything. Just lay it out there: why don't you do TDD? TDD has been fighting for mainstream acceptance since the late nineties with the introduction of eXtreme Programming. It's still not there yet. Today, at least everyone knows what the term TDD is but really so few actually know how to do it.</span></div>
</div>
<div style="font-family: '.sf ui text'; font-size: 17px; line-height: normal; text-align: left;">
<span style="color: #cccccc;"><span style="font-family: ".sfuitext"; font-size: 17pt;">So I've put on a black shirt and twisted the collar on backwards, and I'm ready to take your confession. Later, I'll cover your reasons, my dear developer, o</span><span style="font-family: ".sfuitext"; font-size: 17pt;">n Agile Thoughts podcast </span><span style="font-family: ".pingfangsc-regular"; font-size: 17pt;">(<a href="http://agilenoir.biz/series/agile-thoughts/"><span style="font-family: ".sfuitext"; font-size: 17pt;">https://agilenoir.biz/series/agile-thoughts/</span></a>) where </span><span style="font-family: ".sfuitext"; font-size: 17pt;">I'm doing a series on, brace yourself, "Why Devs don't TDD."</span></span></div>
<div style="line-height: normal;">
<h2 style="font-family: ".sf ui text"; font-size: 17px; text-align: left;">
<span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;">Update:</span></h2>
<div>
<div style="font-family: ".sf ui text"; font-size: 17px;">
<span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;"></span></div>
<div style="font-stretch: normal; line-height: normal; text-align: left;">
<div style="font-family: ".sf ui text"; font-size: 17.4px;">
<div style="color: #454545; font-family: ".sf ui text"; font-size: 17.4px;">
<span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;"><span style="font-family: ".sfuitext"; font-size: 17.41pt;">They are now live! Visit <a href="https://agilenoir.biz/en/agilethoughts/test-automation-pyramid-series/" target="_blank">Agile Thoughts</a> (<a href="https://agilenoir.biz/series/%E6%95%8F%E6%8D%B7%E7%90%86%E5%BF%B5/" target="_blank">敏捷理念</a>播客) and listen to these insightful, easy to understand, and entertaining episodes about TDD. At episode 14, I’m sure you’ll notice its, shall we say, singular style. At Agile Thoughts, we work hard to be Portlandia friendly. ;-)</span></span></div>
<div style="color: #454545; font-family: ".sf ui text"; font-size: 17.4px;">
<span style="color: #cccccc; font-family: ".sfuitext"; font-size: 23.213333129882813px;">Like</span><span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;"> a good TDD priest, I'll keep the "actors" anonymous and discuss what I learn in an entertaining monologue. So don't worry about your scandals and secrets. What is your or your team's excuse</span><span style="color: #cccccc; font-family: ".pingfangsc-regular"; font-size: 17pt;">?</span><span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;"> What's the fear or problem</span><span style="color: #cccccc; font-family: ".pingfangsc-regular"; font-size: 17pt;">?</span><span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;"> What's stopping you</span><span style="color: #cccccc; font-family: ".pingfangsc-regular"; font-size: 17pt;">?</span><span style="color: #cccccc; font-family: ".sfuitext"; font-size: 17pt;"> </span></div>
<br />
<ul style="text-align: left;">
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">009 Introducing the Test Driven Development series</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">010 Agile and TDD Neglect</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">011 The Old Way isn't Sustainable</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">012 An Example of doing TDD</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">013 Developer Intent and the Bible</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">014 Why Devs don’t TDD</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">015 The TDD FUD Spreader</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">016 Driving under the Influence of FUD</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">017 The Architect Disses TDD</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">018 TDD gets no Love from the PO</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">019 A QA Professional Questions Micro Tests and TDD</span></li>
<li><span style="color: #cccccc; font-size: 22.66666603088379px;">... and the series is still ongoing.</span></li>
</ul>
<br />
<div style="color: #454545; font-family: ".sf ui text"; font-size: 17.4px;">
<br /></div>
</div>
</div>
</div>
</div>
<div style="font-family: '.sf ui text'; font-size: 17px; line-height: normal;">
<span style="color: #cccccc;"><span style="font-family: ".sfuitext"; font-size: 17pt;">And hey, if</span><span style="font-family: ".sfuitext"; font-size: 17pt;"> you <b>are</b> doing TDD</span><span style="font-family: ".pingfangsc-regular"; font-size: 17pt;">,</span><span style="font-family: ".sfuitext"; font-size: 17pt;"> then why</span><span style="font-family: ".pingfangsc-regular"; font-size: 17pt;">?</span><span style="font-family: ".sfuitext"; font-size: 17pt;"> What got you going</span><span style="font-family: ".pingfangsc-regular"; font-size: 17pt;">?</span></span><br />
<span style="color: #cccccc; font-family: ".pingfangsc-regular"; font-size: 17pt;"><br /></span></div>
<div style="font-family: '.sf ui text'; font-size: 17px; line-height: normal;">
<span style="color: #cccccc; font-family: ".pingfangsc-regular"; font-size: 17pt;">Drop me the info either on the <a href="https://news.ycombinator.com/item?id=15001609" target="_blank">Hacker News posting </a>thread, or use the comment section below, or send me the "goods" via twitter: @LancerKind</span></div>
<div>
<span style="font-family: ".pingfangsc-regular"; font-size: 17pt;"><br /></span></div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com1tag:blogger.com,1999:blog-284399302764909932.post-71719820114158947862017-05-25T01:27:00.001-07:002017-09-05T22:05:05.491-07:00Excelling at CucumberJVM GLOBAL Step Definitions<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJQ0VF0eZgkD3PxS0w70GQCWbIm6_ggQUMU4oFXFDu-67_LjRrgA5iueO8ZBdpYnTbkMr00LfYR7oba4NSKNDKllkP-4ktD10c8fjzOyYUdglyOea9OejFt-7ckUiRxKHV0WYvNjDIj-A/s1600/cuke_banner_052516.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="141" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJQ0VF0eZgkD3PxS0w70GQCWbIm6_ggQUMU4oFXFDu-67_LjRrgA5iueO8ZBdpYnTbkMr00LfYR7oba4NSKNDKllkP-4ktD10c8fjzOyYUdglyOea9OejFt-7ckUiRxKHV0WYvNjDIj-A/s400/cuke_banner_052516.jpg" width="400" /></a></div>
Cucumber is going global baby! They've a vision that any and all definitions are available at the beck and call of any feature file. Other BDD implementations allow static linking of feature file to a specific set of code of definitions. Let's give the idea of global definitions a try with CucumberJVM (Java) and see what we can learn, cover tools and tricks to work with them, and look at how to design the definition code to be maintainable. The code is on <a href="https://github.com/lancerkind/CucumberAndDI" target="_blank">git hub</a> along with tag points for "before" and "after" refactoring.<br />
<h2 style="text-align: left;">
Feature files, Steps, Definitions, and Test Runners, oh my!</h2>
Feature files are text files containing the BDD (well really Gherkin) Steps such as Give, When, Then. Definitions are built in programming languages and define what those steps mean. Test Runners (such as JUnit, or <i>cucumber</i> command line) launch a program that looks for feature files, parses the feature file, and execute each Step by executing a Definition that matches the Step. BDD Test frameworks usually allow some configuration of how to match a Step with a Definition. Cucumber's vision is that all Definitions should be global and that the feature file should contain enough context to do this correctly.<br />
<h2 style="text-align: left;">
Feature file for planning</h2>
This is the feature file used in planning. It reads pretty well and gives a team a starting point for conversations on a point of sales feature for a pet store.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAXdl2kDplgAGPLJUjtTkzkOm9RfhiwAHa9-jv6f0cwa1SfolqOJZqH2by3rS2jUUjZToyw8PS2TzSJhKzoqTu5o7ZjW4bkRRpCf6KipgaNrxXNsOFvAHFU3gZtuTyf2W2VmJdUfrvd18/s1600/Screen+Shot+2017-05-22+at+4.33.15+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAXdl2kDplgAGPLJUjtTkzkOm9RfhiwAHa9-jv6f0cwa1SfolqOJZqH2by3rS2jUUjZToyw8PS2TzSJhKzoqTu5o7ZjW4bkRRpCf6KipgaNrxXNsOFvAHFU3gZtuTyf2W2VmJdUfrvd18/s400/Screen+Shot+2017-05-22+at+4.33.15+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Buying a dog at a pet store such as Petco give you deals such as this. <br />
(click pic to enlarge)</td></tr>
</tbody></table>
After planning with a number of such documented features, Once the Sprint started, during development of test automation, I realized I needed more context. If the feature file alone was going to "drive" definition discovery, having descriptive columns wasn't going to give me enough differentiation across all steps in a global context. example:<br />
<blockquote class="tr_bq">
When purchasing a "selected accessory"</blockquote>
would generate a match for all other definitions with the words "When purchasing a," totally missing the important piece "selected accessory."<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUKQaUzyptaJuB3has9aKVF5MOewS3jZCPgebrISFH7ID9VvfGY5GjR1m_nDHmDYk3mFbY57t6fSTNUJ6liOeD_XLudnYbEIt-tpiqHWKZhtAzCQeXAyV-fzR_sAmPc923IW_obXuZbc4/s1600/Screen+Shot+2017-05-23+at+7.54.32+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUKQaUzyptaJuB3has9aKVF5MOewS3jZCPgebrISFH7ID9VvfGY5GjR1m_nDHmDYk3mFbY57t6fSTNUJ6liOeD_XLudnYbEIt-tpiqHWKZhtAzCQeXAyV-fzR_sAmPc923IW_obXuZbc4/s400/Screen+Shot+2017-05-23+at+7.54.32+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The Natural editor reporting multiple Definitions matching this Step.</td></tr>
</tbody></table>
<br />
Adjusting the feature file by pulling the descriptive columns "out" will allow us to work with global definitions.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXQAIblsXjavn3ZxIP1FzwnvjOKTv-fMwpIPOXAHcjVra2iNp4CUK5RLS5FvOJxR3kWyGdu3GT8bTB4UznjrysfadgJ-TTwN2hiym7iI_wwRiQDHDprLDAHcYSwMpmN4AB25NeOtLUKBc/s1600/Screen+Shot+2017-05-22+at+4.31.38+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXQAIblsXjavn3ZxIP1FzwnvjOKTv-fMwpIPOXAHcjVra2iNp4CUK5RLS5FvOJxR3kWyGdu3GT8bTB4UznjrysfadgJ-TTwN2hiym7iI_wwRiQDHDprLDAHcYSwMpmN4AB25NeOtLUKBc/s400/Screen+Shot+2017-05-22+at+4.31.38+PM.png" width="400" /></a></div>
<h3 style="text-align: left;">
How to know a Step has enough "closure?"</h3>
First off, we'll never be perfect as reality always brings new adventure. But you'll get closer faster by: reading each step alone, ignoring the context of the scenario title, feature file name, and feature file location. This is how I realized that "When purchasing a" had too little context as the nice context of the column name wasn't going to help me.<br />
<br />
So global definitions will make your feature files a little more wordy. And you will be forced to make decisions in the future when you discover collisions. Feature file editors like Natural will complain to you when you add one that has ambiguous definitions.<br />
<h2 style="text-align: left;">
Test Automation Design</h2>
First off, let's get a feedback loop working. (Code is on <a href="https://github.com/lancerkind/CucumberAndDI" target="_blank">GitHub</a>.) Put the feature file into source control, add a test runner (or if you got the cucumber plugin working in eclipse, that will work too), and execute your test. Observe that the feature file is executed but the scenarios are skipped as there are no definitions. Also the console will give you stub code for the methods.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIv0MBTasSDIdoNpQ47RvWnmZWo19bO3cHM26dQQPBeWpx3WlnhNNyqrBoEoFZqEJuAM4aYLtXMw2Zvx6zeaxnH2zNHVXPct5VRtguuNhV-XIHi6GHhJbRC0__mtV1TWUGLJmtpONfQnc/s1600/Screen+Shot+2017-05-23+at+6.48.58+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="60" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIv0MBTasSDIdoNpQ47RvWnmZWo19bO3cHM26dQQPBeWpx3WlnhNNyqrBoEoFZqEJuAM4aYLtXMw2Zvx6zeaxnH2zNHVXPct5VRtguuNhV-XIHi6GHhJbRC0__mtV1TWUGLJmtpONfQnc/s400/Screen+Shot+2017-05-23+at+6.48.58+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Organize feature files in a sensible hierarchy</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2VoSg_I9Ijz4-ApjJ9C4D6MioG5meiq2whvRdXp68bHwIWmv_jtYPVBkbID_nIHSHstMfJmyDgrQYgaWF4KwqXlbE0SV2px5WMMBDL4RoiESPngxI5RtDEqcPxXfOtG_X1wWLEBD0k3g/s1600/Screen+Shot+2017-05-23+at+6.48.08+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="105" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2VoSg_I9Ijz4-ApjJ9C4D6MioG5meiq2whvRdXp68bHwIWmv_jtYPVBkbID_nIHSHstMfJmyDgrQYgaWF4KwqXlbE0SV2px5WMMBDL4RoiESPngxI5RtDEqcPxXfOtG_X1wWLEBD0k3g/s320/Screen+Shot+2017-05-23+at+6.48.08+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">JUnit test runner</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi30EDfrsBAWbvrD92KYnRxJBjpUb38-gso5pfMxzEGQi3Sh7Fo-lwbNRUAhvuAeyxSXNiCuRIER9zVbd_vXFLHbkALfddt4VEI0yTJhAJdJ1pChxC-J_y-G8hWe6_JkojwaQl_U2ygxLI/s1600/Screen+Shot+2017-05-23+at+6.48.42+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="156" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi30EDfrsBAWbvrD92KYnRxJBjpUb38-gso5pfMxzEGQi3Sh7Fo-lwbNRUAhvuAeyxSXNiCuRIER9zVbd_vXFLHbkALfddt4VEI0yTJhAJdJ1pChxC-J_y-G8hWe6_JkojwaQl_U2ygxLI/s320/Screen+Shot+2017-05-23+at+6.48.42+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">JUnit test case which hands off to Cucumber</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvNcDWrys0p0xHq5EdoNCvu_KDIGtNaOL23qwHl2wcsLD_uD9Vdl25aqLUbdsTeqZn3kvmv1AK1scCA0_jix4Jry2vUSNNSzyL4r6d6BsUWZDo263LzdpC_jdowSs1H-whLIMkeAtjJdQ/s1600/Screen+Shot+2017-05-23+at+6.56.14+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="315" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvNcDWrys0p0xHq5EdoNCvu_KDIGtNaOL23qwHl2wcsLD_uD9Vdl25aqLUbdsTeqZn3kvmv1AK1scCA0_jix4Jry2vUSNNSzyL4r6d6BsUWZDo263LzdpC_jdowSs1H-whLIMkeAtjJdQ/s400/Screen+Shot+2017-05-23+at+6.56.14+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.800000190734863px;">Natural gives feedback that definitions are missing<br />
(If you add definitions, sometimes the feature file needs to<br />
be reopened to force Natural to repairs and check.)</td></tr>
</tbody></table>
Global definitions change how automation is designed and built. When using BDD tools that allow the developer to control linking Steps to Definitions, you'd typically see a one to one mapping of feature file to the java file containing the class of definitions:<br />
<blockquote class="tr_bq">
com/features/purchasing/BuyDog.feature<br />
com/features/purchasing/BuyDogStepDef.java<br />
com/feature/pageobjects/.... </blockquote>
With Cucumber, you're encouraged to build classes in this manner:<br />
<blockquote class="tr_bq">
com/features/purchasting/BuyDog.feature<br />
com/features/definitions/GivenBuysDog.java<br />
com/features/definitions/WhenBoughtSelectedAccessory.java<br />
com/features/definitions/ThenDiscount.java<br />
com/feature/pageobjects/.... </blockquote>
Although this explosion of smaller objects isn't necessary a bad thing, it leaves us with a problem: "When" at line 8 needs to communicate with the "Then" at line 9, so these smaller objects need a way to communicate with each other. A Singleton pattern could do this but puts more burden on the programmer, as now lifecycle management needs to be done to maintain isolation between tests (so that running one scenario doesn't cause a side affect with another scenario due to mismanaged state in a Singleton). A better alternative is to work with Cucumber's lifecycle for doing this via dependency injection.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg03vTYULosIh5xHGfe2YIcctXq6fWGKsvUAl4xAAo5pdoeu0Olae3UuZRo9jPznMGWnS1cJs2X43zjfTHTywE3IkzMGDa0eACEe55Rxsj1kFFU5_sGwvoUVRJUASuSiYodnLmbctiCf5U/s1600/Screen+Shot+2017-05-22+at+4.31.38+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg03vTYULosIh5xHGfe2YIcctXq6fWGKsvUAl4xAAo5pdoeu0Olae3UuZRo9jPznMGWnS1cJs2X43zjfTHTywE3IkzMGDa0eACEe55Rxsj1kFFU5_sGwvoUVRJUASuSiYodnLmbctiCf5U/s400/Screen+Shot+2017-05-22+at+4.31.38+PM.png" width="400" /></a></div>
<h2 style="text-align: left;">
Working inside a World</h2>
<div>
The World lifecycle pattern is simple: the state to be shared between Definitions <a href="https://app.cucumber.pro/projects/cucumber-jvm/documents/master/picocontainer/README.md" target="_blank">is stored in the World</a>, and the world is created at the start of executing a scenario and then destroyed upon completion of the scenario. Upon execution of a new scenario, a new World is created again, and so on. Although the World pattern is <a href="https://www.custardbelly.com/blog/blog-posts/2014/01/22/cucumberjs-world/" target="_blank">heavily emphasized in Cucumber.JS</a>, it's not so explicit for CucumberJVM. Cucumber manages the World lifecycle automatically if you use Dependency Injection. PicoContainer (built by the authors of Cucumber) is a simple and lite weight framework that gets the job done via constructor injection.<br />
<h3 style="text-align: left;">
Here's how</h3>
Add picocontainer to your build dependencies (Because I found PicoContainer.org hard to work with, I used <a href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22picocontainer%22">Maven.org</a> to search for the latest versions of "cucumber-picocontainer" and "picocontainer."):<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2qeATyMz0KaMjnuO49TMGkZvrzzvp7xltJv7dQ7Ued3HwlPbxkOGZ9LxLQ5yoRGirdQgDAUq9-DsM2q5s1sc_PtroWKxwT7vrfkf54CwzzJIsuQ8F1Zszpe6EIGBzz5x2XAtjXjWYeFI/s1600/Screen+Shot+2017-05-04+at+2.25.39+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="56" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2qeATyMz0KaMjnuO49TMGkZvrzzvp7xltJv7dQ7Ued3HwlPbxkOGZ9LxLQ5yoRGirdQgDAUq9-DsM2q5s1sc_PtroWKxwT7vrfkf54CwzzJIsuQ8F1Zszpe6EIGBzz5x2XAtjXjWYeFI/s320/Screen+Shot+2017-05-04+at+2.25.39+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Add two jars to activate World lifecycle and Dependency Injection</td></tr>
</tbody></table>
Take a look at your three definitions and create a new class for passing information.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7bSEC3_3ThyphenhyphenJfUWsS56D1aDiCzalJM-aqn6XCQR1mp4jlUndZIte8M8fDE6joWKILrYph_SxpBHclJ0smFyB3NFNoqK9k59zCN-aXrUOlFdLHaBD2Xgtm5AoR07B2hyRgds1flhGrm_c/s1600/Screen+Shot+2017-05-23+at+5.49.59+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="97" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7bSEC3_3ThyphenhyphenJfUWsS56D1aDiCzalJM-aqn6XCQR1mp4jlUndZIte8M8fDE6joWKILrYph_SxpBHclJ0smFyB3NFNoqK9k59zCN-aXrUOlFdLHaBD2Xgtm5AoR07B2hyRgds1flhGrm_c/s400/Screen+Shot+2017-05-23+at+5.49.59+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Three Step Definitions</td></tr>
</tbody></table>
Since in this case it's about a shopping cart of items, lets go with that.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjM4nh-XtUax8HHiFI4F-i3KGhEqhXxMvG8xU51KymzbQaBHCBA3SpfDhEM6BP6OqjLAWhD27lXYOR0On6_iVKwkdfskxJH9CtrelObSfy3SIZyGK1GRR_CNR3TchShyHkuGMygtDYPnE/s1600/Screen+Shot+2017-05-23+at+5.52.58+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjM4nh-XtUax8HHiFI4F-i3KGhEqhXxMvG8xU51KymzbQaBHCBA3SpfDhEM6BP6OqjLAWhD27lXYOR0On6_iVKwkdfskxJH9CtrelObSfy3SIZyGK1GRR_CNR3TchShyHkuGMygtDYPnE/s400/Screen+Shot+2017-05-23+at+5.52.58+PM.png" width="400" /></a></div>
For now put the object in the same package as its steps in the top level package for definitions. Later we'll reorganize but for now keep writing code because it will be easier to re-organize after more of the design has emerged. Since PicoContainer uses constructor injection, add Constructors for the data injection.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMtAQB133imI3pDC5f424obamnaSMqeyUGqS1IZtEYa2QY_HhYbme_QnP5NPPy9H4Vh7nbpwyD82n9T6j9OVtf2dvnUVMSH1VMblidt1VdziVxEluAG2EJ4VHSHFRmFswy4ccVb4n8aHo/s1600/Screen+Shot+2017-05-23+at+6.24.39+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="67" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMtAQB133imI3pDC5f424obamnaSMqeyUGqS1IZtEYa2QY_HhYbme_QnP5NPPy9H4Vh7nbpwyD82n9T6j9OVtf2dvnUVMSH1VMblidt1VdziVxEluAG2EJ4VHSHFRmFswy4ccVb4n8aHo/s400/Screen+Shot+2017-05-23+at+6.24.39+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Constructor Injection<br />
(Click pic for larger resolution)</td></tr>
</tbody></table>
This is all the "structure" code needed for PicoContainer and Cucumber. When Cucumber executes a feature file with these steps, and it matches to these definitions, it will use PicoContainer to find and inject the dependencies when it constructs these classes, and these dependencies will be inserted in the World during Scenario execution.<br />
<br />
Here are the Given, When, Then definitions using the dependency:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx-GO93FuvctG84d1NEA0ZIXdNKxvQMF5W5Im4z-V2BpIS5knHMVAY0-klTDEU2GC8xT-3TieItGTCiDvPcCWHrXsbl4b_UB0xyRM9nIJicUFIX9I7kDoiB2Fbnx1u15ge0NunEMZg6WE/s1600/Screen+Shot+2017-05-23+at+6.34.15+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx-GO93FuvctG84d1NEA0ZIXdNKxvQMF5W5Im4z-V2BpIS5knHMVAY0-klTDEU2GC8xT-3TieItGTCiDvPcCWHrXsbl4b_UB0xyRM9nIJicUFIX9I7kDoiB2Fbnx1u15ge0NunEMZg6WE/s400/Screen+Shot+2017-05-23+at+6.34.15+PM.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4gYHovMv7fAnIBwsH9SL7Q5h40g-CNZDApB9h9617UENEXv0N7LGPk42M_kjsAxZe6aDDO2xN_3RfbJrexreB8Af45DZ1pACeLrZdICMg7zVc04nQBM34qeBGZfunL0dOQ6xMrEVgr1A/s1600/Screen+Shot+2017-05-23+at+6.34.39+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4gYHovMv7fAnIBwsH9SL7Q5h40g-CNZDApB9h9617UENEXv0N7LGPk42M_kjsAxZe6aDDO2xN_3RfbJrexreB8Af45DZ1pACeLrZdICMg7zVc04nQBM34qeBGZfunL0dOQ6xMrEVgr1A/s400/Screen+Shot+2017-05-23+at+6.34.39+PM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh86iBWCYBBrBKq882QuUxZ7GK8EQfIs3Y7TmDWPoa5nJAc5oCKxeXUi5xErjhyphenhyphenXGnbEfESR0c5fxotsC9f8OPzsEJxc9033Jv6HvPXdUuO8TrhdQXp0Un9psLts_8oRPKPEHo1FjFD5YM/s1600/Screen+Shot+2017-05-23+at+6.35.01+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="271" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh86iBWCYBBrBKq882QuUxZ7GK8EQfIs3Y7TmDWPoa5nJAc5oCKxeXUi5xErjhyphenhyphenXGnbEfESR0c5fxotsC9f8OPzsEJxc9033Jv6HvPXdUuO8TrhdQXp0Un9psLts_8oRPKPEHo1FjFD5YM/s400/Screen+Shot+2017-05-23+at+6.35.01+PM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Execute the test runner and you'll see this is enough for the first scenario outline.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRN5GvEluIaMfAHp_NPZgFxCkBUZFuYRMiyrIWogq4W9jm1nEZdA8hpLnjBKVP_M51789ZyjWLORw4WHlVvvzS4xWnaNayQN-9zCLEJU8Hi0_tDw5Sc_XldVw1RZeulViCQxv7phme3J0/s1600/Screen+Shot+2017-05-23+at+6.43.29+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="317" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRN5GvEluIaMfAHp_NPZgFxCkBUZFuYRMiyrIWogq4W9jm1nEZdA8hpLnjBKVP_M51789ZyjWLORw4WHlVvvzS4xWnaNayQN-9zCLEJU8Hi0_tDw5Sc_XldVw1RZeulViCQxv7phme3J0/s400/Screen+Shot+2017-05-23+at+6.43.29+PM.png" width="400" /></a></div>
<br />
To illustrate the World is being destroyed, a short experiment such as injecting a counter to count how many times the Given is called will make this clear.<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3TXtkhustg0gOrYye-vQeZq0nv3QO7FBvVMympzySY9BSkBw43N45ODs-kkULPM7cf-BIPYaBvCGvboASQ8uFL5MrD4RWrH4mfSXiUOJRcJq4-oRvVqy63rDP_soEZ_zwT4r3fcos4Rc/s1600/Screen+Shot+2017-05-23+at+8.19.21+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3TXtkhustg0gOrYye-vQeZq0nv3QO7FBvVMympzySY9BSkBw43N45ODs-kkULPM7cf-BIPYaBvCGvboASQ8uFL5MrD4RWrH4mfSXiUOJRcJq4-oRvVqy63rDP_soEZ_zwT4r3fcos4Rc/s400/Screen+Shot+2017-05-23+at+8.19.21+PM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Counter is always one because it's replaced each time the Scenario is run<br />
(Click to see full size pic.)</td></tr>
</tbody></table>
Although Counter is always incremented in the definition for Given, and checked in the definition for the Then, it is always set to 1 because each row of a scenario outline gets it's own World.<br />
<h2 style="text-align: left;">
Ramifications of Global Definitions on Design</h2>
A good design does at least these two things well (in this order) that allow a program to respond to change:<br />
1) communicates intent in an understandable way, and<br />
2) is maintainable.<br />
Another 50 pages could be written about other important characteristics--the book <a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=sr_1_fkmr2_1?ie=UTF8&qid=1495695443&sr=8-1-fkmr2&keywords=solid+code+bob+martin" target="_blank">Clean Code</a> is a good reference--but let's keep it to the point: <u>we don't program in binary because it's difficult to understand intent and if we wrote in binary anyhow, eventually you'll be hating life when you have to respond to new requirements.</u><br />
<br />
Global definitions mean our feature files could have a relationship with any definition (Java class with a <complete id="goog_2054698437">@Given, @When, @Then</complete>. So organize the features files in a way that makes the feature files an index into your product's features. <u>Feature files will be the index into your definitions (Java code) as well.</u> To organize the java code so it communicates intent and is maintainable, use the principle of "keeping things that work together next to each other." Said another way, keep definitions grouped with the things your injecting into them. This is a big departure from BDD frameworks that don't do global definitions, where usually the feature files and definitions are grouped together in "src/java/com/feature/purchase/buydog." With global definitions, doing so would actually <b>misinform</b>. With global definitions, it'd be better to drop everything in one namespace. But lets try something better than that.<br />
<br />
For example, organize feature files thusly (there may not be an advantage to having feature files children of src/java directory, but I did this out of habit):<br />
src/java/com/features/purchase/BuyDog.feature<br />
src/java/com/features/purchase/BuyCat.feature<br />
src/java/com/features/purchase/BuyFish.feature<br />
src/java/com/features/returns/ReturnFishTank.feature<br />
src/java/com/features/returns/ReturnDog.feature<br />
<br />
For Java code, I looked at each set of definitions and their collaborators and tried to group them in a sensible way:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0Js-NMToh7GK1xDEPjy_d6JaZXvhOTWr0_TmYvH_fktEb-qhYrSv49zh5_4GLlSfuwY3XzOz5kAEts-wPh8h1EnoemgZgycUzT97iQjs_9lo2a2fMS71ejcQBT_QUUZueFDiV2jNpDzA/s1600/Screen+Shot+2017-05-25+at+12.19.30+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0Js-NMToh7GK1xDEPjy_d6JaZXvhOTWr0_TmYvH_fktEb-qhYrSv49zh5_4GLlSfuwY3XzOz5kAEts-wPh8h1EnoemgZgycUzT97iQjs_9lo2a2fMS71ejcQBT_QUUZueFDiV2jNpDzA/s320/Screen+Shot+2017-05-25+at+12.19.30+AM.png" width="320" /></a></div>
Then later, when I added automation for the few selected accessories a few are not<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKFiwWG0Opueuh_hZjzwlNDIlSLATg6jgfseI3NYSSoJF78X_WLvSHWadd2mk2wvbhHAtavgri6aBP15g9ZJlzN3VFfRwk7ei0QxdzsFkh6u6JPc7sz22WBbSCfbWK3fnGEqr0donFu3Q/s1600/Screen+Shot+2017-05-25+at+12.27.20+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="88" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKFiwWG0Opueuh_hZjzwlNDIlSLATg6jgfseI3NYSSoJF78X_WLvSHWadd2mk2wvbhHAtavgri6aBP15g9ZJlzN3VFfRwk7ei0QxdzsFkh6u6JPc7sz22WBbSCfbWK3fnGEqr0donFu3Q/s400/Screen+Shot+2017-05-25+at+12.27.20+AM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
I had to decide if I wanted to collaborate between the When and Then with the shopping cart, I needed to drop them in the same "shopping" namespace as the previous scenario. The fact that I'm using the same Given definition, then that reinforces that decision. This all makes sense since they are all about the same thing. But get used to the idea that just because steps are in the same feature file, their definitions <u>could be anywhere</u>.<br />
<h2 style="text-align: left;">
Times goes on keep the stair rails polished</h2>
Since feature files are a reflection of a product's features, BDD test automation needs to respond to three kinds of changes:<br />
<ul style="text-align: left;">
<li>new behaviors/features, </li>
<li>adjusting existing behaviors/features, and </li>
<li>adjust how existing behavior/feature operates.</li>
</ul>
<h4 style="text-align: left;">
New behaviors versus adjusting existing behaviors</h4>
</div>
<div>
Organize feature files in a sensible hierarchy with good feature file names so it's easily browsable and searchable in order to answer the question, "is this new idea the PO has a new behavior or a change in an existing behavior?"</div>
<blockquote class="tr_bq">
src/java/com/features/purchase/BuyDog.feature<br />
src/java/com/features/purchase/BuyCat.feature<br />
src/java/com/features/purchase/BuyFish.feature<br />
src/java/com/features/returns/ReturnFishTank.feature<br />
src/java/com/features/returns/ReturnDog.feature </blockquote>
<blockquote class="tr_bq">
...</blockquote>
<div>
(It'll be hard to organize features without knowing the business you're building behaviors for. Go find someone to help/interview about that as this knowledge typically isn't in the IT part of the organization.)</div>
<div>
<div>
<br /></div>
<div>
The business want's to collect customer contact info so they can send them offers via physical mail or email. To do that, they make this offer to the customer at time of checkout by offer VIP cards that give an additional 5% discount on purchases to collect your contact info and send you more offers. </div>
<div>
</div>
<div>
If it's known that there will be ten more VIP card behaviors, better to make a directory just for different VIP features. But if all we know at the time is that there is just this one feature, then we can just add the behavior into an existing feature file as shown (we can always move the feature files around later).</div>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3uirfg4-pDPmoPfzVTBNObSREJggLc_-6q1em_0RSav7FAALtODdPn9nm_OgwIvymVjXNQK4Roa22iNZ66Bw9fHRIFJT9Gh8SL2z8iGVUtzKQ-Bz4twoNH2srHJlW8def6PGvx-n-bGU/s1600/Screen+Shot+2017-05-24+at+7.48.27+AM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="361" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3uirfg4-pDPmoPfzVTBNObSREJggLc_-6q1em_0RSav7FAALtODdPn9nm_OgwIvymVjXNQK4Roa22iNZ66Bw9fHRIFJT9Gh8SL2z8iGVUtzKQ-Bz4twoNH2srHJlW8def6PGvx-n-bGU/s400/Screen+Shot+2017-05-24+at+7.48.27+AM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td class="tr-caption" style="font-size: 12.800000190734863px;"><span style="font-size: small; text-align: left;">Adding another scenario outline (highlighted) to BuyDog</span></td></tr>
</tbody></table>
</td></tr>
</tbody></table>
<div>
The global definitions related to these steps need to be updated to pass information about the VIP card status (the definitions for the Given and Then). Because the definitions are global, finding the impacted definitions should be driven from the feature file. If you've a good feature file editor like Natural, you can open those steps so you can implement a good way to inject (via PicoContainer) an object to pass along VIP card status. If you haven't a good editor, then use your IDE to search for the step, filtering by .java file. If you use IntelliJ, it has <a href="https://www.jetbrains.com/help/idea/2017.1/bdd-testing-framework.html" target="_blank">built in support for Gherkin</a> so that you can put the cursor on a feature files step, then with a CTRL/CMB-B, get to the definition defined by your Java code. If you have nothing but vanilla Eclipse (no Natural plugin), here is how to work with search:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaCXycjGXd5X3EASiQOrbZOhgbLPXkwJXJGyDkQQkViuI3l_qmTtowdXZeDqXTa0fNJBIz8klVXmiLpLB7i2Hkz5-H3Lb-ApedX3QXKxhtjw0Kcvtq2oy4tFnWBJKWdistyNI3viMXhL8/s1600/Screen+Shot+2017-05-24+at+7.40.54+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaCXycjGXd5X3EASiQOrbZOhgbLPXkwJXJGyDkQQkViuI3l_qmTtowdXZeDqXTa0fNJBIz8klVXmiLpLB7i2Hkz5-H3Lb-ApedX3QXKxhtjw0Kcvtq2oy4tFnWBJKWdistyNI3viMXhL8/s320/Screen+Shot+2017-05-24+at+7.40.54+AM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjVfeSXWon_eoKzXf5yE06SOElK_Pr_htJmElDTmaJGkzbGwOFXyX8WFWVjxxWEmy0Ysxb0uJ2vLomFT2Fvjyjk3LcjuuaJhIppShMKeg5mcZ76Zm9rMXhOD3q-5L-WhmEDteQE5jMKkE/s1600/Screen+Shot+2017-05-24+at+7.41.18+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjVfeSXWon_eoKzXf5yE06SOElK_Pr_htJmElDTmaJGkzbGwOFXyX8WFWVjxxWEmy0Ysxb0uJ2vLomFT2Fvjyjk3LcjuuaJhIppShMKeg5mcZ76Zm9rMXhOD3q-5L-WhmEDteQE5jMKkE/s320/Screen+Shot+2017-05-24+at+7.41.18+AM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD72LpF-p09AtUl2SlWNzWc8DWZzenEPaVzgNhcJZtz5sXE-2pEC5brkv1uDVcts2SFE4uKwnkmNVQ1ZSrsF56NSq-Rsfb6NPM-_bAM2kGvQveXJFr1CDav6gD4IHre7GFOKJ28FmDceY/s1600/Screen+Shot+2017-05-24+at+7.41.28+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="139" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD72LpF-p09AtUl2SlWNzWc8DWZzenEPaVzgNhcJZtz5sXE-2pEC5brkv1uDVcts2SFE4uKwnkmNVQ1ZSrsF56NSq-Rsfb6NPM-_bAM2kGvQveXJFr1CDav6gD4IHre7GFOKJ28FmDceY/s320/Screen+Shot+2017-05-24+at+7.41.28+AM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
Answering the question, "what feature files use this definition?" is a bit harder in that you need to avoid the regex expressions. I'm not aware of any tools that help.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4DTJL3yFkiuyFBQzbt7RrVSM7jcIfXon-u8QWUKMJqp1sTb5b2vBqfnZngccGQ6GlKzlX0wgSLCikvKq0SnC6q1HDNidG6LGESiqZ1Qtg0HFgPatptCbnFAKZn2SlqylCPWuBZSiMEyg/s1600/Screen+Shot+2017-05-24+at+8.09.32+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4DTJL3yFkiuyFBQzbt7RrVSM7jcIfXon-u8QWUKMJqp1sTb5b2vBqfnZngccGQ6GlKzlX0wgSLCikvKq0SnC6q1HDNidG6LGESiqZ1Qtg0HFgPatptCbnFAKZn2SlqylCPWuBZSiMEyg/s320/Screen+Shot+2017-05-24+at+8.09.32+AM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxiHUJsPYuHfkTPMfI2s9t6X3U7U-u1WdT5yLaaesVsw9as0IaZX6hvIxrnEaLqvkI1f8H3xfRVCvJspQkINm72CF_Cf3EUAi_Ju9OXhSqSBp4wiblCeIj4zSq0_7LTj4vOVbechpYIw/s1600/Screen+Shot+2017-05-24+at+8.10.25+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxiHUJsPYuHfkTPMfI2s9t6X3U7U-u1WdT5yLaaesVsw9as0IaZX6hvIxrnEaLqvkI1f8H3xfRVCvJspQkINm72CF_Cf3EUAi_Ju9OXhSqSBp4wiblCeIj4zSq0_7LTj4vOVbechpYIw/s320/Screen+Shot+2017-05-24+at+8.10.25+AM.png" width="320" /></a></div>
<div>
<br /></div>
<div>
<b>Adjusting only implementation</b></div>
<div>
In this case, the behavior has been implemented but, darn it, the implementation just seems lacking or is in need of an update. Assumedly the PO knows it's an update of an existing implementation by browsing through the feature documentation (maybe it's been turned into a GitBook). The team brings the story in with a reference to the existing feature file and a simple bullet points on how to change the implementation. During the sprint, the developers start at the feature file and from there open the definitions, read the Java code, and then make changes to the definition to get the test failing because the definition has been updated to the new implementation. With the automation complete, the developers build the functionality (assumedly using TDD so they have micro tests which keep test automation in a <a href="https://itunes.apple.com/us/podcast/001-build-a-solid-pyramid/id1234019720?i=1000385169270&mt=2" target="_blank">sleek and pointy pyramid shape</a>.)</div>
<h2 style="text-align: left;">
Closing Thoughts</h2>
<div>
With support from a feature file editor that will escort you to the definitions, World lifecycle management enabled via dependency injection, and the fact that Cucumber is maintained by developers with significant control of the direction and vision of the company (Aslak Hellesøy, Joseph Wilk, Matt Wynne,Gregory Hnatiuk, and Mike Sassak) I'd say give global definitions a try. It's easy to dismiss trying something like this out of hand. In fact <a href="https://www.accusoft.com/blog/surge-effective-automated-testing-microservice-world-part-1/" target="_blank">some</a> have mentioned how they've tried global definitions and failed, (see References section, "Global scoping is Evil"). In that case they were doing BDD incorrectly (not doing *B* DD at all in fact) as shown in the below, complaining about "click the search button."<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img border="0" data-original-height="674" data-original-width="968" height="278" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFEoKDhQ_pZ35AjsgLN1RRqduwmjNfd27MES2o71rw4uV9Dc-HUdetJ-HYvyvZg_wWtu_YDK6t-4iVTSGbU7NjvOjc2ZmeD2U_pj4JUlXENy0PT4s-MSpfNZdl5lVZjfJ2YRl6nfZe1HE/s400/Screen+Shot+2017-05-25+at+5.50.37+PM.png" style="margin-left: auto; margin-right: auto;" width="400" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Implementation details about UI aren't behavioral</td></tr>
</tbody></table>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixReY99AZnb064USmEfhzeKosyn1yCbunXFvd_jQJfR8_R4hTO5GXOI-oY-zhbDwgbgwVoxsdH1pQ5LOZ-SBOf-WmiS0bpx_CpMMHkpYd0rkAXynU2Ho31eXUKvr4nRXC0AMaD90QEC30/s1600/BDD+elements+of+style+Cover+small.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1132" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixReY99AZnb064USmEfhzeKosyn1yCbunXFvd_jQJfR8_R4hTO5GXOI-oY-zhbDwgbgwVoxsdH1pQ5LOZ-SBOf-WmiS0bpx_CpMMHkpYd0rkAXynU2Ho31eXUKvr4nRXC0AMaD90QEC30/s320/BDD+elements+of+style+Cover+small.jpg" width="226" /></a>Building "un"behavioral tests is a common early adopter's mistake which can happen to even experienced people who haven't stepped out of the box. <a href="http://agilenoir.biz/en/am-i-behavioral-or-not/" target="_blank">Behavioral or Not?</a> and <a href="https://leanpub.com/BDDElementsOfStyle/overview" target="_blank">Behavior Driven Development Elements of Style</a> teach how to build feature files at the correct level. Building tests in the way the author of the above wanted to wasn't maintainable so this effort was in bad shape with or without global definitions. Global definitions forced them to fail faster which was a good thing as they would give up rather than build a bunch of automation that's expensive to maintain. This is likely one of the reasons Cucumber removed the ability to not use global definitions. If you're still unsatisfied, use some strategies to <a href="http://confessionsofanagilecoach.blogspot.com/2017/05/teaching-cucumbers-about-boundaries.html" target="_blank">teach Cucumber about boundaries</a>, use a different BDD framework such as JBehave, or change Cucumber (it's open source) yourself to meet your needs.<br />
<br />
<br />
<br />
<h2 style="text-align: left;">
References</h2>
<a href="https://github.com/lancerkind/CucumberAndDI" target="_blank">Lance's example code for this article on GitHub</a><br />
<a href="http://stackoverflow.com/questions/33062485/picocontainer-for-singleton-di" target="_blank">picocontainer-for-singleton-di</a><br />
<a href="http://stackoverflow.com/questions/34449948/how-to-pass-variable-values-between-steps-in-cucumber-java/34531404#34531404" target="_blank">how-to-pass-variable-values-between-steps-in-cucumber-java</a><br />
<a href="http://confessionsofanagilecoach.blogspot.com/2014/12/recommended-behavior-driven-development.html" target="_blank">Cucumber BDD environment installation</a><br />
<a href="https://www.accusoft.com/blog/surge-effective-automated-testing-microservice-world-part-1/" target="_blank">Global Definitions are EVIL</a><br />
<h2 style="text-align: left;">
Troubleshooting</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCQv_NYV_wI5V3Ht9I2UU6evYfX374U2NxeW_kGKi-bBiMWCuTEs7on6ix1UEK81YW-KLIuw0v9UIsb-BfERd2IIckwcIz3YnzAGZZhGgTI_5jGCWSW-EU5xOjSgMZgoBry6nQedqv5jU/s1600/Screen+Shot+2017-05-04+at+1.50.22+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCQv_NYV_wI5V3Ht9I2UU6evYfX374U2NxeW_kGKi-bBiMWCuTEs7on6ix1UEK81YW-KLIuw0v9UIsb-BfERd2IIckwcIz3YnzAGZZhGgTI_5jGCWSW-EU5xOjSgMZgoBry6nQedqv5jU/s400/Screen+Shot+2017-05-04+at+1.50.22+PM.png" width="400" /></a></div>
<h3 style="text-align: left;">
JUnit green bar stops rendering and tests not working</h3>
Click in the Test Selection window and look for a stack trace in the Failure Trace pane. In cases like this, something has happened before JUnit execution has even started. You'll need to correct the problem exposed in the Failure Trace.<br />
<h3 style="text-align: left;">
Arity Problem</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzNiayK-y7HR-Tu_GDOgrJ_NzE7Oku-w1rM4CvXVSrsl-PAGtkox5_qbY3HrhWA1Lt80UEmjX_fDrSus-A3mRLv_zTwVkPXa75XrmH-VlBTHpXHMXFWZ1q-qKWVPYHJ_UadgPJMX-bRaI/s1600/Screen+Shot+2017-05-24+at+8.32.53+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="80" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzNiayK-y7HR-Tu_GDOgrJ_NzE7Oku-w1rM4CvXVSrsl-PAGtkox5_qbY3HrhWA1Lt80UEmjX_fDrSus-A3mRLv_zTwVkPXa75XrmH-VlBTHpXHMXFWZ1q-qKWVPYHJ_UadgPJMX-bRaI/s400/Screen+Shot+2017-05-24+at+8.32.53+AM.png" width="400" /></a></div>
</div>
<blockquote class="tr_bq">
<blockquote class="tr_bq">
<div class="separator" style="clear: both; text-align: center;">
Failure Trace shows Arity problem</div>
<div>
</div>
</blockquote>
</blockquote>
Fiddling with feature steps that already have definitions may result in the above when there is a mismatch between the number of parameters Cucumber is trying to pass from the step in the feature file into the definition. Check the definition's parameter list and the feature file to see which needs to be straightened out.<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
<br /></blockquote>
</blockquote>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0tag:blogger.com,1999:blog-284399302764909932.post-60897415234295350322017-05-01T16:23:00.002-07:002017-09-05T23:46:50.304-07:00Teaching Cucumber about Boundaries<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwltlU2rS-cXAYcwKMjvj4Rshr7wUcIJlwhtxO0sA6xy2onuMRfONMhKrsjKOyZPUALsyWfH1j2VXSIeqFX0h9Xx_KLB4Go9HAhUH-Gxi3YQ6NSX8aP0-hGCN1bseC1Iqw3_Lc_ozZjEQ/s1600/cf8c6f5224757bd974a191bb0e5d0bdf.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwltlU2rS-cXAYcwKMjvj4Rshr7wUcIJlwhtxO0sA6xy2onuMRfONMhKrsjKOyZPUALsyWfH1j2VXSIeqFX0h9Xx_KLB4Go9HAhUH-Gxi3YQ6NSX8aP0-hGCN1bseC1Iqw3_Lc_ozZjEQ/s320/cf8c6f5224757bd974a191bb0e5d0bdf.jpg" width="320" /></a></div>
Developers are trained to avoid using global anything in programs because you're making big commitments to the uncertain future. <a href="http://agilenoir.biz/behavior-driven-development-bdd/" target="_blank">Behavior Driven Development</a> (BDD) demands teams and their source of requirements to decide what words mean, and then write test automation code that enforces the meaning. In this way BDD encourages the definition of words to be used globally in a specific way in requirements. The problem with Cumber (and all? implementations of Gherkin) is that the only information passed from the feature file to the automation framework is the the usual Gherkin keywords: Given, When, and Then, which isn't the whole picture. Developers, since they can read the whole feature file, can teach the automation to do the right thing by linking to specific steps definitions designed with the whole situation in mind. The BDD framework Cucumber, in particular, doesn't make it simple to control linking feature files to specific test automation (known as step definitions) because it's optimized for a flat global namespace between a feature file and the many step definitions, but there are some boundary controls. Let's look at different levels of boundaries that can be created between your features files with Cucumber-JVM.<br />
<h2 style="text-align: left;">
The Problem</h2>
Take a look at the below two feature files (click for high-res viewing):<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmvjDpV9Zo0rCC-T2k7oOlIcpQNzuQ8f7RsTUq3CehNK8ShzlZUbK9eFY1AhHdLGxyIDqGusE92-BEgFAl3DXHse6QWE2jPh2Pqqog8odkMwET6JhG01UV9EQnIlfwuzt0z0j0i50YCh0/s1600/Screen+Shot+2017-05-01+at+2.11.22+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="269" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmvjDpV9Zo0rCC-T2k7oOlIcpQNzuQ8f7RsTUq3CehNK8ShzlZUbK9eFY1AhHdLGxyIDqGusE92-BEgFAl3DXHse6QWE2jPh2Pqqog8odkMwET6JhG01UV9EQnIlfwuzt0z0j0i50YCh0/s320/Screen+Shot+2017-05-01+at+2.11.22+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Feature File</td></tr>
</tbody></table>
To implement test automation for these two feature files, a Java programmer (in the case of Cucumber-JVM) must write java code that drives their product to prove it works as the customer expects. The problem comes up with "Then signoff" because a single Java method cannot figure out how to handle this case. Although "signoff" is used in both features, it means two different things. It's a pity that although the feature files have quite a lot of context (Feature title, user story, scenario title, directory location) which actually makes it clear what is meant by "signoff," none of that is available to communicate context when doing test automation in any of the Gherkin BDD frameworks. The above two features are in two domains different domains: the domain of package delivery and the domain of multi-user systems. One strategy is to recast the sentence "Then signoff" in one or both of those feature files so this overlap doesn't exist, and this is the first thing to try when faced with this. In cases where such a collision is using words that are a "fixed part of the domain language" then we may want to *not* have a global namespaces and instead create a boundary (AKA a bounded context in Domain Driven Design terms). <br />
<br />
Conceptually global step functions look like this (click pic for high resolution image):<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioSWGcMcJOFJeeWZxE32RLclZcqqMu0Vhf-SYMT2C1gJrFKxDreORHINR1fWEz6N5941SDN84TbfteUkaeJDupfTLFC_ixVnQyOoM4IQmMcpKkPxqkgAr9l1Kjq3duEV-8QeRYps9OKEQ/s1600/BDD+Global+Namespace+conceptB.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioSWGcMcJOFJeeWZxE32RLclZcqqMu0Vhf-SYMT2C1gJrFKxDreORHINR1fWEz6N5941SDN84TbfteUkaeJDupfTLFC_ixVnQyOoM4IQmMcpKkPxqkgAr9l1Kjq3duEV-8QeRYps9OKEQ/s320/BDD+Global+Namespace+conceptB.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Global Scope Step Definitions in a global 'dictionary'</td></tr>
</tbody></table>
Where you'll have a lot of Java steps definitions <b>designed around each gherkin statement</b>. There isn't a direct relationship between a specific feature file and a specific steps definition. Here the idea is to have many re-usable steps definitions (and an automation library that's used across all the steps definitions).<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
But perhaps, you want this for fine grain control and let the developer control all aspects of how each step in the feature file is automated (click pic for high resolution image):<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSLq7OfvdTPTldeHfadNLLXWHqqMBokRC81dBEDI6ygxskrz4V17j6VN-SNKLYfID8tj_s1o68ezw8wbAUhO5Y5PKYNnVlYUm3qm3c-JyV4igokdMAqTkNmPhvXVkL5MCYV4ilpUqxiZ4/s1600/BDD+Cucumber+steps+scoping+C.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSLq7OfvdTPTldeHfadNLLXWHqqMBokRC81dBEDI6ygxskrz4V17j6VN-SNKLYfID8tj_s1o68ezw8wbAUhO5Y5PKYNnVlYUm3qm3c-JyV4igokdMAqTkNmPhvXVkL5MCYV4ilpUqxiZ4/s320/BDD+Cucumber+steps+scoping+C.jpg" width="137" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Each feature file has it's own namespace for steps definitions</td></tr>
</tbody></table>
Here, each feature file gets one (or more) java classes to handle the scenarios within the feature file. The relationship between the steps and the feature file are intimate. The idea here is to have a very simple steps definition for a specific .feature file, and to push as much code as possible into the automation library so it can be re-used across all the steps definitions.<br />
<br />
You can get to this extreme with some easy to do but additional steps. <br />
<h2 style="text-align: left;">
Global Scope</h2>
This one is easiest to get started with as Cucumber is geared to make this easy. Your test automation project should have package arranged like:<br />
<br />
test.pageobjects<br />
test.features<br />
test.features.major_feature_one<br />
test.features.major_feature_one.subfeature<br />
test.features.major_feature_two<br />
...<br />
<br />
Create a JUnit execution class at the top of your test automation project like shown.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyjWXMLX88JAch5KvOC4Ed29r4ADYKDrRMKUt_Qnj1vub5KLNyENJGdu_EwXfbXRh4Pzk6nPstQ4aa11C2WlKtt-gFgdGaHpWZ0dkLVJ8598n1AKRifU6xOJs8W0Xk2OtvIDycmP5L0_k/s1600/Screen+Shot+2017-05-01+at+3.05.20+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyjWXMLX88JAch5KvOC4Ed29r4ADYKDrRMKUt_Qnj1vub5KLNyENJGdu_EwXfbXRh4Pzk6nPstQ4aa11C2WlKtt-gFgdGaHpWZ0dkLVJ8598n1AKRifU6xOJs8W0Xk2OtvIDycmP5L0_k/s320/Screen+Shot+2017-05-01+at+3.05.20+PM.png" width="320" /></a></div>
How everything works is that when launching the JUnit test case, it hands off to Cucumber via the @RunWith, which associates all the subpackage from that point as the namespace to use for matching step definitions. So by putting the JUnit Test class at test.features makes all of our feature files from test.features and downward, match to <b>any</b> of the step definitions declared from this point on down. (BTW, <a href="http://confessionsofanagilecoach.blogspot.com/2014/12/recommended-behavior-driven-development.html#editor_tools" target="_blank">there are tools</a> such as Natural that help in editing feature files and finding which step definition is active.)<br />
<br />
<b>Here's the big news:</b> Nothing is stopping us from having multiple JUnit Test classes in different peer packages, effectively creating boundaries for how step functions are picked up by feature files.<br />
<h2 style="text-align: left;">
Package Scope</h2>
<div>
By putting a Cucumberized JUnit Test case at the top of specific packages, we create separate bounded contexts, a term used by the practice Domain Driven Design, which basically sums up to: put the features and steps for the package delivery in one Java namespace and the features and steps for multiuser in another Java namespace, and then in each package, setup a Cucumberized JUnit Test case. In this way, boundaries between these two different domains are made.</div>
<div>
<br /></div>
<div>
It looks like this:</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitFQEdD-ppeyqgZVYXEyTd1w4t8DOOOXVY8oDUQ58hMBo4_GmP989FPAIXRCF2fK_CILJfdp5zoIWHZ2BhKvZJ2GZRsSl3gSi_iDFolJjMW3ilsP6DgQLujSjySVtYFOe82IP78GmDOCU/s1600/Screen+Shot+2017-05-01+at+3.34.27+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="149" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitFQEdD-ppeyqgZVYXEyTd1w4t8DOOOXVY8oDUQ58hMBo4_GmP989FPAIXRCF2fK_CILJfdp5zoIWHZ2BhKvZJ2GZRsSl3gSi_iDFolJjMW3ilsP6DgQLujSjySVtYFOe82IP78GmDOCU/s320/Screen+Shot+2017-05-01+at+3.34.27+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Package organization</td></tr>
</tbody></table>
<div>
test.features.packagedeliver contains one Cucumberized JUnit Test case, many steps definitions, and many feature files. test.features.processcontrol contains one Cucumberized JUnit Test case, many steps definitions, and many feature files.</div>
<div>
<br /></div>
<div>
Thusly its possible to have a "Then signoff" defined in a steps definition in the processcontrol context and a different definition for "Then signoff" by a steps definition in the packagedelivery context. If another "Then signoff" steps definition is added to say, test.features.processcontrol.increase_system_load, Cucumber will put the brakes on and complain that "Then signoff" is ambiguous as there are multiple definitions in its bounded context.</div>
<div>
<br /></div>
<div>
But what if that isn't enough? Can we make every feature file have its own context? You bet.</div>
<h2 style="text-align: left;">
Feature File Scope</h2>
<div>
If you need to do this, it's a bit of drudge work but it's not complex: for every feature file you'll need to add a Cucumberized JUnit test case, and due to a lovely quirk of Cucumber, drop that feature file's step definitions in their own package. #annoying</div>
<div>
<br /></div>
<div>
Conceptually that looks like this:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8syGOU3Sgij8blMx-PefGXbIbsvUZfUOBHYeA-LCjE9mFbeajfmucZccc3egSWMQ65uANh-GzWt2NqZJuWHwfD4lsmuhpWS4zMPaWq4FFXCoSGap6CSRo8P8tfAWe9oRG5w_j91VsG-c/s1600/BDD+Cucumber+steps+scoping+C.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8syGOU3Sgij8blMx-PefGXbIbsvUZfUOBHYeA-LCjE9mFbeajfmucZccc3egSWMQ65uANh-GzWt2NqZJuWHwfD4lsmuhpWS4zMPaWq4FFXCoSGap6CSRo8P8tfAWe9oRG5w_j91VsG-c/s320/BDD+Cucumber+steps+scoping+C.jpg" width="137" /></a></div>
<div>
Literally, it looks like this (click image to see what's going on):</div>
<div>
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsHdhIu8iL6o4rREHrBii6sG6afNR4RD6T-Hjmoa80x7eAk14TMeULH4elOUmGFLI48Bt72DzR0r4DN8gctmaxDg7tCEPDBns0xEP_h2O8JGY2dovhTlte3eo_vouzkWtYHcbtAk0NswQ/s1600/Screen+Shot+2017-05-01+at+11.00.12+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="208" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsHdhIu8iL6o4rREHrBii6sG6afNR4RD6T-Hjmoa80x7eAk14TMeULH4elOUmGFLI48Bt72DzR0r4DN8gctmaxDg7tCEPDBns0xEP_h2O8JGY2dovhTlte3eo_vouzkWtYHcbtAk0NswQ/s400/Screen+Shot+2017-05-01+at+11.00.12+AM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Two feature files in the test.features.processcontrol package want different step definitions for "Then signoff"</div>
<br />
Now each Cucumberized JUnit test case defines the mapping from a specific feature file and a package to where to load its step definitions. Because this strategy will end up producing a bunch of different JUnit test cases, you'll want to add a JUnit test suite so you can easily execute all your feature tests with one click.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhsu0oXNeZrf8GCmk0UlnGCk2msWlvavCeESuXON3LhuXwq1JOEw7bZP7T7-47-Vezp94Z6thl7CVPBbssC5_iIFv4K6jCfI5wsSK38x4Xpq4I0oGcxtosy9XyOpUkRoqN791Y6FVna3M/s1600/Screen+Shot+2017-05-01+at+4.11.57+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="155" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhsu0oXNeZrf8GCmk0UlnGCk2msWlvavCeESuXON3LhuXwq1JOEw7bZP7T7-47-Vezp94Z6thl7CVPBbssC5_iIFv4K6jCfI5wsSK38x4Xpq4I0oGcxtosy9XyOpUkRoqN791Y6FVna3M/s400/Screen+Shot+2017-05-01+at+4.11.57+PM.png" width="400" /></a></div>
<h2 style="text-align: left;">
Conclusions</h2>
</div>
<div>
You've got the tools to do whatever you want with Cucumber. Treating every feature file as having its own scope is the ultimate of control but do you really need that? The OCD control freak inside does want that and if so, better to find another framework to support that then Cucumber. Defining boundaries using packages will keep the DDD people happy and would work in a pinch when you need it. Cucumber is courageous in pushing people to build global dictionaries. I've only heard of great drama coming out of such striving though people have successfully done it. It seems unfair to having our language in feature files be global context (easy to do as we are humans) and then forcing the automation (a computer program) to discover the context when Gherkin doesn't pass along all the context from the feature files. <br />
<br />
I'm happy to hear from you if you've a story to share: Twitter:<a href="http://twitter.com/LancerKind" target="_blank">@LancerKind</a>, <a href="mailto:LancerKind@gmail.com">LancerKind@gmail.com</a>.<br />
<br />
<h2 style="text-align: left;">
References</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPQB6gvco-5S0wSkjEzsj9SJ_WLBux9YWCBuc9TVcwKnwIkVhiQfsA1bSA0Ur0NFKjVeYbGxsJYV1CFhtrOzBbGNW4_Xe4XAWio3r0-Yn7tUeTVVCfCpTbLZs8RFIRcHbFgZu90gl7DkU/s1600/BDD+elements+of+style+Cover+small.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1132" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPQB6gvco-5S0wSkjEzsj9SJ_WLBux9YWCBuc9TVcwKnwIkVhiQfsA1bSA0Ur0NFKjVeYbGxsJYV1CFhtrOzBbGNW4_Xe4XAWio3r0-Yn7tUeTVVCfCpTbLZs8RFIRcHbFgZu90gl7DkU/s320/BDD+elements+of+style+Cover+small.jpg" width="226" /></a></div>
<div>
<ul style="text-align: left;">
<li>Learn style tips for writing your Gherkin with <a href="https://leanpub.com/BDDElementsOfStyle/overview" target="_blank">Behavior Driven Development Elements of Style</a>.</li>
<li><a href="https://testingneeds.wordpress.com/2015/09/15/junit-runner-with-cucumberoptions/" target="_blank">CucumberOptions</a></li>
<li><a href="http://cucumber.github.io/api/cucumber/jvm/javadoc/cucumber/api/CucumberOptions.html#glue--" target="_blank">CucumberOptions Javadoc</a></li>
<li><a href="https://github.com/junit-team/junit4/wiki/aggregating-tests-in-suites" target="_blank">JUnit 4 Test Suites</a></li>
</ul>
</div>
</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com3tag:blogger.com,1999:blog-284399302764909932.post-24938482689135055562016-05-24T11:56:00.000-07:002017-09-05T23:55:22.304-07:00Exceptional Exception Testing with JUnit 4.11 Rules and Java 8/JUnit5<div dir="ltr" style="text-align: left;" trbidi="on">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://www.facebook.com/MissWisenheimer" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;" target="_blank"><img alt="" border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy5vBFNc5lPZfgPg3UT2BuG6FlzrZg1g04LYiAOduAQwxhjYfNVRLT02CYw1p10hwbxHJ98fEOTEcXEbfm-ZT_pgggLWgP6H58G4uiGG2OnUeyfW2QlQ04y8okfQAriAkXkOgTrLpSzI8/s320/rock+throwing+lightened2.jpg" title="Zipporah throwing a rock at Babbage" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><a href="https://www.facebook.com/MissWisenheimer" target="_blank">Zipporah taking exception to poor 'ol Babbage</a></td></tr>
</tbody></table>
<div style="text-align: left;">
</div>
<br />
<br />
You've got something that takes exception to something else and now you want to automate a test to confirm it correctly throws per your specification:<br />
<br />
<br />
<br />
<br />
<blockquote class="tr_bq">
public class ProblemCauser {<br />
public void throwsException() {<br />
throw new IllegalArgumentException("Negative");<br />
}<br />
} </blockquote>
One way is the below. But can you see a problem? <br />
<blockquote class="tr_bq">
import static org.junit.Assert.*;<br />
import org.junit.Test;<br />
public class RulesAndExceptionTest { <br />
<br />
@Test(<b>expected=IllegalArgumentException.class</b>)<br />
public void canOnlyCheckTypeButNotMessage()<br />
{<br />
ProblemCauser problem = new ProblemCauser();<br />
problem.throwsException(); <br />
}<br />
} </blockquote>
How do you know the exception message has been set to "Negative?" In the past, that left you with the following strategy:<br />
<blockquote class="tr_bq">
@Test<br />
public void theOldWayToTestExceptionMessageAndType() {<br />
<b> try {</b><br />
ProblemCauser problem = new ProblemCauser();<br />
problem.throwsException();<br />
<b> fail("didn't get expected exception");</b><br />
<b>} catch (IllegalArgumentException expected) {<br /> assertEquals("Negative", expected.getMessage());<br /> }</b><br />
}</blockquote>
This does the job. Good work! You can actually fully test drive exception handling with this strategy. The drawback is this is a lot of code to express a simple check. Even worse, many people forget to include the <b>fail()</b> call, which means if an exception <b>doesn't</b> happen, the test would pass. This would be an incorrect test case. A better strategy is to use Junit 4.11's Rules package:<br />
<blockquote class="tr_bq">
import static org.junit.Assert.*;<br />
import org.junit.Ignore;<br />
<b>import org.junit.Rule;</b><br />
import org.junit.Test;<br />
<b>import org.junit.rules.ExpectedException;</b><br />
<br />
public class RulesAndExceptionTest {<br />
<b> @Rule<br /> public ExpectedException </b><b>exceptionPolicy = ExpectedException.none(); // Set the default expectation.</b></blockquote>
<blockquote class="tr_bq">
@Test<br />
public void theNewWayToTestExceptionAndType() {<br />
<b> exceptionPolicy.expect(IllegalArgumentException.class);<br /> exceptionPolicy.expectMessage("Negative");</b><br />
<br />
ProblemCauser problem = new ProblemCauser();<br />
problem.throwsException();<br />
}<br />
}</blockquote>
This is equivalent but uses less code. How does it work?<br />
<h2 style="text-align: left;">
<span style="font-size: x-large;">Exceptional with @Rules</span></h2>
<div style="text-align: left;">
org.junit.Rule is an annotation that can be applied to a field or method. When JUnit executes the test class, it hands control to the Rule and decides how to execute Before, Test, and After, allowing the it to manage whatever it is supposed to manage. In the case of ExpectedException, allowing it to execute the @Test method and handle any exception thrown out to see if it's expected.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
You can have multiple Rules in a test class doing various things. Here are other org.junit.rules.*(<a href="http://junit.org/junit4/javadoc/latest/org/junit/rules/package-summary.html">http://junit.org/junit4/javadoc/latest/org/junit/rules/package-summary.html</a>):</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghgNUYiNoDQVZcqo9seIiR2YzSZH5u3etkZXxLsTjU1sEck0y6chTKYjUUsBo3lRNQGVutDgly-C-kVMjCCXaum_5kHNJNNQ-1gFD9c_J3DaeD0RJ6ejJAEnbrcoVcU-HrmRgS0MqkoNs/s1600/Screen+Shot+2016-05-24+at+7.47.32+AM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghgNUYiNoDQVZcqo9seIiR2YzSZH5u3etkZXxLsTjU1sEck0y6chTKYjUUsBo3lRNQGVutDgly-C-kVMjCCXaum_5kHNJNNQ-1gFD9c_J3DaeD0RJ6ejJAEnbrcoVcU-HrmRgS0MqkoNs/s400/Screen+Shot+2016-05-24+at+7.47.32+AM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Click image to zoom.</td></tr>
</tbody></table>
<div style="text-align: left;">
Using JUnit's Rules, you can clean up exception handling code to one to three lines of code (one for the test class field, then one where you expect to get an exception) making it more readable and less error prone than using a try{...fail();}catch(Exception ex) { assertEquals(...);} pattern. More functionality with less code. That's exceptional!<br />
<h2 style="text-align: left;">
<span style="font-size: x-large;">Even MORE Exceptional with Java 8 and JUnit 5</span></h2>
<div style="text-align: left;">
<span style="font-size: small;">JUnit 5 alpha was released in 2016 and it was designed to use Java 8 language features. assertThrows() tests if the exception type was correct. expectThrows() does the same AND returns the exception type so you can examine it further.</span><br />
<blockquote class="tr_bq">
<span style="font-size: small;">import static org.hamcrest.CoreMatchers.startsWith;<br />import static org.hamcrest.MatcherAssert.assertThat;<br />import org.junit.gen5.api.Assertions;<br />import org.junit.gen5.api.Test;<br />import org.junit.gen5.junit4.runner.JUnit5;<br />import org.junit.runner.RunWith;<br /><br />@RunWith(JUnit5.class)<br />public class ExceptionTestingTest {<br /> @Test<br /> public void throwsExceptionWhenPopped() {<br /> ProblemCauser problem = new ProblemCauser();<br /> Throwable <b>exception</b> = Assertions.<b>expectThrows</b>(IllegalAccessException.class, <b>() -> problem.throwsException()</b>);<br /> assertThat(<b>exception.getMessage()</b>, startsWith("Error:"));<br /> }<br />}</span></blockquote>
<span style="font-size: small;">Java 8 and JUnit5 make exception testing <b>even more exceptional</b>. Java 8 makes it possible to pass function pointers. So now the function pointer to throwsException is passed into expectThrows. And because expect throws returns the Throwable, it's possible to check it's state. Notice the use of hamcrest's assertThat as JUnit5 doesn't have one. </span><br />
<span style="font-size: small;">To get the Alpha 5.0 release to work with Eclipse's JUnit 4 plugin per: <a href="http://junit.org/junit5/docs/current/user-guide/#using-junit-4-to-run-junit-5-tests">http://junit.org/junit5/docs/current/user-guide/#using-junit-4-to-run-junit-5-tests</a>, I had to add the following dependencies to my build path (see Referenced Libraries). </span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNCzccdA7LVegI0NGOmMgClRRgC_t_qe46hTenZr1z_qaNYzDJZGUmkA_WZMs_LFNRxlIJaQaGU_d9VGAtP5ZVlohHbumx4hJQg7wz-PJaDIhMYxx8iHAzJNdn6DJyAOrUwlrkhQkCEsQ/s1600/Screen+Shot+2016-05-31+at+7.22.37+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNCzccdA7LVegI0NGOmMgClRRgC_t_qe46hTenZr1z_qaNYzDJZGUmkA_WZMs_LFNRxlIJaQaGU_d9VGAtP5ZVlohHbumx4hJQg7wz-PJaDIhMYxx8iHAzJNdn6DJyAOrUwlrkhQkCEsQ/s400/Screen+Shot+2016-05-31+at+7.22.37+AM.png" width="400" /></a></div>
<span style="font-size: small;">Notice JUnit 4 on the end. This is for the @RunWith. If you have problems, scroll down to this article's Troubleshooting section. </span><span style="font-size: small;"><a href="http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.junit%22">JUnit 5</a> and <a href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22opentest4j%22">Open Test</a> are available at Maven.org. </span></div>
<h3 style="text-align: left;">
<span style="font-size: large;">Other Solutions </span></h3>
<div style="text-align: left;">
<span style="font-size: small;">Outside of JUnit 5, here are some other solutions but they seem "exceptional" as they require more code and dependencies than using JUnit4 Rules and the code doesn't seem more readable. Here are some articles:</span></div>
<div style="text-align: left;">
<span style="font-size: small;"><a href="http://blog.goyello.com/2015/10/01/different-ways-of-testing-exceptions-in-java-and-junit/">http://blog.goyello.com/2015/10/01/different-ways-of-testing-exceptions-in-java-and-junit/</a></span></div>
<div style="text-align: left;">
<span style="font-size: small;"><a href="http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html">http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html</a> </span><br />
<br />
For background on why you'd want to write micro tests, use your commute time to listen to the <a href="http://agilenoir.biz/series/agile-thoughts/" target="_blank">Agile Thoughts</a> podcast series about the Test Automation Pyramid, episodes 1 through 8. Each episode is short, to the point, and entertaining.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioYhyKs3nkeYCdZ__YFYZqRLOIH3FmRVoG4HaRMP9AdZbcU_vhLwtQCk5AYmbv8kIkEhDv61I-1_SMMPEI8PKVXV_gaBDvhYUN2mBXHKqWkSo3DwqZqej29h9E1yxZ9WXLGSaRgEjfj2s/s1600/test+pyramid+EN2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="768" data-original-width="1024" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioYhyKs3nkeYCdZ__YFYZqRLOIH3FmRVoG4HaRMP9AdZbcU_vhLwtQCk5AYmbv8kIkEhDv61I-1_SMMPEI8PKVXV_gaBDvhYUN2mBXHKqWkSo3DwqZqej29h9E1yxZ9WXLGSaRgEjfj2s/s320/test+pyramid+EN2.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Episodes 1 through 8</td></tr>
</tbody></table>
<br /></div>
</div>
<h2 style="text-align: left;">
<span style="font-size: x-large;">References</span></h2>
<div style="text-align: left;">
<a href="http://junit.org/junit4/javadoc/latest/org/junit/Rule.html">http://junit.org/junit4/javadoc/latest/org/junit/Rule.html</a><br />
<a href="http://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4">http://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4</a><br />
<span style="font-size: small;"><a href="http://junit.org/junit5/docs/current/user-guide/#using-junit-4-to-run-junit-5-tests">http://junit.org/junit5/docs/current/user-guide/#using-junit-4-to-run-junit-5-tests</a></span><br />
<span style="font-size: small;">JUnit 5 API docs on assertThrows and expectThrows: </span><br />
<ul style="text-align: left;">
<li><span style="font-size: small;"><a href="https://junit.ci.cloudbees.com/job/JUnit5/javadoc/org/junit/gen5/api/Assertions.html#assertThrows-java.lang.Class-org.junit.gen5.api.Executable-">https://junit.ci.cloudbees.com/job/JUnit5/javadoc/org/junit/gen5/api/Assertions.html#assertThrows-java.lang.Class-org.junit.gen5.api.Executable-</a> </span></li>
<li><span style="font-size: small;"><a href="https://junit.ci.cloudbees.com/job/JUnit5/javadoc/org/junit/gen5/api/Assertions.html#expectThrows-java.lang.Class-org.junit.gen5.api.Executable-">https://junit.ci.cloudbees.com/job/JUnit5/javadoc/org/junit/gen5/api/Assertions.html#expectThrows-java.lang.Class-org.junit.gen5.api.Executable-</a></span> </li>
</ul>
<h2 style="text-align: left;">
<span style="font-size: x-large;">Troubleshooting</span></h2>
<div style="text-align: left;">
<span style="font-size: x-large;"><span style="font-size: small;">JUnit5 with Eclipse JUnit 4 plugin test runner:</span></span></div>
<ul style="text-align: left;">
<li><span style="font-size: x-large;"><span style="font-size: small;"> <b>ClassNotFound issue with DiscoveryFilter</b> </span></span></li>
<ul>
<li><span style="font-size: x-large;"><span style="font-size: small;">Solution: add junit-engine-api-5.0.0-ALPHA.jar to the build path.</span></span></li>
</ul>
<li><span style="font-size: x-large;"><span style="font-size: small;"> <b>TestRunner executes but doesn't show any test being run. </b></span></span></li>
<ul>
<li><span style="font-size: x-large;"><span style="font-size: small;">In the console, you see the following: org.junit.gen5.launcher.main.ServiceLoaderTestEngineRegistry loadTestEngines<br />INFO: Discovered TestEngines with IDs [junit5]<br /><b>java.lang.NoClassDefFoundError: org/opentest4j/TestAbortedException</b><br /> at org.junit.gen5.engine.support.hierarchical.HierarchicalTestExecutor.<init>(HierarchicalTestExecutor.java:37)</init></span></span></li>
<li><span style="font-size: x-large;"><span style="font-size: small;">Solution: go to Maven repo and add <a href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22opentest4j%22">OpenTest4J jar</a> to the build path.</span></span></li>
</ul>
</ul>
<div style="text-align: left;">
<span style="font-size: x-large;"><span style="font-size: small;"> </span> </span> </div>
</div>
</div>
Lancehttp://www.blogger.com/profile/17911134415607573037noreply@blogger.com0