<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gaurav Singh</title>
    <description>The latest articles on DEV Community by Gaurav Singh (@automationhacks).</description>
    <link>https://dev.to/automationhacks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F167687%2Fc0a44ae4-b15d-4664-be52-251567039eb9.png</url>
      <title>DEV Community: Gaurav Singh</title>
      <link>https://dev.to/automationhacks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/automationhacks"/>
    <language>en</language>
    <item>
      <title>How to setup report portal dashboards using attributes for test observability</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Fri, 22 Nov 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/how-to-setup-report-portal-dashboards-using-attributes-for-test-observability-1n88</link>
      <guid>https://dev.to/automationhacks/how-to-setup-report-portal-dashboards-using-attributes-for-test-observability-1n88</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uwybfv0rabe8cr8uhnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uwybfv0rabe8cr8uhnr.png" alt="A man applying a chart with some label having black and purple data bars" width="800" height="639"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E 3&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is a report portal?
&lt;/h2&gt;

&lt;p&gt;Successful QA engineering teams use data and test observability (o11y) to their advantage to identify areas or tests that need attention and have scope for optimization and drive corrective measures&lt;/p&gt;

&lt;p&gt;Suppose you don’t have a good way to measure and visualize how reliable your test suites are.&lt;/p&gt;

&lt;p&gt;It is easy to be overwhelmed by your suite’s many broken or flaky tests.&lt;/p&gt;

&lt;p&gt;Engineers often struggle with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resorting to ad-hoc local spreadsheets to track bugs for a failing test&lt;/li&gt;
&lt;li&gt;Maintain a record of which cases are broken/flaky&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://reportportal.io/" rel="noopener noreferrer"&gt;ReportPortal&lt;/a&gt; is a free and open-source solution that elegantly solves this exact problem.&lt;/p&gt;

&lt;p&gt;I’ve personally evangelized it at scale-ups like Gojek and CRED, and have been impressed by its power and flexibility with engineering teams deriving a ton of value.&lt;/p&gt;

&lt;p&gt;Quite often, engineering teams spend a lot of time setting up Extent reports, and Allure reports stored in S3 or GCP buckets which while good for static and presentable reports lack a lot of modern real-time analysis features that the report portal provides out-of-the-box&lt;/p&gt;

&lt;p&gt;There is a better way; why not try it? 🤷&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚙️ Setup
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install report portal
&lt;/h3&gt;

&lt;p&gt;I had previously written a few blogs on how to set ReportPortal on a single machine using docker and set logging integration using log back for a Java-based project or with Pytest for a Python-based project&lt;/p&gt;

&lt;p&gt;You can read them below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://automationhacks.io/2020/03/02/how-to-setup-reportportal-on-a-local-docker-instance/" rel="noopener noreferrer"&gt;How to set ReportPortal on a local docker instance - automation hacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://automationhacks.io/2020/09/25/logging-integration-with-logback-testng-in-report-portal/" rel="noopener noreferrer"&gt;How to do logging integration with logback and testng in ReportPortal - automation hacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://automationhacks.io/2021/02/03/python-api-automation-framework-part8-adding-reporting-with-report-portal" rel="noopener noreferrer"&gt;Python API test automation framework (Part 8) Adding reporting with ReportPortal&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here, I’ll just recap the main steps quickly so that this blog is a good starting point and self-enclosed, but will encourage you to read &lt;a href="https://reportportal.io/docs/installation-steps/DeployWithDocker" rel="noopener noreferrer"&gt;Deploy with Docker&lt;/a&gt; for more complete intuition.&lt;/p&gt;

&lt;p&gt;You can skip point 2 since we have a usable docker-compose.yml file already in the Git repo that we’ll work with for this tutorial&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Docker Desktop on your operating system (OS) from the Docker website. Instructions for Mac can be found on &lt;a href="https://docs.docker.com/desktop/setup/install/mac-install/" rel="noopener noreferrer"&gt;Install Docker Desktop on Mac&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download the latest &lt;code&gt;docker-compose.yml&lt;/code&gt; file
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -LO https://raw.githubusercontent.com/reportportal/reportportal/master/docker-compose.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Bring up docker containers by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose -p reportportal up -d --force-recreate

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a directory for OpenSearch and give permissions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p data/opensearch
chmod 775 data/opensearch
chgrp 1000 data/opensearch

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify ReportPortal containers are up by running below and checking that all containers are in a healthy state&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker ps -a

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next access ReportPortal using &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; and use the default username and password &lt;code&gt;superadmin\erebus&lt;/code&gt; to login&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2h66do0jhhqq4f30yuxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2h66do0jhhqq4f30yuxy.png" alt="Report portal login screen" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the bottom left, click on the Superman logo and then on Administrate&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l40myl4qn4ozt1c2ity.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0l40myl4qn4ozt1c2ity.png" alt="Administrate" width="624" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will take you to All Projects, click on &lt;strong&gt;Add New Project&lt;/strong&gt; and type the project name as &lt;strong&gt;test-infra&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yiabfd7qrzwsaa8kdk5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yiabfd7qrzwsaa8kdk5.png" alt="Add new project" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nomcestkr9ihigj7i88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nomcestkr9ihigj7i88.png" alt="Add project name" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next in the bottom left, again click on the Superman logo and then on Profile&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpi5ykgjb4we6zmh3t6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpi5ykgjb4we6zmh3t6v.png" alt="Click on profile" width="620" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will take you to the User profile section, click on API keys and then on Generate API Key&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtepqm95wtuixmmh2w9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtepqm95wtuixmmh2w9q.png" alt="Generate API key" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter a key name like &lt;strong&gt;test-infra&lt;/strong&gt; and then make a copy of the generated key. Please note: This key will not be accessible after this screen so it’s important to &lt;strong&gt;note it down&lt;/strong&gt; somewhere safe&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuu86g3yqqkmegkzoxma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuu86g3yqqkmegkzoxma.png" alt="Type API key unique name" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone git repo
&lt;/h3&gt;

&lt;p&gt;We’ll use &lt;a href="https://github.com/automationhacks/test-infra" rel="noopener noreferrer"&gt;https://github.com/automationhacks/test-infra&lt;/a&gt; repo on my Github account.&lt;/p&gt;

&lt;p&gt;You can clone the repo using either HTTPS/SSH or Github CLI and then open the build.gradle file using IntelliJ IDEA&lt;/p&gt;

&lt;p&gt;You’ll notice this project already has a docker-compose.yml file in the root directory, if you want to use the same for your setup, you could run the command to set up docker containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose -p reportportal up -d --force-recreate

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once Gradle downloads all the dependencies&lt;/p&gt;

&lt;p&gt;Search for &lt;code&gt;reportportal.properties&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and replace the &lt;code&gt;rp.api.key=&amp;lt;your_api_value&amp;gt;&lt;/code&gt; copied in step 10 of previous section&lt;/p&gt;

&lt;p&gt;Required gradle dependencies are already set up in the build.gradle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;testImplementation 'org.testng:testng:7.10.2'
testImplementation 'com.epam.reportportal:agent-java-testng:5.4.2'
testImplementation 'com.epam.reportportal:logger-java-logback:5.2.2'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And logback.xml with configuration for logging is present in src/test/resources/logback.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;configuration&amp;gt;
   &amp;lt;appender name="ReportPortalAppender" class="com.epam.reportportal.logback.appender.ReportPortalAppender"&amp;gt;
       &amp;lt;encoder&amp;gt;
           &amp;lt;pattern&amp;gt;%d{HH:mm:ss.SSS} [%t] %-5level - %msg%n&amp;lt;/pattern&amp;gt;
       &amp;lt;/encoder&amp;gt;
   &amp;lt;/appender&amp;gt;

   &amp;lt;appender name="ASYNC_RP" class="ch.qos.logback.classic.AsyncAppender"&amp;gt;
       &amp;lt;appender-ref ref="ReportPortalAppender"/&amp;gt;
       &amp;lt;!-- Sets the maximum number of log events in the queue --&amp;gt;
       &amp;lt;queueSize&amp;gt;512&amp;lt;/queueSize&amp;gt;
       &amp;lt;discardingThreshold&amp;gt;0&amp;lt;/discardingThreshold&amp;gt;
       &amp;lt;!-- Set to true to include information about the caller of the logging method. --&amp;gt;
       &amp;lt;includeCallerData&amp;gt;true&amp;lt;/includeCallerData&amp;gt;
   &amp;lt;/appender&amp;gt;

   &amp;lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&amp;gt;
       &amp;lt;encoder&amp;gt;
           &amp;lt;pattern&amp;gt;%d{HH:mm:ss.SSS} [%t] %-5level - %msg%n&amp;lt;/pattern&amp;gt;
       &amp;lt;/encoder&amp;gt;
   &amp;lt;/appender&amp;gt;

   &amp;lt;root level="INFO"&amp;gt;
       &amp;lt;appender-ref ref="CONSOLE"/&amp;gt;
       &amp;lt;appender-ref ref="ASYNC_RP"/&amp;gt;
   &amp;lt;/root&amp;gt;
&amp;lt;/configuration&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gradle test job looks like below which registers the report portal listener&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test {
   useTestNG() {
       useDefaultListeners = true
       listeners &amp;lt;&amp;lt; 'io.automationhacks.testinfra.alerting.TestListener'
       listeners &amp;lt;&amp;lt; 'com.epam.reportportal.testng.ReportPortalTestNGListener'
       parallel = 'methods'
       threadCount = 6

       includeGroups System.getProperty('includedGroups', '')
       excludeGroups System.getProperty('excludedGroups', '')
   }

   // pass system properties from commandline
   systemProperties = System.properties

   // show stdout and stderr of test JVM on console
   testLogging.showStandardStreams = true

   finalizedBy jacocoTestReport
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can recap the blogs mentioned at the top for more details on these setups however this is sufficient to get started with pumping data into ReportPortal&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s run some tests
&lt;/h2&gt;

&lt;p&gt;If you navigate to &lt;code&gt;src/test/java/io/automationhacks/testinfra/reqres&lt;/code&gt; package&lt;/p&gt;

&lt;p&gt;You’ll see that I’ve written some rest-assured tests to test the R&lt;a href="https://reqres.in/" rel="noopener noreferrer"&gt;eqRes API&lt;/a&gt;, in general, either get/put/post/delete certain resources and assert their outcomes.&lt;/p&gt;

&lt;p&gt;We can run the test to push the results in the report portal and see how the launches look like by running the below commands&lt;/p&gt;

&lt;p&gt;These are also mentioned in the README.md&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew test -DincludedGroups=identity -Drp.launch=identity_tests

./gradlew test -DincludedGroups=onboarding -Drp.launch=onboarding_tests

./gradlew test -DincludedGroups=performance -Drp.launch=performance_tests

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice here I’m filtering tests that are tagged with a certain TestNG group like identity, onboarding, and performance using &lt;code&gt;-DincludedGroups&lt;/code&gt;, and also using the &lt;code&gt;-Drp.launch&lt;/code&gt; param to give the launch (or test run) a meaningful name&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kigtan42u2lc2u4slmt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kigtan42u2lc2u4slmt.png" alt="Launches" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Attributes as metadata
&lt;/h2&gt;

&lt;p&gt;ReportPortal supports adding additional metadata as key-value pairs either via the command line or as test annotations&lt;/p&gt;

&lt;h3&gt;
  
  
  At launch
&lt;/h3&gt;

&lt;p&gt;If you open Launches, you’ll see the 3 launches show up for identity_tests, onboarding_tests, and performance_tests and they also have some default attributes or tags like those below which are specified in &lt;code&gt;reportportal.properties&lt;/code&gt; for &lt;code&gt;rp.attributes&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group:test_infra;test_type:backend

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These attributes and tags are very useful to filter launches and create informative dashboards&lt;/p&gt;

&lt;p&gt;In a real project, you may want to add even more attributes to a given test suite&lt;/p&gt;

&lt;p&gt;You can override &lt;code&gt;rp.attributes&lt;/code&gt; while running the tests via the Gradle command by appending the below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-Drp.attributes="group:test_infra;test_type:backend;team:identity"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each key: value pair is separated by a colon and delimited by a semi-colon&lt;/p&gt;

&lt;p&gt;Here we have added another attribute called team so that we can derive some team-level metrics using this&lt;/p&gt;

&lt;p&gt;Our test commands look like the below now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew test -DincludedGroups=identity -Drp.launch=identity_tests -Drp.attributes="group:test_infra;test_type:backend;team:identity" --info

./gradlew test -DincludedGroups=onboarding -Drp.launch=onboarding_tests -Drp.attributes="group:test_infra;test_type:backend;team:onboarding" --info

./gradlew test -DincludedGroups=performance -Drp.launch=performance_tests -Drp.attributes="group:test_infra;test_type:backend;team:performance" --info

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can observe that now we have another attribute at the Launch level to filter results by.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nftun7lm51clg9op5yz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nftun7lm51clg9op5yz.png" alt="Attributes on launches" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  At test
&lt;/h3&gt;

&lt;p&gt;We can also add these attributes at a test level&lt;/p&gt;

&lt;p&gt;Say, you want to store who is the on-call or maintainer of a given test so that seeing a failing test indicates the current owner for context&lt;/p&gt;

&lt;p&gt;We can annotate the test with below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Attributes(attributes = {@Attribute(key = "team", value = "identity")})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete test looks like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ReqResCreateUserTest {

   @BeforeClass(alwaysRun = true)
   public void setup() {
       RestAssured.baseURI = "https://reqres.in/api";
   }

   @Test(groups = {Team.IDENTITY, Groups.SMOKE})
   @Attributes(attributes = {@Attribute(key = "team", value = "identity")})
   public void testCreate() {
       String requestBody = "{\"name\": \"morpheus\", \"job\": \"leader\"}";

       given().contentType(ContentType.JSON)
               .body(requestBody)
               .when()
               .post("/users")
               .then()
               // TODO: Broken test example, change to 201 to fix
               .statusCode(200)
               .body("name", equalTo("morpheus"))
               .body("job", equalTo("leader"))
               .body("id", notNullValue())
               .body("createdAt", notNullValue());
   }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we look at the test in launches, you’ll notice the attribute is also stored at a test level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fwan6vuby52ras2bnnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fwan6vuby52ras2bnnf.png" alt="Attribute at test level" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is possible to add multiple attributes at a test level or have multiple keys with the same value or one key with multiple values. You can read the details in &lt;a href="https://github.com/reportportal/client-java/wiki/Test-item-attributes" rel="noopener noreferrer"&gt;test item attributes wiki from ReportPortal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are quick snippets taken fromthe above wiki for reference&lt;/p&gt;

&lt;p&gt;Many keys to one value&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
@Attributes(multiKeyAttributes = { @MultiKeyAttribute(keys = { "k1", "k2" }, value = "v") })
public void third() {
 Assert.assertEquals(1, 1);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One key to many values&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
@Attributes(multiValueAttributes = { @MultiValueAttribute(key = "k", values = { "v1", "v2" }) })
public void fourth() {
 Assert.assertEquals(1, 1);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple attributes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
@Attributes(attributes = {@Attribute(key = "key1", value = "value1"), @Attribute(key = "key2", value = "value2")})
public void sixth() {
 Assert.assertEquals(1, 1);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dashboards
&lt;/h2&gt;

&lt;p&gt;Let’s unpack the power of visualization using dashboards&lt;/p&gt;

&lt;p&gt;There are a couple of distinct personas report portal supports&lt;/p&gt;

&lt;h3&gt;
  
  
  🤹 As a leader
&lt;/h3&gt;

&lt;p&gt;I may want to see broad metrics for the 3 teams that I’m leading and identify hotspots that need attention.&lt;/p&gt;

&lt;p&gt;A few widgets make a lot of sense such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Component health check (table):&lt;/strong&gt; See component level health by splitting them using attribute &lt;strong&gt;team&lt;/strong&gt; and observe overall pass/failed metrics in a tabular format&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component health check:&lt;/strong&gt; See the split of the last test suite at a team level into visual cards with the option to open actually failed tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch statistics chart - bar view:&lt;/strong&gt; Bar chart with a summary of how many cases passed/failed/skipped&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overall statistics - donut view for latest launches:&lt;/strong&gt; Donut chart with a summary of failed/passed cases and how many cases are left to investigate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnkuuwdv3gdlvwljd079.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnkuuwdv3gdlvwljd079.png" alt="Report portal dashboards" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also see additional charts like&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test-cases growth trend chart:&lt;/strong&gt; to see how many tests were added or removed from the suites over time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failed cases trend chart:&lt;/strong&gt; to analyze the status of your broken tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8zapuywidkhnt2cmzwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8zapuywidkhnt2cmzwv.png" alt="Test growth and broken tests" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How can you configure these?&lt;/p&gt;

&lt;p&gt;The magic lies in &lt;strong&gt;creating filters&lt;/strong&gt; with a combination of certain attributes and then using them to create the visualization you desire.&lt;/p&gt;

&lt;p&gt;I promise it’s pretty intuitive once you play with it.&lt;/p&gt;

&lt;p&gt;For instance, below is how you can set up the component health check table&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select a &lt;strong&gt;filter&lt;/strong&gt; that filters all launches having &lt;strong&gt;group equal to test_infra&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;latest launches&lt;/strong&gt; to see the last launch&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;team attribute&lt;/strong&gt; as a way to split the data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79d5c1nc0siu85tr9ndr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79d5c1nc0siu85tr9ndr.png" alt="Edit component health widget" width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And say you want to configure the Overall statistics widget&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;filter&lt;/strong&gt; as &lt;strong&gt;“group as test_infra”&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Donut view&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Latest launches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And you are done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zrbskcawksosn468g58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zrbskcawksosn468g58.png" alt="Overall statistics widget configuration" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also edit the filter by adding more conditions, like below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futujnlk1st9jr44ofesr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futujnlk1st9jr44ofesr.png" alt="Edit filters" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧑🏻‍💻 As an engineer
&lt;/h3&gt;

&lt;p&gt;The previous visualizations are great for getting a bird’s eye view of how a given group with multiple teams or a product is doing.&lt;/p&gt;

&lt;p&gt;Often you as an engineer would rather focus more on how are your tests behaving and may have a team-specific or flow-specific dashboard.&lt;/p&gt;

&lt;p&gt;There are a few more dashboards that are more useful for analysis.&lt;/p&gt;

&lt;p&gt;These are focussed mostly at a launch (or one test suite level) and can be used as lower-level dashboards to drive analysis and fixes in a focussed area.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Component health check:&lt;/strong&gt; sliced for services to find the health of fine-grained services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passing rate per launch:&lt;/strong&gt; to find the overall status for the last launch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flaky test cases table (top 50):&lt;/strong&gt; identity the top flaky cases to look at&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Most failed test cases table (top 50):&lt;/strong&gt; identify all the broken tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2i1loyfrykl2uahsvzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2i1loyfrykl2uahsvzd.png" alt="Services broken and flaky" width="800" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launches duration chart:&lt;/strong&gt; to see which launches take how much time in order to find slow-running suites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Most time-consuming test cases widget (top 20):&lt;/strong&gt; to find slow-running tests to be optimised&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fluduwr3lbsy2qu5uh0ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fluduwr3lbsy2qu5uh0ym.png" alt="Test duration" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;To summarize&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Report portal is an excellent test observability solution adhering to true FOSS (free and open source software) spirit that can be installed over Docker containers on your private network&lt;/li&gt;
&lt;li&gt;Setup is quick and frictionless with running docker-compose, creating a project, getting the API key, and setting up logging integration&lt;/li&gt;
&lt;li&gt;Launches provide you last test run status with support to analyse cases&lt;/li&gt;
&lt;li&gt;Use attributes at the launch or test case level to add rich filtering capabilities&lt;/li&gt;
&lt;li&gt;Create dashboards with either aggregated metrics or low-level analysis metrics suitable both for a leader or engineer persona&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So what are you waiting for?&lt;/p&gt;

&lt;p&gt;Go ahead and have a much better picture of your test suites and keep them in good health 🫶&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwaretesting</category>
      <category>qa</category>
      <category>sdet</category>
      <category>testobservability</category>
    </item>
    <item>
      <title>🎤 Good communication makes great software engineers</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Tue, 29 Oct 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/good-communication-makes-great-software-engineers-16k4</link>
      <guid>https://dev.to/automationhacks/good-communication-makes-great-software-engineers-16k4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffodjpt14dmwwf0hd4921.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffodjpt14dmwwf0hd4921.png" alt="A man on a zoom meeting wearing a hoodie" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;How can software engineers be more confident communicators in their written, spoken and non verbal communication. This blog provides practical tips and insights from my decade plus career as a software engineer.&lt;/p&gt;

&lt;p&gt;You may have heard this anecdotal advice earlier&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Improving your communication skills will help you land senior roles and unlock career growth beyond technical skills”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What does good communication skills mean in practice?&lt;/p&gt;

&lt;p&gt;Let’s dive in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I’m not a communication expert; i’m just going to reiterate some practical advice and insights that i’ve picked up over my career as a software engineer.&lt;/p&gt;

&lt;p&gt;To judge what good looks like, observe; and just look around you.&lt;/p&gt;

&lt;p&gt;At the office, conferences, meetups or the internet and notice what makes someone stand out in their communication?&lt;/p&gt;

&lt;p&gt;You may notice the following traits&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear and concise thought process&lt;/li&gt;
&lt;li&gt;Excellent written communication - Ability to write clear messages on chat messaging, 1 pager or design docs, tech blogs&lt;/li&gt;
&lt;li&gt;Confident spoken communication - Ability to express the ideas and thoughts confidently. They do not appear confused and don’t use a lot of filler words like um, ahem, etc&lt;/li&gt;
&lt;li&gt;Positive Body language - A smile on their face, and calm demeanour even in arbitrarily challenging situations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these may be innate personality traits or a function of their environment from home, school, college or initial workplaces but most of them can also be learned, just like any other skill&lt;/p&gt;

&lt;p&gt;How do you become a better communicator?&lt;/p&gt;

&lt;p&gt;IMHO, There is no secret sauce.&lt;/p&gt;

&lt;p&gt;Good communicators are not born with it.&lt;/p&gt;

&lt;p&gt;They like many others develop and hone their craft over a no of years. Each day improving just a little bit. A large part of it is doing the work and practice. This also involves being open and willing to put yourself on shaky ground or in uncomfortable situations to learn from.&lt;/p&gt;

&lt;p&gt;As a senior engineer. I would expect you to have a solid grasp on good communication skills in 3 key areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Written&lt;/li&gt;
&lt;li&gt;Spoken&lt;/li&gt;
&lt;li&gt;Body language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s break it down shall we?&lt;/p&gt;

&lt;h2&gt;
  
  
  👩🏻‍💻 Written: Great writing is great thinking
&lt;/h2&gt;

&lt;p&gt;What can you do to improve your writing skills?&lt;/p&gt;

&lt;p&gt;Here comes the obvious advice. 😓&lt;/p&gt;

&lt;p&gt;Take the time out and write a lot.&lt;/p&gt;

&lt;p&gt;It may mean spending a little extra effort in writing the report for your stakeholders and make them more engaging without adding a ton of fluff. You may find value in reading &lt;a href="https://developers.google.com/tech-writing" rel="noopener noreferrer"&gt;Technical Writing - Google for Developers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A small tip is to always start a doc with a summary or a TL;DR (too long don’t read) and then break down your content into &lt;strong&gt;What, why, when, who, where, and how&lt;/strong&gt; (5W1H) or the standard &lt;strong&gt;problem space, solutions, metrics, and next steps&lt;/strong&gt; format.&lt;/p&gt;

&lt;p&gt;The format is not that important.&lt;/p&gt;

&lt;p&gt;You need to form a habit to write and write well.&lt;/p&gt;

&lt;p&gt;Also, feedback is a gift. Try to get your design docs reviewed from senior engineers and seek their feedback on how to improve&lt;/p&gt;

&lt;p&gt;Communication at work will only take you so far&lt;/p&gt;

&lt;p&gt;To develop as a writer you should try and write either blogs, newsletters, or short notes. These could be internal or better still a public blog where you write about whatever you are curious and passionate about.&lt;/p&gt;

&lt;p&gt;Even if one person (that’s you) reads it, it’s enough&lt;/p&gt;

&lt;p&gt;You’ll notice your written communication skill will compound over time.&lt;/p&gt;

&lt;p&gt;To build a good vocabulary, reading could be a supplement. Observe how others who write well compose their paragraphs, break down, and elicit thoughts. Initially this could help you maybe mimic their style and then develop your unique voice or writing style.&lt;/p&gt;

&lt;p&gt;If you don’t take anything else; just keep at it.&lt;/p&gt;

&lt;p&gt;If you could build a habit of writing regularly and derive some sort of satisfaction from it, you’ll pretty soon move on from mediocre write-ups to more polished ones&lt;/p&gt;

&lt;p&gt;Writing well is a super power. Especially, in the days of LLM (large language models) and Gen AI being all the buzz. Having the ability to communicate in natural english in a nuanced manner goes someway in getting better results.&lt;/p&gt;

&lt;p&gt;Also, Writing helps you slow down your thoughts and make a lot of concepts more concrete in your mind&lt;/p&gt;

&lt;p&gt;Great writing could also incorporate storytelling but it is easy to get carried away in the beginning. It’s better to focus on concise and clear writing first and then when you get a hang of the basics, move on to more advanced steps&lt;/p&gt;

&lt;p&gt;Here are some actionable tips for you to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a personal tech blog&lt;/li&gt;
&lt;li&gt;Write on the internal company blog&lt;/li&gt;
&lt;li&gt;Develop the habit of writing 1 pager or design docs for your projects and improve and iterate with feedback&lt;/li&gt;
&lt;li&gt;Learn touch typing to improve typing speed and accuracy, it will help you compound like nothing else&lt;/li&gt;
&lt;li&gt;Use a grammar and spelling checker like Grammarly or Google Docs Spell Check and internalize the writing principles and corrections they suggest.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔊 Spoken: Finding your voice
&lt;/h2&gt;

&lt;p&gt;What about being a good confident spoken communicator?&lt;/p&gt;

&lt;p&gt;This may be especially hard to develop as an introvert since you may be afraid of what others will think about you if you say something weird. Trust me. Nobody cares! Engineers’ brain space is full of problems they are solving and they often focus on their thoughts. No one notices the small stuff.&lt;/p&gt;

&lt;p&gt;Remember your peers and managers are not mind readers.&lt;/p&gt;

&lt;p&gt;If you cannot find a way to communicate your thoughts in spoken words, it is very hard to be an effective software engineer&lt;/p&gt;

&lt;p&gt;Please don’t take it the wrong way though; I’m not suggesting you become a loud-mouthed obnoxious person on the team with fake confidence&lt;/p&gt;

&lt;p&gt;I’m talking about becoming someone who really know their stuff and can also express their thoughts as well. A deadly combination if you ask me.&lt;/p&gt;

&lt;p&gt;Here is a practical tip \&lt;/p&gt;

&lt;p&gt;Don’t be a passive attendee in meetings. You may feel like you can multi-task on that coding task and get something else done, but if you think about it, it’s quite hard to do so with split attention.&lt;/p&gt;

&lt;p&gt;If you find a meeting where you are a contributor. Do a bit of research before the meeting and create some small bullet points on what points you want to discuss and then actually bring them up.&lt;/p&gt;

&lt;p&gt;This may be scary for the initial few times, but the more you do it. The more you find your voice automatically affords you a seat at the table. Your manager would be very happy with you for sure as they are often in silent rooms with less participation from team members&lt;/p&gt;

&lt;p&gt;Another way of improving spoken communication is to give talks&lt;/p&gt;

&lt;p&gt;Prepare a topic and then submit a CFP (Call for proposal) at conferences. If you are accepted as a speaker, it may be quite a jarring experience to prepare and deliver the talk but trust me you’ll love the experience afterward as folks usually come up to you after and discuss their ideas and ask their questions. The networking from these events does not hurt as well. You never know what doors open for you with each new connection.&lt;/p&gt;

&lt;p&gt;Some more actionable tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prepare for your meetings and then speak up during them&lt;/li&gt;
&lt;li&gt;Present at internal tech talks within the team, company&lt;/li&gt;
&lt;li&gt;Present externally in conferences and meetups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to sound more confident in your spoken communication?&lt;/p&gt;

&lt;p&gt;Below are few examples that illustrate how during spoken communication we do a lot of self sabotage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; You can always choose a more positive and confident tone.&lt;/p&gt;

&lt;p&gt;I’ve found &lt;a href="https://www.instagram.com/jefferson_fisher/?hl=en" rel="noopener noreferrer"&gt;short reels by jefferson fisher&lt;/a&gt; on instagram quite helpful for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Confident:&lt;/strong&gt; “Sorry, I’m not sure if this is right, but maybe we could…”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confident:&lt;/strong&gt; “I propose we consider this approach…”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Confident:&lt;/strong&gt; “This might be a silly question, but…”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confident:&lt;/strong&gt; “I have a question about…”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Confident:&lt;/strong&gt; “I’m just a junior developer, but I think…”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confident:&lt;/strong&gt; “I believe that…”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Confident:&lt;/strong&gt; “Um, I think maybe we could try using a different approach here, but I’m not sure if it would be better.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confident:&lt;/strong&gt; “I recommend we explore using a different approach here. I’ve done some preliminary research, and I believe it could improve performance.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Confident:&lt;/strong&gt; “Sorry to interrupt, but I have a question about the database schema.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confident:&lt;/strong&gt; “I’d like to raise a question about the database schema.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Confident:&lt;/strong&gt; “I don’t know if I’ll be able to finish this task on time. I’m still learning the new framework.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confident:&lt;/strong&gt; “I’m working hard to learn the new framework, and I’m committed to completing this task. However, I may need some additional support or an adjusted timeline.”&lt;/p&gt;

&lt;p&gt;What if you really don’t know?&lt;/p&gt;

&lt;p&gt;Hey, we are all there! No shame in admitting that but you don’t have to &lt;em&gt;sound overly apologetic or worse try to cover it up with some half assed answer&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You could instead say:&lt;/p&gt;

&lt;p&gt;“I don’t have the information on that right now, but I can look into it and get back to you.”&lt;/p&gt;

&lt;p&gt;“That’s a great question. I’ll need to do some research and come back on this.”&lt;/p&gt;

&lt;p&gt;“I’m not familiar with that specific aspect, but I’m happy to learn more about it.”&lt;/p&gt;

&lt;p&gt;Also, Learning good conversational patterns is also a skill. You can get better at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦿Body language: Non verbal actions matter
&lt;/h2&gt;

&lt;p&gt;This one is especially hard unless you train yourself&lt;/p&gt;

&lt;p&gt;Here are some small tips:&lt;/p&gt;

&lt;p&gt;Make eye contact and nod when you agree as you talk to someone (either in person or even on webcam in a meeting). It shows you are listening actively goes a lot towards building trust.&lt;/p&gt;

&lt;p&gt;Having an upright open posture with a smile goes a long way in establishing you as a confident individual.&lt;/p&gt;

&lt;p&gt;If you always tend to have your arms crossed or slouch a lot, you may come across as someone who is not comfortable&lt;/p&gt;

&lt;p&gt;I found a nice read in this article → &lt;a href="https://www.verywellmind.com/understand-body-language-and-facial-expressions-4147228" rel="noopener noreferrer"&gt;How to Understand Body Language and Facial Expressions&lt;/a&gt;, and i’m sure you’ll find a ton of resources on this on the internet. Please feel free to do your own research and practice having a more open and confident body language&lt;/p&gt;

&lt;p&gt;It helps!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;So let’s bring it all back now.&lt;/p&gt;

&lt;p&gt;I hope you leave with these key takeaways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Written Communication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Practice writing regularly through reports, documentation, blogs, or personal notes.&lt;/li&gt;
&lt;li&gt;Seek feedback from senior engineers to improve.&lt;/li&gt;
&lt;li&gt;Develop a clear and concise writing style.&lt;/li&gt;
&lt;li&gt;Use tools like Grammarly to improve grammar and spelling.&lt;/li&gt;
&lt;li&gt;Learn touch typing for speed and accuracy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Spoken Communication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actively participate in meetings and prepare talking points beforehand.&lt;/li&gt;
&lt;li&gt;Present at internal and external tech talks to build confidence and share knowledge.&lt;/li&gt;
&lt;li&gt;Use a confident tone and avoid filler words.&lt;/li&gt;
&lt;li&gt;Be willing to research and learn to answer questions effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Body Language:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain eye contact to build trust.&lt;/li&gt;
&lt;li&gt;Practice open posture and a friendly demeanor to project confidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now what are you waiting for. Go out there and practice. You got this! 💪&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwaretesting</category>
      <category>qa</category>
      <category>sdet</category>
      <category>careeradvice</category>
    </item>
    <item>
      <title>Is there growth in testing?</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Sat, 12 Oct 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/is-there-growth-in-testing-4hl3</link>
      <guid>https://dev.to/automationhacks/is-there-growth-in-testing-4hl3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvakddxyjex8oq0df54pt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvakddxyjex8oq0df54pt.png" alt="Image showing a boy and a girl holding apple macbooks with an upward arrow indicating growth" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Over the past month, I have mentored 3 engineers directly on topmate. It’s been a fun experiment, and I love the experience of talking to new engineers at different stages in their jobs, hearing about their challenges and perspectives, and hopefully nudging them in the right direction. If you or someone you know wants to get in touch with me, here is my topmate page: &lt;a href="https://topmate.io/automationhacks/" rel="noopener noreferrer"&gt;https://topmate.io/automationhacks/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve been getting a recurring question from them and over the years many other engineers have reached out to me on LinkedIn, and Twitter DMs, asking me what is the growth ladder for a testing professional&lt;/p&gt;

&lt;p&gt;Some variants of this question are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“I’ve heard testing maxes out at 10 years of experience. Should I move to dev or management?”&lt;/li&gt;
&lt;li&gt;“My manager is suggesting that becoming a manager is the way you can progress in your testing career”&lt;/li&gt;
&lt;li&gt;“I feel stuck and don’t know what else to explore to transition from QA automation to SDET”&lt;/li&gt;
&lt;li&gt;“Is testing a dead-end job, is there a career to be made in this?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are critical questions and each engineer is right to ask them&lt;/p&gt;

&lt;p&gt;They also paint quite a gloomy picture of the role&lt;/p&gt;

&lt;p&gt;Let me help you paint a better picture&lt;/p&gt;

&lt;h2&gt;
  
  
  🪜 Job Ladder
&lt;/h2&gt;

&lt;p&gt;There are tons of different testing setups in the industry.&lt;/p&gt;

&lt;p&gt;If you want to get a feel of it you can read &lt;a href="https://automationhacks.io/2022-11-10-deep-dive-into-evolution-of-testing-organizations" rel="noopener noreferrer"&gt;Deep dive into evolution of testing organizations&lt;/a&gt; in which I’ve explored some of these.&lt;/p&gt;

&lt;p&gt;There are 3 &lt;a href="https://automationhacks.io/2021/08/22/who-the-heck-is-an-SDET" rel="noopener noreferrer"&gt;major roles&lt;/a&gt; that people play in the testing space&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test Engineer&lt;/li&gt;
&lt;li&gt;Software engineer in Test (SDET)&lt;/li&gt;
&lt;li&gt;Engineering manager (EM)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In many companies there are well-defined career ladders for each of them, some it is a very flat org and a whole buffet of different setups in between&lt;/p&gt;

&lt;h3&gt;
  
  
  🧑🏻‍💻 Individual contributor (IC)
&lt;/h3&gt;

&lt;p&gt;I’ve seen companies have a level distribution generally like the one below for individual contributors&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intern, fresher, entry-level (0 - 2 years)&lt;/li&gt;
&lt;li&gt;Mid-level (2 - 6)&lt;/li&gt;
&lt;li&gt;Senior level (6 - 9)&lt;/li&gt;
&lt;li&gt;Staff/Lead level (8 - 12) (may have direct reports)&lt;/li&gt;
&lt;li&gt;Architect/Principal (12+) (may have direct reports)&lt;/li&gt;
&lt;li&gt;… some further levels like senior principal/architect, distinguished (this is quite rare)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small caveat:&lt;/p&gt;

&lt;p&gt;Please don’t take the years of experience thing so seriously, they are general approximations&lt;/p&gt;

&lt;p&gt;I don’t believe in the years of experience level filtering because while indicative of breadth of experience it may not convey the level of depth your company needs for a given role.&lt;/p&gt;

&lt;p&gt;The notion that years of experience automatically entitles you to a given level is a bit absurd. You may not have exposure to that level of responsibility, impact, and scale. Past performance, strong aptitude for the role, and transferable skills are probably better indicators, but hey, who am I to question everything that’s broken with recruiting and compensation 🤷&lt;/p&gt;

&lt;p&gt;In some companies, Test engineer roles are capped at a certain level, while SDET role generally has these levels.&lt;/p&gt;

&lt;p&gt;One clear signal to someone reading this is that developing software engineering skills is crucial to your long-term success as an IC&lt;/p&gt;

&lt;h3&gt;
  
  
  👩🏻‍💼 Engineering manager (EM)
&lt;/h3&gt;

&lt;p&gt;Typically there are below levels&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engineering manager (5 - 8)&lt;/li&gt;
&lt;li&gt;Senior engineering manager (8 - 12)&lt;/li&gt;
&lt;li&gt;Director (12 - 15)&lt;/li&gt;
&lt;li&gt;VP (Vice president) (15+)&lt;/li&gt;
&lt;li&gt;SVP (Senior vice president) (15+)&lt;/li&gt;
&lt;li&gt;GM (General manager) (15+)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VP+ levels are usually present in larger companies, but levels up to Director are quite common&lt;/p&gt;

&lt;p&gt;Many companies may cap the levels at a certain point depending on whether that role is required and if the company is big enough&lt;/p&gt;

&lt;p&gt;I would not go in-depth on what are the roles and expectations from each level in this blog.&lt;/p&gt;

&lt;p&gt;Any solid leadership group worth their salt would have enumerated this as an internal rubric, but let me know in the comments if you would like to hear my take on it and I can expand on this as well&lt;/p&gt;

&lt;h2&gt;
  
  
  🧗🏻 Climbing the ladder
&lt;/h2&gt;

&lt;p&gt;Your level on the engineering ladder is a strong indicator of growth.&lt;/p&gt;

&lt;p&gt;It has implications for your title, and compensation and directly influences the level of agency and responsibility you can have in the org as well as your impact&lt;/p&gt;

&lt;p&gt;What does the path look like to climb this ladder?&lt;/p&gt;

&lt;p&gt;This is a pretty deep question, it may even demand a book read like &lt;a href="https://www.amazon.in/Staff-Engineers-Path-Individual-Contributors/dp/9355421915" rel="noopener noreferrer"&gt;The Staff Engineer’s Path: A Guide for Individual Contributors Navigating Growth and Change&lt;/a&gt;or &lt;a href="https://www.amazon.in/Managers-Path-Leaders-Navigating-Growth/dp/9352135474/" rel="noopener noreferrer"&gt;The Manager’s Path: A Guide for Tech Leaders Navigating Growth and Change&lt;/a&gt; or even a new one just for professionals in testing&lt;/p&gt;

&lt;p&gt;I’ll attempt to give you an ELI5 (explain like I’m five) answer without going into deep nuances&lt;/p&gt;

&lt;p&gt;Loosely speaking,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Learn:&lt;/strong&gt; Became a deep expert in your domain, make sure you have the technical proficiency needed to do your job, which may involve learning a new technology, stack, programming language, or people skills&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute well:&lt;/strong&gt; You should clearly understand what role you are playing in your org, talk, and ensure you and your manager are on the same page on this, then go out and execute, execute, and execute. Keep on knocking off project after project and do the damn best job you can do on them with the customer in mind. Make sure you are in the top 5% of your team and try your best to create value everywhere and be a force multiplier on the team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perform at the next level:&lt;/strong&gt; Then look at your level + 1 role and expectations and ensure you take some of them on. Keep on knocking them off and present a demonstrated history of strong performance. Performance appraisals and reviews are usually lagging and if you are already performing at the next level and your manager is good, chances are you can expect a promotion soon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seek and mentor:&lt;/strong&gt; Find out the best people in your company, local region, or globally, and follow them. Learn from them and soak their wisdom. Seek out mentors discuss ideas with them and go back and implement and show hands-on value. You should also pass on what you’ve learned to help others around you get better&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This advice and general framework has personally served me quite well in my career but like I said earlier, there is tons of nuance here. Maybe I’ll write some more about these in future blogs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦖 Develop the Breath
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech
&lt;/h3&gt;

&lt;p&gt;Is climbing the ladder all there is to it?&lt;/p&gt;

&lt;p&gt;If you’ve reached the Principal/Architect level, have you made it in your testing career?&lt;/p&gt;

&lt;p&gt;What’s next?&lt;/p&gt;

&lt;p&gt;You’ll find that only seeking compensation, title, and level as proxies for growth can turn out quite shallow in the end.&lt;/p&gt;

&lt;p&gt;Please don’t get me wrong, it is super important and you need to do a good job at it.&lt;/p&gt;

&lt;p&gt;✅ Your life and your family depend on you for this. I will encourage you to keep on working on it to make sure you are rewarded and paid well for your efforts&lt;/p&gt;

&lt;p&gt;❌ But it’s not the only thing.&lt;/p&gt;

&lt;p&gt;When people say testing maxes out at X years of experience, they are taking a quite shallow view of using the job ladder as the only growth indicator&lt;/p&gt;

&lt;p&gt;But testing is not &lt;em&gt;just&lt;/em&gt; your years of experience&lt;/p&gt;

&lt;p&gt;It’s a crucial part of SDLC without which no software should ever go to production.&lt;/p&gt;

&lt;p&gt;You’ll be a fool to ignore this at your peril.&lt;/p&gt;

&lt;p&gt;There is a ton of breath to be covered and if you are a person on your way to becoming a master, you have much to learn and experience young padawan 🥷&lt;/p&gt;

&lt;p&gt;Let me attempt to give you a peek at how I think about it&lt;/p&gt;

&lt;h4&gt;
  
  
  Areas to explore
&lt;/h4&gt;

&lt;p&gt;We can divide the software engineering world into 6 broad areas&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mobile&lt;/li&gt;
&lt;li&gt;Web&lt;/li&gt;
&lt;li&gt;Backend&lt;/li&gt;
&lt;li&gt;Infra&lt;/li&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;li&gt;ML/AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these areas has opportunities to choose which layer you write tests at:&lt;/p&gt;

&lt;h4&gt;
  
  
  Test Pyramid
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Unit&lt;/li&gt;
&lt;li&gt;Integration&lt;/li&gt;
&lt;li&gt;E2E&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  And broader areas to apply to the above
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Engineering productivity&lt;/li&gt;
&lt;li&gt;Code coverage&lt;/li&gt;
&lt;li&gt;Exploratory testing&lt;/li&gt;
&lt;li&gt;Building community with like-minded peers&lt;/li&gt;
&lt;li&gt;Test frameworks&lt;/li&gt;
&lt;li&gt;Test infrastructure&lt;/li&gt;
&lt;li&gt;Test tooling&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Accessibility&lt;/li&gt;
&lt;li&gt;Chaos/Resilience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can take a cartesian product of these three and that is one unique combination for you to explore deeper&lt;/p&gt;

&lt;p&gt;E.g. Mobile test infra, data security, data performance, backend chaos/resilience, etc.&lt;/p&gt;

&lt;p&gt;It’s a whole wide world out there for your explore&lt;/p&gt;

&lt;p&gt;I have 13+ years of professional experience in testing and I can safely say I’ve just scratched the surface&lt;/p&gt;

&lt;p&gt;I also don’t think I’ll be able to explore them deeply in this lifetime but I’m still curious and choose to make incremental progress towards some of them.&lt;/p&gt;

&lt;p&gt;Each of these areas in itself could be a project for you to explore and become an expert in over a few quarters&lt;/p&gt;

&lt;p&gt;The good news is there is a ton of overlap and you can build a lot of deep software engineering skills; such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading code&lt;/li&gt;
&lt;li&gt;Writing code&lt;/li&gt;
&lt;li&gt;Understanding programming principles&lt;/li&gt;
&lt;li&gt;Understanding system design: How do DB, Queue, cache, CDN, and Gateway work and are scaled, how do you test with them&lt;/li&gt;
&lt;li&gt;Building solutions&lt;/li&gt;
&lt;li&gt;And so on …&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🍦 Soft skills
&lt;/h3&gt;

&lt;p&gt;You’ll notice that above is a list of technical things&lt;/p&gt;

&lt;p&gt;To grow into a mature leader you also need polished people skills.&lt;/p&gt;

&lt;p&gt;They are table stakes if you are in a manager role or in an IC leader role like an architect&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building trust with peers&lt;/li&gt;
&lt;li&gt;Driving cross-functional projects&lt;/li&gt;
&lt;li&gt;Project management&lt;/li&gt;
&lt;li&gt;Strong written and verbal communication skills&lt;/li&gt;
&lt;li&gt;Empathy&lt;/li&gt;
&lt;li&gt;Servant leadership&lt;/li&gt;
&lt;li&gt;Engage and develop others with 1:1 mentoring&lt;/li&gt;
&lt;li&gt;Sponsoring someone’s career growth&lt;/li&gt;
&lt;li&gt;Public speaking in internal presentations, all hands, meetups, or conferences&lt;/li&gt;
&lt;li&gt;Negotiation&lt;/li&gt;
&lt;li&gt;Conflict resolution&lt;/li&gt;
&lt;li&gt;Succession planning and removing single points of failure in teams&lt;/li&gt;
&lt;li&gt;Setting direction and strategy&lt;/li&gt;
&lt;li&gt;So on and so forth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can keep on adding to this list as well&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;Say you are someone who has worked on most of these and still feels stuck.&lt;/p&gt;

&lt;p&gt;How do you grow then?&lt;/p&gt;

&lt;p&gt;Firstly congratulations 🎊&lt;/p&gt;

&lt;p&gt;I’m sure you are in a very small company of elite people who have proven experience with all or most of these.&lt;/p&gt;

&lt;p&gt;Well,&lt;/p&gt;

&lt;p&gt;Software engineering is huge.&lt;/p&gt;

&lt;p&gt;If vertical growth is not possible, you may consider even shifting to a horizontal ladder in a different set of skills like a developer, product manager, data scientist, AI/ML engineer, etc.&lt;/p&gt;

&lt;p&gt;As you can see there is no endgame in this world and endless challenges are waiting for you with great rewards.&lt;/p&gt;

&lt;p&gt;All you can do is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work hard, have fun, and make history&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is Amazon’s motto and a pretty good way to summarise this blog&lt;/p&gt;

&lt;p&gt;Keep on delivering exceptional value for your customers, because that is what truly matters&lt;/p&gt;

&lt;p&gt;If your company does well, you will also do well and leaders around you will be happy to share more responsibilities.&lt;/p&gt;

&lt;p&gt;Growth will happen organically.&lt;/p&gt;

&lt;p&gt;Go out there, kill it!&lt;/p&gt;

&lt;p&gt;Hope this helps. Let me know in the comments if you want to share a personal journey around how you grew in software testing or what I’ve missed. I’m looking forward to learning from your thoughts&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwaretesting</category>
      <category>qa</category>
      <category>sdet</category>
      <category>careeradvice</category>
    </item>
    <item>
      <title>📛 Common terminology misses in software testing</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Thu, 26 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/common-terminology-misses-in-software-testing-5f81</link>
      <guid>https://dev.to/automationhacks/common-terminology-misses-in-software-testing-5f81</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgpiv3bb9acohb7d84v5m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgpiv3bb9acohb7d84v5m.png" alt="Image showing green tick and a red circle" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I posted about &lt;a href="https://www.linkedin.com/posts/automationhacks_softwaretesting-qa-qualityengineering-activity-7244599705110749184-DkEm?utm_source=share&amp;amp;utm_medium=member_desktop" rel="noopener noreferrer"&gt;this topic&lt;/a&gt; on LinkedIn and saw a lot of engagement. Somehow I have this eerie feeling that I’ll be pointing people to this blog in the future as well 🔮&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It’s not &lt;strong&gt;automation&lt;/strong&gt; testing, it’s &lt;strong&gt;automated&lt;/strong&gt; testing - We are not testing automation, we are automating tests&lt;/li&gt;
&lt;li&gt;It’s not &lt;strong&gt;QA&lt;/strong&gt; Automation, it’s just &lt;strong&gt;Test&lt;/strong&gt; automation or better &lt;strong&gt;Automation In Testing (AIT)&lt;/strong&gt; - we cannot fully automate quality assurance, it is a broad term and some activities will require human intuition, we can automate tests to give faster feedback at scale.&lt;/li&gt;
&lt;li&gt;It’s not a &lt;strong&gt;QA Automation engineer&lt;/strong&gt; , call them &lt;strong&gt;SDET&lt;/strong&gt; or &lt;strong&gt;Test&lt;/strong&gt; engineer please, and yes people with these titles both write automated tests but there are differences, read &lt;a href="https://automationhacks.io/2021/08/22/who-the-heck-is-an-SDET" rel="noopener noreferrer"&gt;this&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QA Engineer&lt;/strong&gt; is not the same as &lt;strong&gt;SDET&lt;/strong&gt; , again there is a difference 😉 - read blog in point 3 for more details&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flaky&lt;/strong&gt; tests are not the same as &lt;strong&gt;broken&lt;/strong&gt; tests, there is a difference 😉 - Broken tests are nice as they fail all the time, but flaky tests? not so much, sometimes they pass, sometimes they fail, but they always point to a nice problem underneath to debug, don’t ignore them, and don’t retry them 5 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assertions&lt;/strong&gt; and &lt;strong&gt;checks&lt;/strong&gt; are mostly the same! I rest my case!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality engineering&lt;/strong&gt; is loosely the same as &lt;strong&gt;QA Engineering&lt;/strong&gt; as a term to refer to the organization - dropping &lt;strong&gt;assurance&lt;/strong&gt; does not make much difference. Leadership does implicit conversion anyway and applies their old biases. Calling it Test engineering is more explicit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QA is a process, it’s not an engineer&lt;/strong&gt; - Would you refer a person with something abstract? Probably not. Calling them testers is much better and more explicit. 🙏&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% automation&lt;/strong&gt; , really? - your energy is much better focussed on ROI (return on investment) driven automation. The &lt;a href="https://en.wikipedia.org/wiki/Pareto_principle" rel="noopener noreferrer"&gt;Pareto principle&lt;/a&gt; exists for a reason. Chasing vanity metrics just leads to unnecessary suboptimal waste&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your mileage may vary. We can refer to the same thing with different labels and that’s completely okay&lt;/p&gt;

&lt;p&gt;Did I just stir up a hornet’s nest? 🫣&lt;br&gt;&lt;br&gt;
I’ve learned to not care about these much but occasionally push some buttons and I prefer some over the other. I thought I’d just put it out there.&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwaretesting</category>
      <category>qa</category>
      <category>sdet</category>
    </item>
    <item>
      <title>🫨 Should an SDET move over to SDE?</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Sun, 22 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/should-an-sdet-move-over-to-sde-5f9h</link>
      <guid>https://dev.to/automationhacks/should-an-sdet-move-over-to-sde-5f9h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtuhs8y64d8xymdbqa3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtuhs8y64d8xymdbqa3a.png" alt="Image showing a man shaking the hand of a women with SDE and SDET written on the tshirt" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This question often crosses the mind of people working as an SDET one or more times during their careers. In this blog, I offer a personal story from my own career and hopefully give you a mental model around thinking about this.&lt;/p&gt;

&lt;p&gt;Let me start with a personal story&lt;/p&gt;

&lt;h2&gt;
  
  
  Back story
&lt;/h2&gt;

&lt;p&gt;In 2018, I joined Gojek as a Product Engineer QA with around 7 years of experience. I joined as the 3rd full-time QA engineer amidst a large consultant team.&lt;/p&gt;

&lt;p&gt;During my initial month, I met another bright engineer with around 4-5 years of experience who was super passionate about Ruby programming.&lt;/p&gt;

&lt;p&gt;In Gojek, we had a Java-based mobile and API testing framework used by teams already, he wanted to build his own version in Ruby. That seemed like an audacious goal that surely would not be able to land well without some solid technical justification, leadership, and influence&lt;/p&gt;

&lt;p&gt;Oddly, I had just come from a Python background and was head over heels in love with the language, and wanted to use Python to rewrite our framework into something more simpler.&lt;/p&gt;

&lt;p&gt;Ah, the naivety and simpler times.&lt;/p&gt;

&lt;p&gt;We debated over which language was better and drew and architecture but could not come to terms with language choice among other things, and that eventually led to this idea not really materializing after a couple of weeks of effort and we both went to focus on our own teams&lt;/p&gt;

&lt;p&gt;After couple of months into my new role, I heard that person is switching over to being a backend engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧑‍💻Should I switch to dev?
&lt;/h2&gt;

&lt;p&gt;I wondered curiously if switching to a developer role makes sense.&lt;/p&gt;

&lt;p&gt;At the time, I had personally not come across a lot of examples of people who had made this transition&lt;/p&gt;

&lt;p&gt;I also did not feel strongly about making the move myself.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;It was quite simple for me&lt;/p&gt;

&lt;p&gt;I loved software testing ❤️&lt;/p&gt;

&lt;p&gt;I also loved coding ❤️&lt;/p&gt;

&lt;p&gt;I felt I was good at both&lt;/p&gt;

&lt;p&gt;This love for the craft of software testing did not come originally but I developed it organically as I grew in my career as a tester&lt;/p&gt;

&lt;p&gt;I started to appreciate learning its nuances. I loved peeling one more layer of the onion at a time, every new project or challenge helped me become better by adding another tool or approach to my toolset. I was a journeyman on the way to explore different depths of testing.&lt;/p&gt;

&lt;p&gt;I also felt that there were lots of things that I had not yet explored yet&lt;/p&gt;

&lt;p&gt;I did some personal reflection at the time and tried to imagine how my role would be different if I was to transition to a developer role&lt;/p&gt;

&lt;p&gt;I would have to get trained on one specialization like backend, mobile, web, or data and self-learn most of it myself or on the job.&lt;/p&gt;

&lt;p&gt;I would write a lot of code and get better at it&lt;/p&gt;

&lt;p&gt;Probably take care of on-calls and any production incidents and earn my keep as a mid-level developer&lt;/p&gt;

&lt;p&gt;But, It would probably mean not getting enough time to test and I would mostly be in one vertical to have any reasonable chance of being any good at it.&lt;/p&gt;

&lt;p&gt;I might switch teams and domains but the core skill would remain this at least for sometime&lt;/p&gt;

&lt;p&gt;I did not like the notion of being with one technical domain.&lt;/p&gt;

&lt;p&gt;I wanted to be able to switch between web, mobile, backend, performance, accessibility, tooling, etc, and see how I could bring my testing mindset to add value in these diverse domains&lt;/p&gt;

&lt;p&gt;Each of these areas appeared super interesting to me and I liked that as a tester and programmer in general I could learn and switch in these domains in service of testing&lt;/p&gt;

&lt;p&gt;At the time, I also personally felt that the experience I’ve accumulated to this point in testing would go wasted. That thought was probably not true. I’ve come to realize that solid testing skills are transferable and super valuable in the life of a developer as well&lt;/p&gt;

&lt;p&gt;Heck, I would not be far off to presume that software testing is more than 50% of the job of any decent developer anyway.&lt;/p&gt;

&lt;p&gt;If it’s well tested, its not really developed is it?&lt;/p&gt;

&lt;h2&gt;
  
  
  💰Hey, what about the money?
&lt;/h2&gt;

&lt;p&gt;Would I make more money as a developer?&lt;/p&gt;

&lt;p&gt;Probably!&lt;/p&gt;

&lt;p&gt;that was certainly attractive to consider.&lt;/p&gt;

&lt;p&gt;However, my 1st hike at Gojek proved this wrong as I was paid on par with developers with similar experience and levels. I also personally did not index a lot on pay but more on learning and developing myself as a professional.&lt;/p&gt;

&lt;p&gt;I was lucky that I was in a company with solid leaders who took care of compensation without me having to worry about it.&lt;/p&gt;

&lt;p&gt;So in the end it was an easy decision for me.&lt;/p&gt;

&lt;p&gt;I decided to stay in testing&lt;/p&gt;

&lt;p&gt;Within 3 years, I grew from an individual contributor (IC) as a Product engineer QA to a Lead SDET in a couple of years and then on to an Engineering manager SDET within another year managing a team of leaders and ICs&lt;/p&gt;

&lt;p&gt;It was fantastic growth. I worked with a diverse team of engineers, some into exploratory testing, and some deep into coding and building frameworks and tools. I enjoyed building a backend community with other SDETs and also forayed into public speaking at conferences, writing a blog, etc.&lt;/p&gt;

&lt;p&gt;I met talented, humble people who were all passionate about the craft of software testing and doing it well to ensure our company can succeed in its mission on providing a quality experience to our customers.&lt;/p&gt;

&lt;p&gt;It was a ton of fun&lt;/p&gt;

&lt;h2&gt;
  
  
  🤷 Who is an SDET anyway?
&lt;/h2&gt;

&lt;p&gt;I also realised that as a Test engineer turned into SDET&lt;/p&gt;

&lt;p&gt;I am not really a tester, nor a developer, and not a product manager&lt;/p&gt;

&lt;p&gt;I’m someone in between who oddly can empathize with each function and possess some of the skills each people in above roles have&lt;/p&gt;

&lt;p&gt;I have my legs in multiple worlds.&lt;/p&gt;

&lt;p&gt;I’m a developer who thinks, breathes, and eats testing for breakfast and loves it&lt;/p&gt;

&lt;p&gt;I obsess over coverage, over getting fast and reliable testing signals so that teams can make decisions to ship or not&lt;/p&gt;

&lt;p&gt;I love building scalable tooling, frameworks, and infrastructure to help everyone test better&lt;/p&gt;

&lt;p&gt;I also love understanding how the actual applications or systems work under the hood not just functionally or non-functionally but what they mean to an end user.&lt;/p&gt;

&lt;p&gt;I can think in user flows and journey touch points and also step into the details of the code and also zoom out at a user level&lt;/p&gt;

&lt;p&gt;This is a pretty amazing and very challenging role if you think about it.&lt;/p&gt;

&lt;p&gt;I wrote about all the things an SDET is expected to do in this blog, &lt;a href="https://automationhacks.io/2021/08/22/who-the-heck-is-an-SDET" rel="noopener noreferrer"&gt;Who the heck is an SDET?&lt;/a&gt; Feel free to give it a read to form a better intuition.&lt;/p&gt;

&lt;h2&gt;
  
  
  SDET vs SDE?
&lt;/h2&gt;

&lt;p&gt;In my view, we are both programmers at heart&lt;/p&gt;

&lt;p&gt;The difference lies in that we as software engineers are just focused on different things, working together to bring customer value sooner and safer&lt;/p&gt;

&lt;h3&gt;
  
  
  But really, who is an SDE?
&lt;/h3&gt;

&lt;p&gt;An SDE has lots of things on their plate&lt;/p&gt;

&lt;p&gt;They are very much focused on taking requirements from products, and businesses and using their knowledge and experience of the systems, to translate it into changes in the application.&lt;/p&gt;

&lt;p&gt;They may write new APIs, make changes to existing ones, write a new app screen, or sometimes build a completely new one from scratch. They may sometimes work on general tooling as well at CI/CD, deployment level&lt;/p&gt;

&lt;p&gt;SDE also focus on testing, they write unit tests and integration tests with mocks and may do some manual testing or occasionally write an E2E test as well.&lt;/p&gt;

&lt;p&gt;I have personally seen many SDEs who are very good testing minds as well and such individuals experience high growth in their careers since they are usually appreciated by their leaders for shipping services or apps with high-quality&lt;/p&gt;

&lt;p&gt;They also fix any bugs in the system either self-discovered or by someone other than them&lt;/p&gt;

&lt;p&gt;SDEs are experts in their own systems and sometimes get tunnel vision by focusing too much on their code where they are not able to zoom out and see the feature as a whole.&lt;/p&gt;

&lt;p&gt;It’s not really by choice, the amount of work and pace is often demanding enough and when put under the burner to deliver faster, a dev may opt for doing just good enough testing leading to missing bugs which are a factor of simulating different conditions&lt;/p&gt;

&lt;p&gt;SDE’s also take care of deployments and ensure production systems are running smoothly, occasionally resolving any production outages&lt;/p&gt;

&lt;p&gt;They focus on elegant software architectures and learn about many different technologies based on their domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about an SDET?
&lt;/h3&gt;

&lt;p&gt;You should read the above &lt;a href="https://automationhacks.io/2021/08/22/who-the-heck-is-an-SDET" rel="noopener noreferrer"&gt;blog&lt;/a&gt; to get the full description, but let me give a short summary&lt;/p&gt;

&lt;p&gt;An SDET is a developer who focuses on testing problems.&lt;/p&gt;

&lt;p&gt;This person can write application code (APIs, apps, tools, etc) if required but spends the majority of their time building frameworks, tools, and testing infrastructure to enable everyone on the team to do better efficient testing.&lt;/p&gt;

&lt;p&gt;The line is really quite thin between the two roles with a lot of overlapping skills&lt;/p&gt;

&lt;p&gt;This also means that an SDET is expected to develop generalist software engineering skills based on the domain or team they serve.&lt;/p&gt;

&lt;p&gt;If they are in a backend platform team, they should understand the backend architecture, tooling, programming language, and CI setup so that they can reason about how to test them better&lt;/p&gt;

&lt;p&gt;If they work on a mobile team, they should understand the app codebase, what testing frameworks and solutions exist, and be able to work with and enhance them&lt;/p&gt;

&lt;p&gt;In some teams they may author automated tests at unit, integration and E2E level, in some, they may not&lt;/p&gt;

&lt;p&gt;An SDET could work on different problems depending on their company size, stage, and setup&lt;/p&gt;

&lt;p&gt;Some examples of problems they usually solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build code coverage tooling&lt;/li&gt;
&lt;li&gt;Build test observability tools and solutions&lt;/li&gt;
&lt;li&gt;Bring tooling to do better testing at unit test levels like mutation, pairwise,&lt;/li&gt;
&lt;li&gt;Build distributed systems testing solutions around reliability, resilience&lt;/li&gt;
&lt;li&gt;Test boundaries of a system at scaled load&lt;/li&gt;
&lt;li&gt;Work on testing mobile, web, and backend systems: Each has its own set of nuances and challenges&lt;/li&gt;
&lt;li&gt;Tooling for security&lt;/li&gt;
&lt;li&gt;Work on efficient CI/CD systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could go on and on and on … it’s a really long list&lt;/p&gt;

&lt;p&gt;It is also hard for one person to be knowledgeable in all of these and usually requires a whole career and lots of bandwidth to develop breath as well as depth in a few&lt;/p&gt;

&lt;h2&gt;
  
  
  Also, SDET != QA?
&lt;/h2&gt;

&lt;p&gt;A slight tangent&lt;/p&gt;

&lt;p&gt;Calling an SDET a QA engineer is very confusing, and honestly changes the narrative about what they are expected to do.&lt;/p&gt;

&lt;p&gt;The testing industry has come with many different titles to describe different activities that people do and for the benefit of colleagues in general, we roll up everything to QA, more on this topic in &lt;a href="https://automationhacks.io/2020/10/08/testers-identity-crisis/" rel="noopener noreferrer"&gt;Testers identity crisis - QA/QE/AE/SDET/SET/TA?&lt;/a&gt; and &lt;a href="https://automationhacks.io/2020/02/29/the-problem-with-titles-for-testers/" rel="noopener noreferrer"&gt;The problem with titles for testers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QA (Quality assurance) is not a dirty word&lt;/p&gt;

&lt;p&gt;It has been bashed and confused a lot&lt;/p&gt;

&lt;p&gt;Often managers hear QA and they would be quick to dumb down and reduce people with this title as simple button pushers which is not true to reality.&lt;/p&gt;

&lt;p&gt;Some people find a hack to rename it to QE (Quality engineering) but that is also quite ambiguous&lt;/p&gt;

&lt;p&gt;I would just prefer if people call it what it is&lt;/p&gt;

&lt;p&gt;Test engineering&lt;/p&gt;

&lt;p&gt;We are focussing on efficient testing at scale, nothing more, nothing less.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, should you switch?
&lt;/h2&gt;

&lt;p&gt;I can’t and won’t make that decision for you.&lt;/p&gt;

&lt;p&gt;It’s personal.&lt;/p&gt;

&lt;p&gt;You have to decide for yourself as you know your own context the best&lt;/p&gt;

&lt;p&gt;Hopefully, this blog gives you some sense of what are the differences between SDE and SDET so that you can make an educated guess about what role would you prefer&lt;/p&gt;

&lt;p&gt;Also if you decide to move to a dev role, it is not a one-way door.&lt;/p&gt;

&lt;p&gt;I would rather think of it as a two-way door and if you don’t like what you see, you can always switch back as an SDET or to some other vertical.&lt;/p&gt;

&lt;p&gt;Software engineering is a wide enough field to support multiple lateral transitions and we should not see our careers as being rigid or sticking to one thing only&lt;/p&gt;

&lt;p&gt;Go on, explore. Try out different roles and see what you really fancy. It’s a whole wide world out there.&lt;/p&gt;

&lt;p&gt;Why don’t you take a leap of faith?&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwaretesting</category>
      <category>softwareengineering</category>
      <category>careeradvice</category>
      <category>qa</category>
    </item>
    <item>
      <title>⚖️ Balancing product vs platform engineering as an SDET️</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Thu, 12 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/balancing-product-vs-platform-engineering-as-an-sdet-21ha</link>
      <guid>https://dev.to/automationhacks/balancing-product-vs-platform-engineering-as-an-sdet-21ha</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yt2l7g3bv9wtjyfyw3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yt2l7g3bv9wtjyfyw3a.png" alt="Image showing a man on a balancing scale with a laptop in his hand" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Any SDET working on a product team faces this&lt;/p&gt;

&lt;p&gt;Does any of the below sound familiar?&lt;/p&gt;

&lt;p&gt;“We &lt;em&gt;want&lt;/em&gt; to focus on building tooling/frameworks and infra to enable everyone on the team to test better but we don’t have the bandwidth to build this out”&lt;/p&gt;

&lt;p&gt;“Our current setup is sub-optimal, we need things to improve, but also need to deliver on projects X, Y, and Z which are critical and time-sensitive priorities for the org”&lt;/p&gt;

&lt;p&gt;“This is part of your core competency and expectation as a senior engineer and you must dedicate X% of your time to this”&lt;/p&gt;

&lt;p&gt;“I feel overwhelmed and burned out and just don’t have the time for this, can we set realistic expectations”&lt;/p&gt;

&lt;p&gt;“There are tons of meetings every day, how can I ever have time to work on this?”&lt;/p&gt;

&lt;p&gt;“We cannot use BAU as a reason for not working on this”&lt;/p&gt;

&lt;p&gt;Sounds familiar?&lt;/p&gt;

&lt;p&gt;Some of these may be strong statements, but you get the drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this a problem? 🤔
&lt;/h2&gt;

&lt;p&gt;Yes and no! 😏&lt;/p&gt;

&lt;p&gt;If you are an SDET embedded in a product team where there are always next projects or priorities lined up for you and your company also has expectations around product engineers doing platform work as well, you are surely going to be in this situation at a certain point in your career&lt;/p&gt;

&lt;p&gt;An interesting side effect of this is that SDET on these teams &lt;strong&gt;almost start to feel guilty&lt;/strong&gt; for not being able to do platform engineering work, leading to a false sense of degraded job satisfaction, lack of motivation, and unnecessary friction toward product testing work&lt;/p&gt;

&lt;p&gt;Many engineers feel a strong pull and must work on more cross-cutting problems.&lt;/p&gt;

&lt;p&gt;Some do seem to find the time to do it as well. What are they doing differently? 😖&lt;/p&gt;

&lt;p&gt;Before we peel more layers of the onion, let’s define what we are talking about here first&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is platform engineering?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It has many names within the software testing space that I’ve come across like test infra, engineering productivity, Testing center of excellence (CoE), etc…, but the idea is quite simple at its core&lt;/p&gt;

&lt;p&gt;Engineers build testing tooling, frameworks, and infrastructure to help everyone test better thereby increasing shipping velocity without sacrificing quality.&lt;/p&gt;

&lt;p&gt;This is interesting work and is also highly valued in many big companies depending on whether leaders recognize the value of infra and tools. They know even a small productivity gain for engineers can compound massive savings and speed boosts.&lt;/p&gt;

&lt;p&gt;It’s sexy because it is complex and challenging with opportunities to learn and develop deep skills as a generalist software engineer. Who would not want to work in this space?&lt;/p&gt;

&lt;p&gt;Also, What could this work look like in practice?&lt;/p&gt;

&lt;p&gt;One month you may be building bespoke tooling for a specific org problem or another month solutions that are more cross-cutting spanning code coverage, mobile device management, Test observability, CI/CD pipelines optimizations, intuitive frameworks on the backend, frontend (mobile, web), general test tooling for data setup, test environment isolation, etc&lt;/p&gt;

&lt;p&gt;The list is long.&lt;/p&gt;

&lt;p&gt;The role demands &lt;strong&gt;dedicated bandwidth&lt;/strong&gt; , which unfortunately our engineer in this situation does not have&lt;/p&gt;

&lt;p&gt;Many companies have realized the simple fact that this is a different kind of work to product engineering work and build dedicated specialized teams to work on these and then evangelize solutions to other teams.&lt;/p&gt;

&lt;p&gt;Such teams gather feedback and then keep on iterating, hopefully making things seamless over some time. Since the nature of the work is quite ambiguous without clear ROI many times, leadership may find it difficult to justify investments in this space as well.&lt;/p&gt;

&lt;p&gt;But, I digress. That is a tangent for another time.&lt;/p&gt;

&lt;p&gt;Let’s come back to our original issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you do? 🫢
&lt;/h2&gt;

&lt;p&gt;Now that we have ample context about the problem&lt;/p&gt;

&lt;p&gt;Let’s explore some solution spaces&lt;/p&gt;

&lt;h3&gt;
  
  
  Product work is not all bad
&lt;/h3&gt;

&lt;p&gt;How you think about a problem matters&lt;/p&gt;

&lt;p&gt;I think we need to change the narrative a bit.&lt;/p&gt;

&lt;p&gt;As an SDET, we are &lt;strong&gt;accountable&lt;/strong&gt; for the quality of a given feature/group/service, etc. It is our core job role to ensure anything that is shipped is of high quality and delights the customer.&lt;/p&gt;

&lt;p&gt;Efficient testing at scale is one clear lever you have but there could be other ways as well like getting devs to help out and own a larger chunk of test authoring, doing mob testing, building automated solutions that can run continuously and provide rapid feedback as you tackle increasingly complex problems&lt;/p&gt;

&lt;p&gt;At the end of the day, You should remind yourself that your role is really in the service of the customer.&lt;/p&gt;

&lt;p&gt;You are here to act as a force multiplier and get everyone on the team excited about testing.&lt;/p&gt;

&lt;p&gt;You will need to be on top of changes, immerse yourself in context, attend meetings, influence the product roadmap, and of course handle the entire QA cycle of test planning, design, execution, automation, reporting, and bug life cycle and occasion prod issues&lt;/p&gt;

&lt;p&gt;Before graduating to platform engineering, you need to be excellent at all of the above.&lt;/p&gt;

&lt;p&gt;Please remind yourself that this work is what product, business and leadership care about.&lt;/p&gt;

&lt;p&gt;You have to make peace with this situation and dedicate a large part of your day to ensuring that this objective is served. This is full-time work and you should never feel guilty for doing this&lt;/p&gt;

&lt;p&gt;But …&lt;/p&gt;

&lt;p&gt;We need to look further as well right?&lt;/p&gt;

&lt;h3&gt;
  
  
  What about platform engineering work? 🧰
&lt;/h3&gt;

&lt;p&gt;If you think about it&lt;/p&gt;

&lt;p&gt;You practically have three options infront of you&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move to a dedicated platform engineering team 🧑‍🤝‍🧑&lt;/li&gt;
&lt;li&gt;Make focus time in your day-to-day job to work on platform projects 🧑‍💻&lt;/li&gt;
&lt;li&gt;Join/form a community and do things bottom-up 🙋&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Move&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The 1st option is a no-brainer, in the short term, it may be difficult to materialize but works out better in the longer term.&lt;/p&gt;

&lt;p&gt;If you can move to a team whose sole charter is to focus on platform engineering work within testing&lt;/p&gt;

&lt;p&gt;You will have all the bandwidth you need to pick the right problem and work on it&lt;/p&gt;

&lt;p&gt;The funded team will have a clear charter and roadmap, a set of problems they are solving, and automatic leadership buy-in.&lt;/p&gt;

&lt;p&gt;It’s also a little scary once you shift your mindset from product engineering to platform engineering. You’ll realize the patterns and mental models you have in one do not translate immediately to the other.&lt;/p&gt;

&lt;p&gt;You will need to acquire and build broader and more generalist software engineering skills. Also, develop awareness about different tools and technologies and the right problems to solve. This role requires judgement, communication, and external awareness to identify industry solutions and approaches to problems as well&lt;/p&gt;

&lt;p&gt;These like any other skills can be learned to a degree, but you need the right approach, growth mindset, and the willingness to put in the time hopefully with some experienced mentors&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Make the time 🐸&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;If option 1 is not feasible, because your team does not support internal mobility or such a group or team does not exist itself.&lt;/p&gt;

&lt;p&gt;What can you do then?&lt;/p&gt;

&lt;p&gt;You should first align with your manager that you want to spend &lt;strong&gt;X%&lt;/strong&gt; of your time on platform engineering work. In my experience asking for 20% is fair. If you think about it, it is 1 day in a week or 2 hours every day for a 40-hour work week and usually a small enough period that other stakeholders in your team will not even notice.&lt;/p&gt;

&lt;p&gt;If your manager does not care or is too much in a firefighting setup all the time. You may have an environment that is not conducive to this and then it is time to evaluate other options&lt;/p&gt;

&lt;p&gt;But assuming you have alignment&lt;/p&gt;

&lt;p&gt;When was the last time, you blocked your calendar and sat heads down to work on solving a platform problem?&lt;/p&gt;

&lt;p&gt;Think about it for a moment, before you read on.&lt;/p&gt;

&lt;p&gt;In my experience, time blocks on your calendar that you protect almost like a hawk are helpful. If you ensure to work without any distractions during this time, chances are you will make some progress for sure. People refer to this as “Eat the frog” i.e. work on the toughest part of the problem first thing in the morning when your mind is fresh.&lt;/p&gt;

&lt;p&gt;This of course requires discipline and changing a few other habits as well like mindless doom-scrolling slack messages without really getting much signal among endless noise and chatter&lt;/p&gt;

&lt;p&gt;I usually turn off notifications, kill Slack or other messaging apps, put on DND on my phone, and then get right down to business. Time block helps a little in mitigating Parkinson’s law since time-bound tasks tend to get finished than unbounded tasks.&lt;/p&gt;

&lt;p&gt;Another tip would be choosing your personal time of the day when it’s less chaotic and people are not demanding of your time will work better, so either earlier in the morning or later in the afternoon, or early evening. It depends on when you are more focused.&lt;/p&gt;

&lt;p&gt;Go on, Try it, trust me it works wonders!&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Community 🤝&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Do you have to be the hero who saves the day alone and gets all the credit?&lt;/p&gt;

&lt;p&gt;Well, No. ⛔&lt;/p&gt;

&lt;p&gt;That is not how modern teams work&lt;/p&gt;

&lt;p&gt;You may still choose to do it, but you’ll have a tougher road ahead getting people to adopt whatever you choose to build.&lt;/p&gt;

&lt;p&gt;Instead, one alternative could be to form a community of like-minded peers&lt;/p&gt;

&lt;p&gt;Talk to your peers and get a few other engineers excited about solving a problem, and then figure out times in your schedule to work on it and solve it.&lt;/p&gt;

&lt;p&gt;A small 2 pizza bottoms-up team can work wonders. You don’t need a lot of folks, just people who are really motivated.&lt;/p&gt;

&lt;p&gt;You get bonus experience in leadership, community building, and building deeper relationships with peers either in the same team or beyond your team as well. It’s a win-win!&lt;/p&gt;

&lt;p&gt;This kind of organic initiative is something leadership usually likes a lot and it’s also good for your career growth within the company since you build the org and make it stronger by opening up mentoring opportunities.&lt;/p&gt;

&lt;p&gt;Oftentimes more than the work, investing in building meaningful relationships may have an outsized long-term impact on your careers&lt;/p&gt;

&lt;p&gt;You just may make a workplace friend as well as another bonus. 🤷&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin 🐠
&lt;/h2&gt;

&lt;p&gt;I know I did not solve your problem&lt;/p&gt;

&lt;p&gt;But hopefully, it gave you some ideas on how to think about this in your day-to-day and perhaps some approaches you can try.&lt;/p&gt;

&lt;p&gt;Go ahead and try your own experiments, some may just work out. 😉&lt;/p&gt;

&lt;p&gt;Also, if you have faced this, how have you solved this in the past. Let me know your thoughts in the comments.&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>careeradvice</category>
      <category>advice</category>
    </item>
    <item>
      <title>BYOL: bring your own learning ⚡️</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Tue, 13 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/byol-bring-your-own-learning-2d7i</link>
      <guid>https://dev.to/automationhacks/byol-bring-your-own-learning-2d7i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2rxe0slmepk774ayivo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2rxe0slmepk774ayivo.png" alt="Image showing a man learning and rocket ships flying around him" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Created by microsoft copilot using DALL.E&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you work in a dynamic fast-paced company, you will learn a lot&lt;/p&gt;

&lt;p&gt;Of course, having experienced talented peers who are invested in your growth or willing to share their knowledge can also teach you a lot.&lt;/p&gt;

&lt;p&gt;Situations and challenges that you are exposed to and your personal experience trying out different approaches in different contexts also help you grow&lt;/p&gt;

&lt;p&gt;All these are good, but are they enough?&lt;/p&gt;

&lt;h2&gt;
  
  
  Your employer does not own your learning
&lt;/h2&gt;

&lt;p&gt;Now this may come as a surprise to some&lt;/p&gt;

&lt;p&gt;The company does not owe it to you to develop you completely as a professional.&lt;/p&gt;

&lt;p&gt;You’ll see ad-hoc learning and development programs that seem like an afterthought and don’t really have quality content&lt;/p&gt;

&lt;p&gt;Sometimes engineers in the community start voluntary communities like Lunch and Learn, weekly book clubs, etc but these quickly fizzle out when the original organizer and their supporters lose interest or just move on&lt;/p&gt;

&lt;p&gt;You’ll also be wise to realize that you are ultimately there to do a job&lt;/p&gt;

&lt;p&gt;Do what you are being paid to do, and do it well.&lt;/p&gt;

&lt;p&gt;Hopefully, you work on something that is intellectually interesting and challenging as well and that the company needs to meet its own goals but in many cases it may be business-as-usual tasks&lt;/p&gt;

&lt;p&gt;If you think about it, You are ultimately responsible for creating business outcomes that make the company profit and that is completely fair for a business owner to expect from an employee&lt;/p&gt;

&lt;h2&gt;
  
  
  All doom and gloom?
&lt;/h2&gt;

&lt;p&gt;In your career, some managers will prioritize your growth and learning by helping find mentors, suggesting conferences, and training, and brainstorming and following up on progress during 1:1s and some really won’t and may index more on delivery and other things that the company cares about&lt;/p&gt;

&lt;p&gt;You are lucky if you have a leader like the former.&lt;/p&gt;

&lt;p&gt;In such work environments, it is quite easy to feel stuck get demotivated, and feel like you are not growing fast enough and others are racing ahead.&lt;/p&gt;

&lt;p&gt;Social media allows for easy comparison with your peers and past colleagues and while you just see the end shiny result, you are ultimately comparing your career with the highlight reels of theirs&lt;/p&gt;

&lt;p&gt;Given all this, What would you do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You could change teams or groups&lt;/li&gt;
&lt;li&gt;You can take ownership and try to find mentors within the company yourself&lt;/li&gt;
&lt;li&gt;You could quit and move on and try to seek greener pastures elsewhere. Most of the advice on the internet suggests something along the lines of: “If you can’t change your manager; change your manager”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are they scalable?&lt;/p&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;h2&gt;
  
  
  A way out?
&lt;/h2&gt;

&lt;p&gt;What perhaps would work better is a &lt;strong&gt;deliberate consistent self-learning habit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You may counter immediately and say you don’t have time for this.&lt;/p&gt;

&lt;p&gt;Yes, you may be super busy in your off hours, have social and family obligations, etc…&lt;/p&gt;

&lt;p&gt;But if you pause and reflect, is taking out &lt;strong&gt;30-45&lt;/strong&gt; mins a day or &lt;strong&gt;a few hours over the weekend&lt;/strong&gt; that difficult? You may be wasting this time doing N other activities&lt;/p&gt;

&lt;p&gt;Building this muscle can bring you compounding effects that years of doing your routine job may not necessarily bring.&lt;/p&gt;

&lt;p&gt;Have you heard the quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Do you have 10 years experience or just 1 year repeated 10 times?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don’t know who said this first, but this was how I used to approach the initial years of my career. Living the good life, going mostly with the flow at work, etc, and complaining and sometimes wondering how others are achieving more.&lt;/p&gt;

&lt;p&gt;Until one day stopped and took control of my own learning…&lt;/p&gt;

&lt;p&gt;I changed directions toward making this change and focussed on building a learning habit often at a fixed time and tracked this in Habitica as well.&lt;/p&gt;

&lt;p&gt;I’ll say while most of the events in my career have been incremental, this habit has worked quite well for me over the years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why spend more time learning?
&lt;/h2&gt;

&lt;p&gt;If you still need a bit more context&lt;/p&gt;

&lt;p&gt;Aside from the technology landscape constantly evolving and this being just the need of the hour, why would you want to do this?&lt;/p&gt;

&lt;p&gt;Mostly because It’s super fun since you are not constrained by the tech choices, tools, environment, or language that your employer needs. You can build skills in other vertical or horizontal areas.&lt;/p&gt;

&lt;p&gt;Another side bonus is that you’ll be more confident about establishing yourself and speaking as a person who knows their stuff&lt;/p&gt;

&lt;p&gt;You may build new and unique skills by working on side projects&lt;/p&gt;

&lt;p&gt;Learning about things more deeply and hacking on side projects can be oddly very self-satisfying&lt;/p&gt;

&lt;p&gt;And who knows, you may end up using this knowledge at work sometimes in obvious or nonobvious ways as well. Growth is not always linear&lt;/p&gt;

&lt;h2&gt;
  
  
  What next?
&lt;/h2&gt;

&lt;p&gt;Also, don’t just stop at self-learning, put the &lt;a href="https://fs.blog/feynman-technique/" rel="noopener noreferrer"&gt;Feynman technique&lt;/a&gt; to work&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“You keep on learning and learning, and pretty soon you learn something no one has learned before.” ― Richard Feynman&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Bring this learning to your workplace and unique context.&lt;/li&gt;
&lt;li&gt;Discuss it with peers and see how you can implement it to make things faster, better, and more efficient&lt;/li&gt;
&lt;li&gt;Teach it to folks you are mentoring (if they are genuinely interested)&lt;/li&gt;
&lt;li&gt;Talk about it in tech talks either internally&lt;/li&gt;
&lt;li&gt;Write a blog about it&lt;/li&gt;
&lt;li&gt;Give a talk about it at a public conference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try some of these, whatever feels comfortable for you, and see how it can unlock levels of value that are not immediately obvious but you’ll see the difference slowly compounding over time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best time to start this habit was 5 years ago, but the second best time is today.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So don’t procrastinate, start now and begin anywhere!&lt;/p&gt;

&lt;p&gt;Go on, you got this!&lt;/p&gt;

&lt;p&gt;I’m rooting for you! 🤝&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>careeradvice</category>
      <category>advice</category>
    </item>
    <item>
      <title>TribeQonf 2024 Debrief</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Sun, 14 Jul 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/tribeqonf-2024-debrief-5edg</link>
      <guid>https://dev.to/automationhacks/tribeqonf-2024-debrief-5edg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt2h89cyhdu5umg99gmh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt2h89cyhdu5umg99gmh.png" alt="Image showing a persons photo speaking at conference" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The test tribe, TribeQonf 2024&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why read this?
&lt;/h2&gt;

&lt;p&gt;I had the privilege of presenting a &lt;a href="https://docs.google.com/presentation/d/e/2PACX-1vTpW84YaOPWIauhb4Iv7j1kv_iNR-PE_okDygXjgxmLs6qNd_tPPsJEvoAYsRmUTW5iG4LxxPG9-Nwv/pub?start=false&amp;amp;loop=true&amp;amp;delayms=10000" rel="noopener noreferrer"&gt;talk&lt;/a&gt; on &lt;a href="https://www.thetesttribe.com/tribeqonf/gaurav-s/" rel="noopener noreferrer"&gt;Mastering gRPC testing&lt;/a&gt; at the TribeQonf 2024 conference on 12-13th July conducted at Radisson blu in the silicon corridor ORR bengaluru.&lt;/p&gt;

&lt;p&gt;It was a fun 2 days of high energy, insights, and networking. I had the chance to hit some of the sponsor booths and chat with peer speakers and attendees, It was a pretty solid conference experience put up by The Test Tribe team&lt;/p&gt;

&lt;p&gt;In this blog, I wanted to digest and decompress some of the advice and insights that I took away. Some of these validated thoughts that I have already had while others were novel ways of looking at problems.&lt;/p&gt;

&lt;p&gt;I hope it is useful, and it would be fantastic also to hear your thoughts in the comments and if you were an attendee, what were your top takeaways.&lt;/p&gt;

&lt;p&gt;I’ll break it down into a few sections to keep it contextual&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Talks&lt;/li&gt;
&lt;li&gt;Sponsor booths and what problems they are solving&lt;/li&gt;
&lt;li&gt;Networking with peers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Talks
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Inside-out look at a Tester’s success - Pradeep Soundararajan
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.thetesttribe.com/tribeqonf/pradeep-soundararajan/" rel="noopener noreferrer"&gt;Pradeep&lt;/a&gt; called out he faced challenging conditions but was able to thrive regardless as a tester by using them to his advantage&lt;/p&gt;

&lt;p&gt;He explained how he was let go from different jobs before he was able to turn it all around by starting his own company. His journey into fitness, spirituality, and prioritizing his health with a holistic lifestyle while focusing on the 1% of things that are in your control vs all the other noise and chatter we tend to focus on really stood out to me.&lt;/p&gt;

&lt;p&gt;Whatever we achieve in our careers is not solely due to our own efforts but also due to people around us.&lt;/p&gt;

&lt;p&gt;We should focus more on living a holistic life than chasing the next promotion or title bump and deal with all the self-induced stress that comes from it.&lt;/p&gt;

&lt;p&gt;Moolya and Pradeep were also kind to provide his new book &lt;strong&gt;&lt;a href="https://www.amazon.in/Growth-Driven-Testing-startups-enterprises-ebook/dp/B0CLCPN1S4" rel="noopener noreferrer"&gt;Growth Driven Testing&lt;/a&gt;&lt;/strong&gt; as part of the attendee kit and I’m looking forward to reading and learning more from it.&lt;/p&gt;
&lt;h3&gt;
  
  
  AIoT is here testers. Are you ready for the challenge? - Vipin Jain
&lt;/h3&gt;

&lt;p&gt;Vipin broke down challenges in the IOT testing space and how there are multiple challenges in different layers of the &lt;a href="https://www.cloudflare.com/en-gb/learning/ddos/glossary/open-systems-interconnection-model-osi/" rel="noopener noreferrer"&gt;OSI model&lt;/a&gt;. He explained how data aggregation is a key process and also provided an example of how AI could play a role in HealthTech for medical report analysis, alerting and providing patients care.&lt;/p&gt;
&lt;h3&gt;
  
  
  AI-Augmented Testing: How Generative AI and Prompt Engineering Turn Testers into Superheroes, Not Replace Them - Jonathon Wright
&lt;/h3&gt;

&lt;p&gt;This talk was super high energy&lt;/p&gt;

&lt;p&gt;Jonathan played some AI-generated Hindi songs while graciously showing off his dancing skills on stage as well. No small feat if you ask me.&lt;/p&gt;

&lt;p&gt;Jonathan works at Keysight which provides a platform for test automation on hardware and sensor devices. Keysight also acquired Eggplant which provides the ability to automate UI flows using computer vision technology and provides a low code solution to create automated tests using a UI&lt;/p&gt;

&lt;p&gt;Jonathan suggested running GenAI models on local servers (or even a surface tab 😉) as a way to get leverage out of these tools without sharing your confidential data with big AI companies&lt;/p&gt;

&lt;p&gt;He also reshared Tariq King’s method of using markdown to provide a more procedural prompt to GenAI to get better results.&lt;/p&gt;

&lt;p&gt;An example of this is shared below which was also built upon by Rahul Verma in &lt;a href="https://www.linkedin.com/pulse/prompting-experimenting-markdown-example-test-data-generation-verma-toiuf/" rel="noopener noreferrer"&gt;this LinkedIn post&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# MISSION
Generate data in multiple languages for a given context.

# INPUT

1. A user registration page in a web app takes the following inputs:

* `First Name`
* `Last Name`
* `Date of Birth in dd-MonthName-year format.`
* `City`
* `Country`
* `Full Address`

2. The web site accepts data in multiple languages.

# STEPS

1. `Choose 10 natural languages which can typically have challenges in a multi-lingual websites.`
2. `Create a Language Table with Language and Challenges.`
3. `Create one example of user data for each language by incorporating the challenges identified in the context of user registration data.`

# RESPONSE FORMAT

1. `Language Challenges Table`
2. `Tests Table with Test ID, Language, First Name, Last Name and so on columns for each data item.`
3. `The response should include only the above 2 tables.`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building Harmony in Chaos: Orchestrating Agile Delivery in Startup Realms - Nishi GG
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Pace is continuous; stress should not be” - Nishi GG&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This quote stood out to me&lt;/p&gt;

&lt;p&gt;Nishi provided practical advice on how to structure agile teams to deliver while not letting stress levels rise in startups&lt;/p&gt;

&lt;p&gt;Some of her advice revolved around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template for tracking sprint items with Dev, QA estimates and 2W daily Gantt to call out when Dev/QA and other activities would take place and reduce ambiguity&lt;/li&gt;
&lt;li&gt;Identifying a clear POD structure with a short or long-term charter and continuing or dissolving them when the business outcomes are met to focus on the next problem&lt;/li&gt;
&lt;li&gt;She mentioned why reserving bandwidth of senior engineers and architects is important for multiple reasons and how it could be used for engineering productivity goals&lt;/li&gt;
&lt;li&gt;Importance of having clear focus time for teams while not neglecting play and creative time as well and how a growth pod could be set to ensure engineers get opportunities to work on horizontal items instead of their vertical initiatives&lt;/li&gt;
&lt;li&gt;She talked about pair/buddy testing as an important ingredient of a successful agile team for all the obvious benefits it could provide&lt;/li&gt;
&lt;li&gt;How &lt;strong&gt;Sprint - 1&lt;/strong&gt; automation model is followed in teams at Pictory&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Revolutionizing Quality Engineering: Breaking Orthodoxy, Shaping the Future - Sahil Puri
&lt;/h3&gt;

&lt;p&gt;Sahil’s talk was packed with insights into the challenges in quality engineering space from the lens of a realist and had suggestions on what could be some pragmatic approaches to solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quality is everyone’s responsibility 

&lt;ul&gt;
&lt;li&gt;The key point was to not be defensive about the &lt;strong&gt;“Why did we miss this bug?”&lt;/strong&gt; question but instead lead the RCA and truth seek on the gaps in your testing and delivery process and plug that leaking hole&lt;/li&gt;
&lt;li&gt;Quality is an abstract term for some, it is better to index on ensuring testing happens at all phases of the cycle by stakeholders with test professionals helping to set adequate guardrails&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Look beyond just testing 

&lt;ul&gt;
&lt;li&gt;Focus on the pre-development stage - understand the go to market (GTM) strategy, business metrics, and outcomes and feed that into your test strategy&lt;/li&gt;
&lt;li&gt;Understand system design, implementation details, and the expected scalability to do better testing around functional and non-functional requirements&lt;/li&gt;
&lt;li&gt;Keep an eye on post-launch metrics to learn whether solutions actually solved the problem and moved the metrics in the right direction, take those insights and feed them back into your delivery process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Coding dilemma 

&lt;ul&gt;
&lt;li&gt;Learn to code; it would be immensely helpful&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Speed vs quality 

&lt;ul&gt;
&lt;li&gt;It’s not always a tradeoff&lt;/li&gt;
&lt;li&gt;Teams should index towards increasing release velocity while not sacrificing product quality by the use of priority and data to identify the right scope and automated processes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Priority 

&lt;ul&gt;
&lt;li&gt;Test engineers should develop their advocacy and sales skills to ensure they are able to convey value in language that business and leadership teams understand&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Focus on breath as well as depth&lt;/li&gt;

&lt;li&gt;100% tests &lt;strong&gt;should&lt;/strong&gt; not be automated 

&lt;ul&gt;
&lt;li&gt;Focus on &lt;strong&gt;should not&lt;/strong&gt; instead of &lt;strong&gt;could not&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Focus on areas that can help you release faster with confidence instead of chasing a vanity metric&lt;/li&gt;
&lt;li&gt;Identify a clear CI/CD strategy and also understand DevOps bits to have better control over the release and quality processes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pattern Thinking for Prompt Engineers: A Case Study in Test Design - Rahul Verma
&lt;/h3&gt;

&lt;p&gt;Apart from the markdown method that Jonathan shared earlier, Rahul explained how even providing additional context by using &lt;strong&gt;markdown bold&lt;/strong&gt; could lead to better outcomes, he also showed how learning plain text diagraming tools syntax like &lt;strong&gt;Mermaid, and PlantUML&lt;/strong&gt; could be used to generate state transition tests&lt;/p&gt;

&lt;p&gt;Changing the language to passive voice could lead to better outcomes while creating custom GPTs&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigating Your Career Journey: Strategies for Growth and Success - Ajay Balamurugadas
&lt;/h3&gt;

&lt;p&gt;Ajay provided actionable advice on how career growth for engineers could look like in a very engaging talk&lt;/p&gt;

&lt;p&gt;He touched upon&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The need to build a network of peers who could act as accountability partners and help you learn 

&lt;ul&gt;
&lt;li&gt;You should ask &lt;strong&gt;“When was the last time you tried something new for the 1st time?”&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A few book recommendations - Atomic habits, Deep Work, Extreme Ownership - Be the CEO of Your Life, So Good They Can’t Ignore You&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Use of &lt;strong&gt;5W1H&lt;/strong&gt; (What, Why, when, where, Who, How) and &lt;strong&gt;5 why’s&lt;/strong&gt; as a method of thinking deeply about problems and solutions and additionally add &lt;strong&gt;else&lt;/strong&gt; and &lt;strong&gt;not&lt;/strong&gt; to be more critical in your thinking&lt;/li&gt;

&lt;li&gt;Use the disposable time during your workday to do some experiments about what activity you can do things differently.&lt;/li&gt;

&lt;li&gt;Resources: 

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/productivitytoolkit" rel="noopener noreferrer"&gt;Ultimate Productivity Toolkit - Rahul Parwal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://testwithajay.com/" rel="noopener noreferrer"&gt;Test With Ajay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.howtodeal.dev/" rel="noopener noreferrer"&gt;How to Deal with Difficult People on Software Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sponsor booths
&lt;/h2&gt;

&lt;p&gt;It was amazing to see different companies trying to leverage some form of AI, GenAI solution in order take care of generating test plans and even code using existing UI frameworks. I hope to learn more about this space in near future and hopefully build some of these tools as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  DevAssure
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.devassure.io/" rel="noopener noreferrer"&gt;DevAssure&lt;/a&gt; provides interesting oppurtunity to generate tests using GenAI taking web app screenshots and figma mocks as an input. While they are focussing on web to start with this definitely seems like a start up to watch out for.&lt;/p&gt;

&lt;h3&gt;
  
  
  BrowserStack
&lt;/h3&gt;

&lt;p&gt;Browser stack apart from their device and web farm capabilities is building tooling in test observability space with a product already out called &lt;a href="https://www.browserstack.com/test-observability" rel="noopener noreferrer"&gt;BrowserStack Test Observability&lt;/a&gt;. The demo was quite interesting and could provide better insights into test automation metrics&lt;/p&gt;

&lt;h3&gt;
  
  
  Keysight
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.keysight.com/us/en/products/software/software-testing/eggplant-test.html" rel="noopener noreferrer"&gt;Eggplant Test Automation Intelligence&lt;/a&gt; solution offers the user of computer vision to provide a smarter low code solution to automated web, desktop, and mobile apps&lt;/p&gt;

&lt;h3&gt;
  
  
  BlinqIO
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://blinq.io/" rel="noopener noreferrer"&gt;BlinqIO&lt;/a&gt; offers an AI test engineer to provide capabilities for GenAI test generation using multiple underlying LLMs for web apps and API&lt;/p&gt;

&lt;h3&gt;
  
  
  Catchpoint
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.catchpoint.com/" rel="noopener noreferrer"&gt;Catchpoint&lt;/a&gt; offers observability on network metrics like latency, response times, reliability by intrumenting mobile and backend systems. While they compete with other trace and monitoring tools like datadog and new relic. The ability to get feedback before builds are shipped to prod at different POP (points of presence) was interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Devzery
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.devzery.com/" rel="noopener noreferrer"&gt;Devzery&lt;/a&gt; solves fast autonomous API testing by leveraging GenAI to generate API tests with CI/CD integration&lt;/p&gt;

&lt;h3&gt;
  
  
  TestSigma
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://testsigma.com/" rel="noopener noreferrer"&gt;Testsigma&lt;/a&gt; provides low code solution to take care of tests on web, mobile and even have a cloud device/web farm to boast about.&lt;/p&gt;

&lt;h3&gt;
  
  
  FinerCircle
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.thetesttribe.com/finer-circle/" rel="noopener noreferrer"&gt;Finer Circle&lt;/a&gt; provides a niche community of testing professionals with courses, interest based cohorts and much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Networking
&lt;/h2&gt;

&lt;p&gt;One of the main benefits of a conference is that it brings smart like minded people under one roof and allows the merging of ideas&lt;/p&gt;

&lt;p&gt;I was able to connect with some peers and industry experts and get insights from their rich and diverse experiences.&lt;/p&gt;

&lt;p&gt;Capturing these for posterity and hopefully serve as a reminder for folks reading this to go out and talk and network with folks. You never know what these connections may manifest for you in the future.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Geosley Andrades,&lt;/strong&gt; Director AccelQ - we chatted about his experience of transiting from a testing professional to engineering manager to a director in product marketing space. How his hands on experience helps him in his new role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pradeep soundarajan&lt;/strong&gt; , CEO moolya about his personal life and early married life&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sahil puri&lt;/strong&gt; EM Zupee I enjoyed learning about his experience managing an engineering team at zupee, challenges in test automation space and QA, org dysfunctions. His talk was of course well delivered and really insightful&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shrini Kulkarni, ex Director&lt;/strong&gt; - we chatted about unit, integration testing and specifically use cases of consumer driven contract testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chakravati Singh&lt;/strong&gt; Automation Architect PhonePe about problems PhonePe is solving at mobile testing layer, pre submit stages and how PhonePe testing org has evolved. Understanding how a dedicated platform team solves automation problems was very interesting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prem Kumar Nagaraj,&lt;/strong&gt; staff engineer Razorpay about his experience building tooling at CI/CD, security tools, test selection and his prior experience at amazon&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tushar sharma&lt;/strong&gt; , growth and marketing, The Test Tribe - we talked about community building, how testing communities are evolving and what are the pain points and challenges that finer circle is solving&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sayanthan Roy,&lt;/strong&gt; Architect at Amadeus about how a plan to transition testers into automation engineers could look like, the approach and challenges that come with working with legacy frameworks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gunesh Patil&lt;/strong&gt; , Test architect, Ushur - I learned about technical challenges he’s trying to solve at his company in an interesting space on customer experience and engagement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And thats a wrap.&lt;/p&gt;

&lt;p&gt;Pretty action packed conference.&lt;/p&gt;

&lt;p&gt;I think it will take some time to digest the dense information overload and I have nothing but gratitude about getting a chance to experience this conference and would definitely encourage folks to attend this next time.&lt;/p&gt;

&lt;p&gt;Please let me know if you have questions or thoughts in the comments.&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaretesting</category>
      <category>testautomation</category>
      <category>qa</category>
    </item>
    <item>
      <title>Testing gRPC #5: Load test a gRPC service</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Sun, 02 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/testing-grpc-5-load-test-a-grpc-service-7mm</link>
      <guid>https://dev.to/automationhacks/testing-grpc-5-load-test-a-grpc-service-7mm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6gu397y4tz9pqb1aowq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6gu397y4tz9pqb1aowq.jpeg" alt="Image showing a developer coding load tests on the cloud" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image powered by DALL-E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the final blog in this series where we explored gRPC testing and automation.&lt;/p&gt;

&lt;p&gt;A quick recap, In the previous blogs in this series, we explored testing different layers of the test pyramid within gRPC context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-1-set-up-a-grpc-server" rel="noopener noreferrer"&gt;What gRPC is, using web UI to explore the API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;How to unit &lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-2-how-to-unit-test-a" rel="noopener noreferrer"&gt;test the server&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to unit test the &lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-3-how-to-unit-test-a-grpc-client" rel="noopener noreferrer"&gt;client code&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;How to &lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-4-how-to-e2e-test-a-grpc-service?utm_source=profile&amp;amp;utm_medium=reader2" rel="noopener noreferrer"&gt;E2E test a server method&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please feel free to catch up on those before reading this one.&lt;/p&gt;
&lt;h2&gt;
  
  
  What you’ll learn? 🌱
&lt;/h2&gt;

&lt;p&gt;In this blog, I’ll explore how to setup a load test written in Java leveraging locust4j library to setup a locust slave.&lt;/p&gt;

&lt;p&gt;Our slave worker will push aggregated results to a python based locust server.&lt;/p&gt;

&lt;p&gt;I hope you are also excited like I am to explore this topic.&lt;/p&gt;

&lt;p&gt;Let’s go 🏃&lt;/p&gt;
&lt;h2&gt;
  
  
  Why write load tests?
&lt;/h2&gt;

&lt;p&gt;Any modern successful business is bound to hit scale at some point in their journey. When it does, you don’t want to caught with a bad surprise wherein your systems are not able to scale, handle faults gracefully.&lt;/p&gt;

&lt;p&gt;Load testing prepares engineering teams for such events.&lt;/p&gt;

&lt;p&gt;Identifying how the system holds up with next level of scale is extremely important.&lt;/p&gt;

&lt;p&gt;A system that is not scalable, reliable or fault tolerant when put under stress would let down its users and often that means direct revenue loss or even worse losing a customers trust.&lt;/p&gt;

&lt;p&gt;Preventing such bugs to leak to production should be an acceptance criteria for any service seeing the light of the day in production.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚓ Setup locust master
&lt;/h2&gt;

&lt;p&gt;I hope we are on the same page that writing load tests are super important.&lt;/p&gt;

&lt;p&gt;Let’s focus on understanding how to do this for our route guide service&lt;/p&gt;

&lt;p&gt;We will use popular load testing framework called locust for this example. I had previously explored how to write a locust python test on a mock API. Please feel free to give it&lt;a href="https://automationhacks.io/2019/07/28/how-to-setup-a-load-test-via-locust/" rel="noopener noreferrer"&gt;a read&lt;/a&gt; if interested&lt;/p&gt;

&lt;p&gt;&lt;a href="https://automationhacks.io/2019/07/28/how-to-setup-a-load-test-via-locust/" rel="noopener noreferrer"&gt;How to setup a load test via Locust - automation hacks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will need python3 to be installed on our machine.&lt;/p&gt;

&lt;p&gt;Most modern mac comes with python3 pre-installed, if not, you can follow this wonderful guide to &lt;a href="https://docs.python-guide.org/starting/installation/" rel="noopener noreferrer"&gt;Properly Installing Python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will also setup virtualenv to install required dependency, in this case locust and will use Pipenv. You can read &lt;a href="https://automationhacks.io/2020/07/12/how-to-manage-your-python-virtualenvs-with-pipenv/" rel="noopener noreferrer"&gt;How to manage your python virtualenvs with Pipenv - Automation Hacks&lt;/a&gt; to get familiar with Pipenv&lt;/p&gt;

&lt;p&gt;Below is a short summary:&lt;/p&gt;

&lt;p&gt;Ensure you have below setup in your .zshrc, .bash_profile or in your windows system environment variables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# One time setup
# Set below in .zshrc or .bash_profile depending upon the type of shell you are using
export WORKON_HOME=~/virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next install pipenv using python3&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install pipenv

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then clone or take a pull from this repository &lt;a href="https://github.com/automationhacks/grasp-grpc" rel="noopener noreferrer"&gt;GitHub - automationhacks/grasp-grpc: Ports routeguide example service from grpc-java repository and adds functional and non functional API tests&lt;/a&gt; and ensure you cd to folder with Pipfile&lt;/p&gt;

&lt;p&gt;Run below to start virtualenv&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipenv shell

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we’ll start our locust master server using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;locust -f locust_master.py --master --master-bind-host=127.0.0.1 --master-bind-port=5557

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll see something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2024-05-31 08:29:15,073] Gauravs-Laptop/INFO/locust.main: Starting web interface at http://0.0.0.0:8089
[2024-05-31 08:29:15,078] Gauravs-Laptop/INFO/locust.main: Starting Locust 2.28.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does this command do?&lt;/p&gt;

&lt;p&gt;Here we are running a dummy locust server in &lt;strong&gt;master&lt;/strong&gt; mode and binding the server to &lt;strong&gt;localhost&lt;/strong&gt; and a random port &lt;strong&gt;5557&lt;/strong&gt;. In a production setup, this script could be running on a remote server which we would use in our tests.&lt;/p&gt;

&lt;p&gt;Below is how &lt;a href="https://github.com/automationhacks/grasp-grpc/blob/main/locust_master.py" rel="noopener noreferrer"&gt;locust_master.py&lt;/a&gt; looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from locust import User, TaskSet, task

class DummyTask(TaskSet):
   @task(1)
   def dummy(self):
       pass

class Dummy(User):
   tasks = [DummyTask]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we init &lt;strong&gt;DummyTask&lt;/strong&gt; as a sub class of &lt;strong&gt;TaskSet&lt;/strong&gt; and have a method dummy with a weight of 1 using the &lt;strong&gt;@task()&lt;/strong&gt; annotation.&lt;/p&gt;

&lt;p&gt;Weight is a way in locust to increase the probability of a certain task to be picked ahead of others. In this case, we have a single task so it does not really matter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class DummyTask(TaskSet):
   @task(1)
   def dummy(self):
       pass

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also have the Dummy class of type User with our tasks list with this DummyTask&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Dummy(User):
   tasks = [DummyTask]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generate required files using proto compiler
&lt;/h2&gt;

&lt;p&gt;Ensure all relevant files are generated using gRPC proto compiler by executing below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew installDist

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You only need to run this if you’ve made some changes to the proto message or RPC. This step could be skipped if there are no changes. In a prod setup, you may have a automation process that updates dependencies as part of a CI/CD pipeline&lt;/p&gt;

&lt;h2&gt;
  
  
  Start gRPC server
&lt;/h2&gt;

&lt;p&gt;Next, let’s ensure our server and API is up by executing below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./build/install/grasp-grpc/bin/route-guide-server

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll see something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
May 31, 2024 8:34:47 AM io.automationhacks.routeguide.RouteGuideServer start
INFO: Server started, listening on 8980

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Writing a locust slave
&lt;/h2&gt;

&lt;p&gt;The key component of this load test is a locust slave written in Java that can be used by locust4j to start load test.&lt;/p&gt;

&lt;p&gt;I’ve added an example slave in &lt;a href="https://github.com/automationhacks/grasp-grpc/blob/33726eac548214c586e9d372fe9b998733f45d8a/src/main/java/io/automationhacks/routeguide/perf/RouteGuideLoadGen.java#L8" rel="noopener noreferrer"&gt;RouteGuideLoadGen.java&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it looks like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package io.automationhacks.routeguide.perf;

import com.github.myzhan.locust4j.Locust;
import io.automationhacks.routeguide.perf.tasks.GetFeatureTask;

import static io.automationhacks.routeguide.constants.Constants.*;

public class RouteGuideLoadGen {

 public void getFeatureLoad() {
   var locust = configureLocustMaster();

   locust.run(new GetFeatureTask(1));
 }

 private Locust configureLocustMaster() {
   var locust = Locust.getInstance();

   locust.setMasterHost(LOCUST_MASTER_HOST);
   locust.setMasterPort(LOCUST_MASTER_PORT);
   locust.setMaxRPS(LOCUST_MAX_RPS);

   return locust;
 }

 public static void main(String[] args) {
   new RouteGuideLoadGen().getFeatureLoad();
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s unpack this&lt;/p&gt;

&lt;p&gt;We have a method to configure our slave worker with the locust master instance&lt;/p&gt;

&lt;p&gt;We then invoke the run method and provide it a task instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void getFeatureLoad() {
   var locust = configureLocustMaster();

   locust.run(new GetFeatureTask(1));
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also use the below method to get a Locust instance and set the master host, port as well as the peak requests per second (RPS) for the slave.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private Locust configureLocustMaster() {
   var locust = Locust.getInstance();

   locust.setMasterHost(LOCUST_MASTER_HOST);
   locust.setMasterPort(LOCUST_MASTER_PORT);
   locust.setMaxRPS(LOCUST_MAX_RPS);

   return locust;
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Locust load task
&lt;/h2&gt;

&lt;p&gt;We also have to write a Locust task that has the actual gRPC API that we want to invoke.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class GetFeatureTask extends AbstractTask {
 private final int weight;

 RouteGuideTestClient client =
     new RouteGuideTestClient(ROUTE_GUIDE_SERVER_HOST, ROUTE_GUIDE_SERVER_PORT);

 public GetFeatureTask(int weight) {
   this.weight = weight;
 }

 @Override
 public int getWeight() {
   return weight;
 }

 public String getName() {
   return "RouteGuide.GetFeature";
 }

 public void execute() {
   int latitude = 407838351;
   int longitude = -746143763;

   Point point = Point.newBuilder().setLatitude(latitude).setLongitude(longitude).build();

   // Record the startTime for the API call
   var startTime = getTime();
   // Add thread safe logic here to make the API call
   Feature response = client.getFeature(point);
   // Record the endTime for the API call
   var endTime = getTime();

   // Do not swallow any exceptions from the API, we should propagate any exceptions to locust
   assertWithMessage(
           "Could not find the feature at lat: %s long: %s".formatted(latitude, longitude))
       .that(response.getName())
       .isEqualTo("Patriots Path, Mendham, NJ 07945, USA");

   // Record success or failure to locust
   long responseTime = endTime - startTime;
   int contentLength = response.getName().length();

   Locust.getInstance()
       .recordSuccess("GetFeature", response.getName(), responseTime, contentLength);
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above is the complete method, let’s understand how this works&lt;/p&gt;

&lt;p&gt;We create a task &lt;strong&gt;GetFeatureTask&lt;/strong&gt; that extends &lt;strong&gt;AbstractTask&lt;/strong&gt; from locust4j&lt;/p&gt;

&lt;p&gt;We configure the local Grpc client&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RouteGuideTestClient client =
     new RouteGuideTestClient(ROUTE_GUIDE_SERVER_HOST, ROUTE_GUIDE_SERVER_PORT);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And also configure a constructor and getter to set custom weight if we want from the caller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public GetFeatureTask(int weight) {
   this.weight = weight;
 }

 @Override
 public int getWeight() {
   return weight;
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We set the name to be used during the load test&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public String getName() {
   return "RouteGuide.GetFeature";
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally the execute method has the actual logic wherein we invoke the locally running service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void execute() {
   int latitude = 407838351;
   int longitude = -746143763;

   Point point = Point.newBuilder().setLatitude(latitude).setLongitude(longitude).build();

   // Record the startTime for the API call
   var startTime = getTime();
   // Add thread safe logic here to make the API call
   Feature response = client.getFeature(point);
   // Record the endTime for the API call
   var endTime = getTime();

   // Do not swallow any exceptions from the API, we should propagate any exceptions to locust
   assertWithMessage(
           "Could not find the feature at lat: %s long: %s".formatted(latitude, longitude))
       .that(response.getName())
       .isEqualTo("Patriots Path, Mendham, NJ 07945, USA");

   // Record success or failure to locust
   long responseTime = endTime - startTime;
   int contentLength = response.getName().length();

   Locust.getInstance()
       .recordSuccess("GetFeature", response.getName(), responseTime, contentLength);
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An interesting thing to note is that we capture the total time taken for the API call and response to come back and then later on persist it in the Locust instance using below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Locust.getInstance()
       .recordSuccess("GetFeature", response.getName(), responseTime, contentLength);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start the load test
&lt;/h2&gt;

&lt;p&gt;We can run &lt;a href="https://github.com/automationhacks/grasp-grpc/blob/main/src/main/java/io/automationhacks/routeguide/perf/RouteGuideLoadGen.java" rel="noopener noreferrer"&gt;RouteGuideLoadGen.java&lt;/a&gt; to start the slave and register it with the master.&lt;/p&gt;

&lt;p&gt;If we navigate to the locust master host running at &lt;a href="http://0.0.0.0:8089" rel="noopener noreferrer"&gt;http://0.0.0.0:8089&lt;/a&gt;, we can see below UI&lt;/p&gt;

&lt;p&gt;Here we’ll be kind and just use 5 users with a ramp up of 1 user per second.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jc3fv7qxhwubl8knpo3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jc3fv7qxhwubl8knpo3.png" alt="Locust home page" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see the charts with real time performance RPS and failure stats and other diagnostic data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faatjd805d0fglwgpu9wh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faatjd805d0fglwgpu9wh.png" alt="Locust charts" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workers tab also shows the current no of workers registered&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa64j5o52jhe15ox516s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa64j5o52jhe15ox516s.png" alt="Locust workers" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we can also see stats about the overall test&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgx03m1jbtmeh7t9wmel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgx03m1jbtmeh7t9wmel.png" alt="Locust stats" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since this is a local test running on my machine, I also used the Activity monitor app and could see spike in CPU and memory usage stats while the test was running. Within a short time, I bombarded 40K RPS on my locally hosted service. Surely a pretty sizable scale for a single service. 🤩&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Standing on the shoulder of giants.&lt;/p&gt;

&lt;p&gt;I referred to below resources to come up with this write up. Hopefully this is useful for your further exploration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.locust.io/en/stable/testing-other-systems.html#grpc" rel="noopener noreferrer"&gt;Locust python notes on Grpc testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/grpc/test-infra" rel="noopener noreferrer"&gt;Grpc Test Infra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Distributed Load Testing of &lt;a href="https://github.com/pedrodeoliveira/locust-rest-grpc" rel="noopener noreferrer"&gt;REST/gRPC APIs using Locust&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.blazemeter.com/blog/locust-performance-testing" rel="noopener noreferrer"&gt;Blog from blazemeter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/myzhan/locust4j" rel="noopener noreferrer"&gt;Locust4j&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Example &lt;a href="https://github.com/myzhan/locust4j/blob/master/examples/task/Main.java" rel="noopener noreferrer"&gt;Main.java file to start a locust test&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Locust &lt;a href="https://github.com/nejckorasa/locust4j-http-load" rel="noopener noreferrer"&gt;Http load example project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Locust Http &lt;a href="https://github.com/nejckorasa/locust4j-http-load" rel="noopener noreferrer"&gt;load another example project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;wrk an &lt;a href="https://github.com/wg/wrk" rel="noopener noreferrer"&gt;example HTTP benchmarking tool&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was a short introduction to the world of load testing with locust and demos how you are not really limited to knowing python to leverage this wonderful tool. I hope you’ll give it a try and explore the nitty gritties of this tool&lt;/p&gt;

&lt;p&gt;To summarize, we learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can write locust load test for a gRPC service using Java with a slave to Locust master setup&lt;/li&gt;
&lt;li&gt;How to setup locust master in python&lt;/li&gt;
&lt;li&gt;How to generate required build file for Java gRPC service&lt;/li&gt;
&lt;li&gt;Start a local gRPC server&lt;/li&gt;
&lt;li&gt;Write a utility to setup a Locust Java slave worker&lt;/li&gt;
&lt;li&gt;Start execution and observe metrics on Locust web UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please let me know if you have questions or thoughts in the comments.&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please subscribe to the substack &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; and follow my YouTube channel ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaretesting</category>
      <category>testautomation</category>
      <category>grpc</category>
    </item>
    <item>
      <title>Testing gRPC #4: How to E2E test a gRPC service</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Wed, 20 Mar 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/testing-grpc-4-how-to-e2e-test-a-grpc-service-4lc8</link>
      <guid>https://dev.to/automationhacks/testing-grpc-4-how-to-e2e-test-a-grpc-service-4lc8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdh4pgk921g6ovbtu3yc4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdh4pgk921g6ovbtu3yc4.png" alt="Image showing a male developers coding APIs in a futuristic space station" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image powered by DALL-E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the previous blogs in this series, we learned &lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-1-set-up-a-grpc-server" rel="noopener noreferrer"&gt;what gRPC is, using web UI to explore the API&lt;/a&gt; and then how to unit &lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-2-how-to-unit-test-a" rel="noopener noreferrer"&gt;test the server&lt;/a&gt; and &lt;a href="https://newsletter.automationhacks.io/p/testing-grpc-3-how-to-unit-test-a-grpc-client" rel="noopener noreferrer"&gt;client code&lt;/a&gt;. Please feel free to catch up on those before reading this one.&lt;/p&gt;
&lt;h2&gt;
  
  
  What you’ll learn? 🌱
&lt;/h2&gt;

&lt;p&gt;In this article, I’m gonna walk you through how to write a functional API test for a gRPC API using the live service. It’s super important to write these end-to-end tests to make sure your API works the way it’s supposed to. We’ll start with setting up the codebase and then create a basic test client to talk to the gRPC API.&lt;/p&gt;

&lt;p&gt;After that, I’ll show you an example of a functional API end-to-end test using TestNG and how to check if the API is responding properly. Let’s go! ⚡&lt;/p&gt;
&lt;h2&gt;
  
  
  Why write E2E tests?
&lt;/h2&gt;

&lt;p&gt;Writing unit tests is probably the fastest way to get quick feedback about your services and codebase health. They are the lowest fidelity and fastest among different types of tests that you can write.&lt;/p&gt;

&lt;p&gt;However, would you be confident in shipping a unit-tested API directly to production?&lt;/p&gt;

&lt;p&gt;Probably not!&lt;/p&gt;

&lt;p&gt;You still would want confidence that the API works functionally.&lt;/p&gt;

&lt;p&gt;Let’s assume your API is consumed by an upstream API, in such a case, you would want to make sure your API accepts valid input as per the contract that you’ve agreed with the dependant team and returns expected responses to the upstream service in positive, negative and edge test cases&lt;/p&gt;

&lt;p&gt;You can get some of this isolated feedback with an integration test as well, however, if the workflow is complicated and you have multiple dependencies between APIs and databases, then writing integration tests could be tedious at best.&lt;/p&gt;

&lt;p&gt;That’s where a bunch of E2E tests come pretty handy.&lt;/p&gt;

&lt;p&gt;While they run slower than a unit or integration test, in return you get realistic feedback about the state of your API, workflows, etc.&lt;/p&gt;

&lt;p&gt;In this post, we’ll build on top of what we’ve learnt so far and explore how we can write functional API tests for a gRPC service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hint ⚡&lt;/strong&gt; : It’s not so much different 😉&lt;/p&gt;
&lt;h2&gt;
  
  
  Set up repo and codebase ⚙️
&lt;/h2&gt;

&lt;p&gt;For this next section of the blog, I’ve copied the relevant files from &lt;a href="https://github.com/grpc/grpc-java/tree/master/examples/src/main/java/io/grpc/examples/routeguide" rel="noopener noreferrer"&gt;grpc-java&lt;/a&gt; and created a new standalone project in my Github account that only covers &lt;strong&gt;routeguide service&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This should help make it easier for someone new to not be overwhelmed by the huge project structure and seeing a lot of unrelated complex code.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/automationhacks/grasp-grpc" rel="noopener noreferrer"&gt;https://github.com/automationhacks/grasp-grpc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will clone the repository and set up a new project in IntelliJ and ensure the build the successful by running these commands:&lt;/p&gt;

&lt;p&gt;Using SSH&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git@github.com:automationhacks/grasp-grpc.git

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Github CLI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gh repo clone automationhacks/grasp-grpc

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s check we can build the project without running tests for now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew clean build -x test

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s ensure that we have all the classes generated by building the project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew installDist

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s start our test server by running below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./build/install/grasp-grpc/bin/route-guide-server

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that our server is running at localhost:8980 port&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mar 19, 2024 9:52:42 PM io.automationhacks.routeguide.RouteGuideServer start
INFO: Server started, listening on 8980

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Write an API test client
&lt;/h2&gt;

&lt;p&gt;When we test a live API, it is usually hosted at some specific host and port on infrastructure that could be either our company local data center or cloud platforms like Google cloud, AWS, Azure, etc.&lt;/p&gt;

&lt;p&gt;We’ll write a test client for our E2E API tests so that we can invoke our gRPC APIs and also maintain this as we add more functions&lt;/p&gt;

&lt;p&gt;Below is how we can create a simple client with one method for getFeature() service method&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/automationhacks/grasp-grpc/blob/main/src/main/java/io/automationhacks/routeguide/RouteGuideTestClient.java" rel="noopener noreferrer"&gt;RouteGuideTestClient.java&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package io.automationhacks.routeguide;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import java.util.logging.Logger;

public class RouteGuideTestClient {

 private final Logger logger = Logger.getLogger(RouteGuideTestClient.class.getName());
 private final RouteGuideGrpc.RouteGuideBlockingStub blockingStub;

 public RouteGuideTestClient(String host, int port) {
   ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
   blockingStub = RouteGuideGrpc.newBlockingStub(channel);
 }

 public Feature getFeature(Point point) {
   return blockingStub.getFeature(point);
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s unpack this,&lt;/p&gt;

&lt;p&gt;We initialize RouteGuideBlockingStub to allow us to make API calls&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private final RouteGuideGrpc.RouteGuideBlockingStub blockingStub;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the constructor, we accept the host and port where the server is running and then use ManagedChannelBuilder.forAddress() method to get a ManagedChannel instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public RouteGuideTestClient(String host, int port) {
   ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then use this channel to initialize our blockingStub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;blockingStub = RouteGuideGrpc.newBlockingStub(channel);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then write a method for our &lt;strong&gt;getFeature()&lt;/strong&gt; API that accepts a &lt;strong&gt;Point&lt;/strong&gt; and then returns the &lt;strong&gt;Feature&lt;/strong&gt; at that specific point&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Feature getFeature(Point point) {
   return blockingStub.getFeature(point);
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we can go ahead and implement other service methods, this single fine for now and we’ll implement them in the future while understanding other testing concepts&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing an API E2E test
&lt;/h2&gt;

&lt;p&gt;Let’s write a functional API test to verify the getFeature() service method is working fine.&lt;/p&gt;

&lt;p&gt;Below is the complete test which you can also find in examples/src/test/java/io/grpc/examples/routeguide directory in the codebase&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/automationhacks/grasp-grpc/blob/main/src/test/java/io/automationhacks/routeguide/RouteGuideE2ETest.java" rel="noopener noreferrer"&gt;RouteGuideE2ETest.java&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package io.automationhacks.routeguide;

import static com.google.common.truth.Truth.assertWithMessage;

import org.testng.annotations.Test;

public class RouteGuideE2ETest {

 private static final String HOST = "localhost";
 private static final int PORT = 8980;

 @Test
 public void testGetFeature() {
   System.out.println("Executed testGetFeature()");
   RouteGuideTestClient client = new RouteGuideTestClient(HOST, PORT);

   int latitude = 407838351;
   int longitude = -746143763;

   Point point = Point.newBuilder().setLatitude(latitude).setLongitude(longitude).build();
   Feature response = client.getFeature(point);

   assertWithMessage(
           "Could not find the feature at lat: %s long: %s".formatted(latitude, longitude))
       .that(response.getName())
       .isEqualTo("Patriots Path, Mendham, NJ 07945, USA");
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s understand our test&lt;/p&gt;

&lt;p&gt;Within our test, we have specified our host and port where our server is running&lt;/p&gt;

&lt;p&gt;⚡ In a real framework, it’s a good idea to store these in an environment properties file and read it using Java classes to manage system properties. This would ensure if the host/port changes, we only have to change properties file, or build in flexibility to make the test environment agnostic&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static final String HOST = "localhost";
private static final int PORT = 8980;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll use TestNG test runner to run our E2E tests and we specify the test method by annotating our function with &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/test"&gt;@test&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
 public void testGetFeature() {}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make the API call, we need to initialize the test client that we had earlier created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RouteGuideTestClient client = new RouteGuideTestClient(HOST, PORT);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the builder provided by protocol buffers to create a point object and set latitude and longitude&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int latitude = 407838351;
int longitude = -746143763;

Point point = Point.newBuilder().setLatitude(latitude).setLongitude(longitude).build();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then make the API call via the client like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature response = client.getFeature(point);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we assert if the response name provided matches our expectation, we use fluent assertions from &lt;a href="https://truth.dev/" rel="noopener noreferrer"&gt;Google truth library&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   assertWithMessage(
           "Could not find the feature at lat: %s long: %s".formatted(latitude, longitude))
       .that(response.getName())
       .isEqualTo("Patriots Path, Mendham, NJ 07945, USA");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do we know this latitude/longitude would return this feature name?&lt;/p&gt;

&lt;p&gt;When the server starts, we read from a JSON file specified called &lt;a href="https://github.com/automationhacks/grasp-grpc/blob/3199fb54471aea98468efc7d271fe6efcfb80156/src/main/resources/io/automationhacks/routeguide/route_guide_db.json#L4" rel="noopener noreferrer"&gt;route_guide_db.json&lt;/a&gt; and store it in the in-memory collection.&lt;/p&gt;

&lt;p&gt;RouteGuideServer.java&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public RouteGuideServer(int port) throws IOException {
 this(port, RouteGuideUtil.getDefaultFeaturesFile());
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s run our tests using below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew clean runTests

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see our test was executed since it printed our logger entry Executed testGetFeature() and our test passed 🎀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Task :runTests

Gradle Test Executor 15 STANDARD_ERROR
    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

Gradle suite &amp;gt; Gradle test &amp;gt; io.automationhacks.routeguide.RouteGuideE2ETest &amp;gt; testGetFeature STANDARD_OUT
    Executed testGetFeature()

BUILD SUCCESSFUL in 2s
10 actionable tasks: 10 executed

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now you are well equipped to go ahead and write many E2E API tests for your gRPC APIs, so what are you waiting for? Go ahead and test some gRPC APIs 😀&lt;/p&gt;

&lt;p&gt;Please let me know if you have questions or thoughts in the comments.&lt;/p&gt;

&lt;p&gt;In the next post, we will grasp how to write a &lt;strong&gt;Non functional load test on our API,&lt;/strong&gt; we’ll attempt to gain more confidence that the service works when dealing with live load.&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please share it and follow me ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaretesting</category>
      <category>testautomation</category>
      <category>grpc</category>
    </item>
    <item>
      <title>Testing gRPC #3: How to unit test a gRPC client</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Thu, 07 Mar 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/testing-grpc-3-how-to-unit-test-a-grpc-client-4go4</link>
      <guid>https://dev.to/automationhacks/testing-grpc-3-how-to-unit-test-a-grpc-client-4go4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fehho6bus98bqizif0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fehho6bus98bqizif0p.png" alt="Image showing male and female developers coding APIs with a screen at the back and some icons like Java etc" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image powered by DALL-E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;⏪ Recap:&lt;/p&gt;

&lt;p&gt;In the previous blog, we understood how to write a unit test for a gRPC server using InProcessChannel, and InProcessServer and how to set automatic cleanup using GrpcCleanupRule. In case you missed it, please feel free to read it &lt;a href="https://automationhacks.io/2024-02-20-testing-grpc-2-how-to-unit-test-a-grpc-server" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⏩ What we’ll learn:&lt;/p&gt;

&lt;p&gt;In this blog, we’ll understand how a client could be written for our Grpc service and also how can we unit test that.&lt;/p&gt;

&lt;p&gt;Let’s go ⚡&lt;/p&gt;
&lt;h2&gt;
  
  
  System under test
&lt;/h2&gt;

&lt;p&gt;Before we understand how the client is written, let’s first grasp how the service is implemented for this method since we have to write a client method for essentially this.&lt;/p&gt;

&lt;p&gt;You can see the complete &lt;strong&gt;listFeatures()&lt;/strong&gt; method in &lt;strong&gt;RouteGuideServer.java&lt;/strong&gt; below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RouteGuideServer.java

@Override
public void listFeatures(Rectangle request, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {
 int left = min(request.getLo().getLongitude(), request.getHi().getLongitude());
 int right = max(request.getLo().getLongitude(), request.getHi().getLongitude());
 int top = max(request.getLo().getLatitude(), request.getHi().getLatitude());
 int bottom = min(request.getLo().getLatitude(), request.getHi().getLatitude());

 for (Feature feature : features) {
   if (!RouteGuideUtil.exists(feature)) {
     continue;
   }

   int lat = feature.getLocation().getLatitude();
   int lon = feature.getLocation().getLongitude();
   if (lon &amp;gt;= left &amp;amp;&amp;amp; lon &amp;lt;= right &amp;amp;&amp;amp; lat &amp;gt;= bottom &amp;amp;&amp;amp; lat &amp;lt;= top) {
     responseObserver.onNext(feature);
   }
 }
 responseObserver.onCompleted();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s walk through the code and grasp how this server method is implemented&lt;/p&gt;

&lt;p&gt;If you observe the method signature, We pass in a &lt;code&gt;Rectangle request&lt;/code&gt; and &lt;code&gt;StreamObserver&amp;lt;Feature&amp;gt; responseObserver&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We know from &lt;strong&gt;route_guide.proto&lt;/strong&gt; , that &lt;strong&gt;Rectangle&lt;/strong&gt; is a collection of 2 &lt;strong&gt;Point&lt;/strong&gt; (lo and hi), such that each point has a &lt;strong&gt;latitude&lt;/strong&gt; and &lt;strong&gt;longitude&lt;/strong&gt;. This represents a bounding rectangle in a map. Think of a rectangle that covers the central part of Bangalore (of course, there could be much better representation to create a geofence but for the sake of simplicity let’s go with this 😏)&lt;/p&gt;

&lt;p&gt;Below is the proto in case you need a recap.&lt;/p&gt;

&lt;p&gt;route_guide.proto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
 int32 latitude = 1;
 int32 longitude = 2;
}

// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
message Rectangle {
 // One corner of the rectangle.
 Point lo = 1;

 // The other corner of the rectangle.
 Point hi = 2;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also pass in &lt;code&gt;StreamObserver&amp;lt;Feature&amp;gt; responseObserver&lt;/code&gt; which is used to return a stream of &lt;strong&gt;Feature&lt;/strong&gt; objects to the client. The stream could loosely be understood as an array that the client can read either all at once or one object at a time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void listFeatures(Rectangle request, StreamObserver&amp;lt;Feature&amp;gt; responseObserver)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s look at the service methods body, We then extract 4 corners of the rectangle by finding min and max values from lo and hi longitude and latitudes to get an integer that represents each corner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; int left = min(request.getLo().getLongitude(), request.getHi().getLongitude());
 int right = max(request.getLo().getLongitude(), request.getHi().getLongitude());
 int top = max(request.getLo().getLatitude(), request.getHi().getLatitude());
 int bottom = min(request.getLo().getLatitude(), request.getHi().getLatitude());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice, We then iterate in the features already stored in our database (in this example, the features are stored in an in-memory Collection but in a real application this could be any other data store) and check if a feature exists&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (Feature feature : features) {
   if (!RouteGuideUtil.exists(feature)) {
     continue;
   }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see the &lt;strong&gt;exists()&lt;/strong&gt; it just checks if the feature has a valid name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RouteGuideUtil.java

/**
* Indicates whether the given feature exists (i.e. has a valid name).
*/
public static boolean exists(Feature feature) {
 return feature != null &amp;amp;&amp;amp; !feature.getName().isEmpty();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the feature exists then we check&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if that feature longitude lies between left and right ranges&lt;/li&gt;
&lt;li&gt;and latitude lies between the bottom and top ranges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;if we find such a feature then we add it to the responseObserver’s &lt;strong&gt;onNext&lt;/strong&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int lat = feature.getLocation().getLatitude();
   int lon = feature.getLocation().getLongitude();
   if (lon &amp;gt;= left &amp;amp;&amp;amp; lon &amp;lt;= right &amp;amp;&amp;amp; lat &amp;gt;= bottom &amp;amp;&amp;amp; lat &amp;lt;= top) {
     responseObserver.onNext(feature);
   }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we call the &lt;strong&gt;responseObserver.onCompleted();&lt;/strong&gt; method to indicate that the rpc call is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Implement a Client
&lt;/h2&gt;

&lt;p&gt;So now we understand what our service is actually doing.&lt;/p&gt;

&lt;p&gt;Let’s understand how a client method could be written for &lt;code&gt;listFeatures()&lt;/code&gt; method&lt;/p&gt;

&lt;p&gt;Any client for a service method/operation usually performs 3 functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Construct the request for the service&lt;/li&gt;
&lt;li&gt;Call the service method using either a &lt;strong&gt;blockingStub&lt;/strong&gt; or an &lt;strong&gt;asyncStub&lt;/strong&gt; (in other words either sync or async call)&lt;/li&gt;
&lt;li&gt;Logs or return the response back for further processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With above context, We can write a gRPC client for this &lt;strong&gt;listFeatures()&lt;/strong&gt; method like below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RouteGuideClient.java

public class RouteGuideClient {
 private static final Logger logger = Logger.getLogger(RouteGuideClient.class.getName());

 private final RouteGuideBlockingStub blockingStub;
 private final RouteGuideStub asyncStub;

 private Random random = new Random();
 private TestHelper testHelper;

 /** Construct client for accessing RouteGuide server using the existing channel. */
 public RouteGuideClient(Channel channel) {
   blockingStub = RouteGuideGrpc.newBlockingStub(channel);
   asyncStub = RouteGuideGrpc.newStub(channel);
 }

/**
* Blocking server-streaming example. Calls listFeatures with a rectangle of interest. Prints each
* response feature as it arrives.
*/
public void listFeatures(int lowLat, int lowLon, int hiLat, int hiLon) {
 info("*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", lowLat, lowLon, hiLat,
     hiLon);

 Rectangle request =
     Rectangle.newBuilder()
         .setLo(Point.newBuilder().setLatitude(lowLat).setLongitude(lowLon).build())
         .setHi(Point.newBuilder().setLatitude(hiLat).setLongitude(hiLon).build()).build();

 Iterator&amp;lt;Feature&amp;gt; features;
 try {
   features = blockingStub.listFeatures(request);

   for (int i = 1; features.hasNext(); i++) {
     Feature feature = features.next();
     info("Result #" + i + ": {0}", feature);
     if (testHelper != null) {
       testHelper.onMessage(feature);
     }
   }
 } catch (StatusRuntimeException e) {
   warning("RPC failed: {0}", e.getStatus());
   if (testHelper != null) {
     testHelper.onRpcError(e);
   }
 }
}}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initialize client
&lt;/h3&gt;

&lt;p&gt;Let’s unpack the client and understand how it works&lt;/p&gt;

&lt;p&gt;We start with Initialising our client class &lt;code&gt;RouteGuideClient&lt;/code&gt; and setup our logger using standard&lt;code&gt;java.util.logging.Logger;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RouteGuideClient {
 private static final Logger logger = Logger.getLogger(RouteGuideClient.class.getName());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, We declare a &lt;strong&gt;RouteGuideBlockingStub&lt;/strong&gt; and a &lt;strong&gt;RouteGuideStub&lt;/strong&gt; to make desired sync or async calls to the server and also initialize few other utilities like &lt;strong&gt;Random&lt;/strong&gt; and &lt;strong&gt;TestHelper&lt;/strong&gt; which may be used&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; private final RouteGuideBlockingStub blockingStub;
 private final RouteGuideStub asyncStub;
 private Random random = new Random();
 private TestHelper testHelper;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then pass in the channel to the client constructor and then initialize our blocking and async stubs using method from &lt;strong&gt;RouteGuideGrpc&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** Construct client for accessing RouteGuide server using the existing channel. */
 public RouteGuideClient(Channel channel) {
   blockingStub = RouteGuideGrpc.newBlockingStub(channel);
   asyncStub = RouteGuideGrpc.newStub(channel);
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  listFeatures
&lt;/h3&gt;

&lt;p&gt;We implement our listFeatures client method that accepts 4 int parameters, as we saw before they represent the co-ordinates for bottom left and upper right corners of the bounding rectangle&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lowLat → Latitude for a lower point of the rectangle&lt;/li&gt;
&lt;li&gt;lowLon → Longitude for a lower point of the rectangle&lt;/li&gt;
&lt;li&gt;hiLat → Latitude for the upper point of the rectangle&lt;/li&gt;
&lt;li&gt;hiLon → Longitude for the upper point of the rectangle
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void listFeatures(int lowLat, int lowLon, int hiLat, int hiLon) {
 info("*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", lowLat, lowLon, hiLat,
     hiLon);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our client, we prepare our request by constructing the &lt;strong&gt;Rectangle&lt;/strong&gt; object that the service expects. We can directly use the &lt;strong&gt;newBuilder()&lt;/strong&gt; exposed for each proto and use builder pattern to construct our request&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Rectangle request =
     Rectangle.newBuilder()
         .setLo(Point.newBuilder().setLatitude(lowLat).setLongitude(lowLon).build())
         .setHi(Point.newBuilder().setLatitude(hiLat).setLongitude(hiLon).build()).build();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we make the actual request to get our list of features using blockingStub and store it in a collection called &lt;code&gt;Iterator&amp;lt;Feature&amp;gt;&lt;/code&gt; that we can iterate upon&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Iterator&amp;lt;Feature&amp;gt; features;
 try {
   features = blockingStub.listFeatures(request);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then iterate and log each feature found within the specified coordinates. If we catch a &lt;strong&gt;StatusRuntimeException&lt;/strong&gt; then we log this as a warning&lt;/p&gt;

&lt;p&gt;In a real application, the client might do further processing based on these features but for now, we just log the features received&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try {
   features = blockingStub.listFeatures(request);
   for (int i = 1; features.hasNext(); i++) {
     Feature feature = features.next();
     info("Result #" + i + ": {0}", feature);
     if (testHelper != null) {
       testHelper.onMessage(feature);
     }
   }
 } catch (StatusRuntimeException e) {
   warning("RPC failed: {0}", e.getStatus());
   if (testHelper != null) {
     testHelper.onRpcError(e);
   }
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to unit test a client
&lt;/h2&gt;

&lt;p&gt;So now, we understand the basics&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How the service method under test is implemented&lt;/li&gt;
&lt;li&gt;How is the client for such a service written&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s come to the fun part.&lt;/p&gt;

&lt;p&gt;How will we ensure our client works fine?&lt;/p&gt;

&lt;p&gt;Of course, we write a test&lt;/p&gt;

&lt;p&gt;You can find the complete test &lt;a href="https://github.com/grpc/grpc-java/blob/v1.60.x/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java" rel="noopener noreferrer"&gt;examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java&lt;/a&gt; but we’ll focus on how to write a test for listFeatures()&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RouteGuideClientTest.java

@RunWith(JUnit4.class)
public class RouteGuideClientTest {
 /**
  * This rule manages automatic graceful shutdown for the registered server at the end of test.
  */
 @Rule
 public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();

 private final MutableHandlerRegistry serviceRegistry = new MutableHandlerRegistry();
 private final TestHelper testHelper = mock(TestHelper.class);
 private final Random noRandomness =
     new Random() {
       int index;
       boolean isForSleep;

       /**
        * Returns a number deterministically. If the random number is for sleep time, then return
        * -500 so that {@code Thread.sleep(random.nextInt(1000) + 500)} sleeps 0 ms. Otherwise, it
        * is for list index, then return incrementally (and cyclically).
        */
       @Override
       public int nextInt(int bound) {
         int retVal = isForSleep ? -500 : (index++ % bound);
         isForSleep = ! isForSleep;
         return retVal;
       }
     };
 private RouteGuideClient client;

 @Before
 public void setUp() throws Exception {
   // Generate a unique in-process server name.
   String serverName = InProcessServerBuilder.generateName();
   // Use a mutable service registry for later registering the service impl for each test case.
   grpcCleanup.register(InProcessServerBuilder.forName(serverName)
       .fallbackHandlerRegistry(serviceRegistry).directExecutor().build().start());
   client = new RouteGuideClient(grpcCleanup.register(
       InProcessChannelBuilder.forName(serverName).directExecutor().build()));
   client.setTestHelper(testHelper);
 }

/**
* Example for testing blocking server-streaming.
*/
@Test
public void listFeatures() {
 final Feature responseFeature1 = Feature.newBuilder().setName("feature 1").build();
 final Feature responseFeature2 = Feature.newBuilder().setName("feature 2").build();
 final AtomicReference&amp;lt;Rectangle&amp;gt; rectangleDelivered = new AtomicReference&amp;lt;Rectangle&amp;gt;();

 // implement the fake service
 RouteGuideImplBase listFeaturesImpl =
     new RouteGuideImplBase() {
       @Override
       public void listFeatures(Rectangle rectangle, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {
         rectangleDelivered.set(rectangle);

         // send two response messages
         responseObserver.onNext(responseFeature1);
         responseObserver.onNext(responseFeature2);

         // complete the response
         responseObserver.onCompleted();
       }
     };
 serviceRegistry.addService(listFeaturesImpl);

 client.listFeatures(1, 2, 3, 4);

 assertEquals(Rectangle.newBuilder()
                  .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
                  .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
                  .build(),
              rectangleDelivered.get());
 verify(testHelper).onMessage(responseFeature1);
 verify(testHelper).onMessage(responseFeature2);
 verify(testHelper, never()).onRpcError(any(Throwable.class));
}

/**
* Example for testing blocking server-streaming.
*/
@Test
public void listFeatures_error() {
 final Feature responseFeature1 =
     Feature.newBuilder().setName("feature 1").build();
 final AtomicReference&amp;lt;Rectangle&amp;gt; rectangleDelivered = new AtomicReference&amp;lt;Rectangle&amp;gt;();
 final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);

 // implement the fake service
 RouteGuideImplBase listFeaturesImpl =
     new RouteGuideImplBase() {
       @Override
       public void listFeatures(Rectangle rectangle, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {
         rectangleDelivered.set(rectangle);

         // send one response message
         responseObserver.onNext(responseFeature1);

         // let the rpc fail
         responseObserver.onError(fakeError);
       }
     };
 serviceRegistry.addService(listFeaturesImpl);

 client.listFeatures(1, 2, 3, 4);

 assertEquals(Rectangle.newBuilder()
                  .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
                  .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
                  .build(),
              rectangleDelivered.get());
 ArgumentCaptor&amp;lt;Throwable&amp;gt; errorCaptor = ArgumentCaptor.forClass(Throwable.class);
 verify(testHelper).onMessage(responseFeature1);
 verify(testHelper).onRpcError(errorCaptor.capture());
 assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;I know, its a lot. 🤟&lt;/p&gt;

&lt;p&gt;Let’s break it down step by step and grasp how this works&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Structure
&lt;/h3&gt;

&lt;p&gt;The code is a test class named &lt;strong&gt;RouteGuideClientTest&lt;/strong&gt; designed to test a client for communicating with a service called RouteGuide. It uses JUnit4 as we saw before&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RunWith(JUnit4.class)
public class RouteGuideClientTest {}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatic Cleanup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;@Rule&lt;/strong&gt; annotation tells JUnit to manage an instance of &lt;strong&gt;GrpcCleanupRule&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;This rule ensures that test servers are automatically shut down after each test, keeping the test environment clean.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
  * This rule manages automatic graceful shutdown for the registered server at the end of test.
  */
 @Rule
 public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Before
 public void setUp() throws Exception {
   // Generate a unique in-process server name.
   String serverName = InProcessServerBuilder.generateName();
   // Use a mutable service registry for later registering the service impl for each test case.
   grpcCleanup.register(InProcessServerBuilder.forName(serverName)
       .fallbackHandlerRegistry(serviceRegistry).directExecutor().build().start());
   client = new RouteGuideClient(grpcCleanup.register(
       InProcessChannelBuilder.forName(serverName).directExecutor().build()));
   client.setTestHelper(testHelper);
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We write &lt;strong&gt;@Before&lt;/strong&gt; annotated &lt;strong&gt;setUp&lt;/strong&gt; method to ensure each test method starts with a unique instance of the client&lt;/p&gt;

&lt;p&gt;We create a unique in-process server (running within the same process)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Generate a unique in-process server name.
   String serverName = InProcessServerBuilder.generateName();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also use a registry to allow different services to be registered for different test cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Use a mutable service registry for later registering the service impl for each test case.
   grpcCleanup.register(InProcessServerBuilder.forName(serverName) .fallbackHandlerRegistry(serviceRegistry).directExecutor().build().start());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then initialize the client and pass it the channel which is also registered for auto cleanup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client = new RouteGuideClient(grpcCleanup.register(
       InProcessChannelBuilder.forName(serverName).directExecutor().build()));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We inject a mock object called &lt;strong&gt;testHelper&lt;/strong&gt; for observing calls and verifying behavior. We’ll see that this is an interface that exposes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client.setTestHelper(testHelper);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;We then write 2 unit tests that do below on a high level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;listFeatures&lt;/strong&gt; : This tests successful communication with the server simulating a positive case 

&lt;ul&gt;
&lt;li&gt;Creates a fake service implementation to control server behavior.&lt;/li&gt;
&lt;li&gt;Calls the client’s listFeatures method to initiate communication.&lt;/li&gt;
&lt;li&gt;Asserts that the correct request was sent and the expected responses were received.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;listFeatures_error&lt;/strong&gt; : Tests how the client handles errors. 

&lt;ul&gt;
&lt;li&gt;Set up a fake service that throws an error.&lt;/li&gt;
&lt;li&gt;Verifies that the client properly propagates the error to the test helper.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  ✅ Positive case
&lt;/h4&gt;

&lt;p&gt;Let’s unpack the positive case first to understand this a bit better&lt;/p&gt;

&lt;p&gt;Below is the complete test at a glance. We will do a walkthrough on it below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
* Example for testing blocking server-streaming.
*/
@Test
public void listFeatures() {
 final Feature responseFeature1 = Feature.newBuilder().setName("feature 1").build();
 final Feature responseFeature2 = Feature.newBuilder().setName("feature 2").build();
 final AtomicReference&amp;lt;Rectangle&amp;gt; rectangleDelivered = new AtomicReference&amp;lt;Rectangle&amp;gt;();

 // implement the fake service
 RouteGuideImplBase listFeaturesImpl =
     new RouteGuideImplBase() {
       @Override
       public void listFeatures(Rectangle rectangle, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {
         rectangleDelivered.set(rectangle);

         // send two response messages
         responseObserver.onNext(responseFeature1);
         responseObserver.onNext(responseFeature2);

         // complete the response
         responseObserver.onCompleted();
       }
     };
 serviceRegistry.addService(listFeaturesImpl);

 client.listFeatures(1, 2, 3, 4);

 assertEquals(Rectangle.newBuilder()
                  .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
                  .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
                  .build(),
              rectangleDelivered.get());
 verify(testHelper).onMessage(responseFeature1);
 verify(testHelper).onMessage(responseFeature2);
 verify(testHelper, never()).onRpcError(any(Throwable.class));
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, let’s break this down.&lt;/p&gt;

&lt;p&gt;We first annotate our method with &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/test"&gt;@test&lt;/a&gt;&lt;/strong&gt; and then build two feature objects by using the builder as before by passing in the name as “feature 1” and “feature 2”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
public void listFeatures() {
 final Feature responseFeature1 = Feature.newBuilder().setName("feature 1").build();
 final Feature responseFeature2 = Feature.newBuilder().setName("feature 2").build();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a thread-safe reference to the &lt;strong&gt;Rectangle&lt;/strong&gt; object using Java &lt;a href="https://stackoverflow.com/questions/3964211/when-to-use-atomicreference-in-java" rel="noopener noreferrer"&gt;AtomicReference&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final AtomicReference&amp;lt;Rectangle&amp;gt; rectangleDelivered = new AtomicReference&amp;lt;Rectangle&amp;gt;();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this test, we are &lt;strong&gt;only interested in testing our client&lt;/strong&gt; , thus we create a fake service implementation for &lt;strong&gt;listFeatures&lt;/strong&gt; method. This ensures isolation for this unit test.&lt;/p&gt;

&lt;p&gt;To do so, we construct &lt;strong&gt;new RouteGuideImplBase()&lt;/strong&gt;and define our &lt;a href="https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html" rel="noopener noreferrer"&gt;anonymous subclass&lt;/a&gt; by overriding the &lt;strong&gt;listFeatures&lt;/strong&gt; method like below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// implement the fake service
 RouteGuideImplBase listFeaturesImpl =
     new RouteGuideImplBase() {
       @Override
       public void listFeatures(Rectangle rectangle, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the body, we set the rectangle passed in from the request into the &lt;code&gt;AtomicReference&amp;lt;Rectangle&amp;gt;&lt;/code&gt; rectangleDelivered we had defined earlier&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rectangleDelivered.set(rectangle);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also want the server to stream and return the two features we had earlier created to the client, thus we use the &lt;strong&gt;onNext()&lt;/strong&gt; method in &lt;strong&gt;responseObserver&lt;/strong&gt; to do so. This is a way for gRPC to provide &lt;a href="https://grpc.io/docs/what-is-grpc/core-concepts/#server-streaming-rpc" rel="noopener noreferrer"&gt;server-side streaming&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// send two response messages
responseObserver.onNext(responseFeature1);
responseObserver.onNext(responseFeature2);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we complete the RPC by calling &lt;strong&gt;onCompleted()&lt;/strong&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// complete the response
responseObserver.onCompleted();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see the complete fake service implementation below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// implement the fake service
 RouteGuideImplBase listFeaturesImpl =
     new RouteGuideImplBase() {
       @Override
       public void listFeatures(Rectangle rectangle, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {
         rectangleDelivered.set(rectangle);

         // send two response messages
         responseObserver.onNext(responseFeature1);
         responseObserver.onNext(responseFeature2);

         // complete the response
         responseObserver.onCompleted();
       }
     };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will add this fake service to our &lt;strong&gt;service registry&lt;/strong&gt; as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serviceRegistry.addService(listFeaturesImpl);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome, Let’s make our service call via the client. Here 1, 2, 3, and 4 are the lat and longs for the lower left and upper right corner of the rectangle as we saw before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client.listFeatures(1, 2, 3, 4);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After making the call, we should check whether the rectangle object returned from the response matches what we expect with the below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertEquals(Rectangle.newBuilder()
                  .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
                  .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
                  .build(),
              rectangleDelivered.get());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we also need to verify if our client actually called our fake service. We can ensure that using &lt;a href="https://www.digitalocean.com/community/tutorials/mockito-verify" rel="noopener noreferrer"&gt;verify() method from Mockito library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use the &lt;strong&gt;verify()&lt;/strong&gt; method from Mockito library to verify interactions with the mocked object &lt;strong&gt;testHelper&lt;/strong&gt; as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verify(testHelper).onMessage(responseFeature1);
verify(testHelper).onMessage(responseFeature2);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This interface is defined in &lt;strong&gt;&lt;a href="https://github.com/grpc/grpc-java/blob/eb8b1d8379008ab89cade89392d265bed90a2692/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java#L315" rel="noopener noreferrer"&gt;RouteGuideClient.java&lt;/a&gt;&lt;/strong&gt; as below and if we go over its definition, it exposes two methods onMessage() and onRpcError. We can use this to ensure the client calls the &lt;strong&gt;onMessage()&lt;/strong&gt; method with &lt;strong&gt;responseFeature1&lt;/strong&gt; and then with &lt;strong&gt;responseFeature2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
* Only used for helping unit test.
*/
@VisibleForTesting
interface TestHelper {
 /**
  * Used for verify/inspect message received from server.
  */
 void onMessage(Message message);

 /**
  * Used for verify/inspect error received from server.
  */
 void onRpcError(Throwable exception);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also ensure that the &lt;strong&gt;onRpcError()&lt;/strong&gt; method was never called since this is a positive case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verify(testHelper, never()).onRpcError(any(Throwable.class));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see other examples of verify() and never() method calls and their usages on &lt;a href="https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#4" rel="noopener noreferrer"&gt;mockito docs&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ⛔ Negative test
&lt;/h4&gt;

&lt;p&gt;How would we write a negative test for our client?&lt;/p&gt;

&lt;p&gt;In this test, We want to check what happens when our service throws an RPC error&lt;/p&gt;

&lt;p&gt;The initial setup is identical, except in this case we only prepare one &lt;strong&gt;responseFeature1&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
public void listFeatures_error() {
 final Feature responseFeature1 =
     Feature.newBuilder().setName("feature 1").build();
 final AtomicReference&amp;lt;Rectangle&amp;gt; rectangleDelivered = new AtomicReference&amp;lt;Rectangle&amp;gt;();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We initialize a &lt;strong&gt;fakeError&lt;/strong&gt; of StatusRuntimeException type and then initialize it with an INVALID_ARGUMENT value&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; final StatusRuntimeException fakeError = new StatusRuntimeException(Status.INVALID_ARGUMENT);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We again implement our fake service and repeat the same steps as above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// implement the fake service
RouteGuideImplBase listFeaturesImpl =
   new RouteGuideImplBase() {
     @Override
     public void listFeatures(Rectangle rectangle, StreamObserver&amp;lt;Feature&amp;gt; responseObserver) {
       rectangleDelivered.set(rectangle);

       // send one response message
       responseObserver.onNext(responseFeature1);

       // let the rpc fail
       responseObserver.onError(fakeError);
     }
   };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice, the last step. 🔵&lt;/p&gt;

&lt;p&gt;We now use the onError method to return the fake error that we had created earlier. In a production app, the service may use this to communicate to the client that something went wrong in processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;responseObserver.onError(fakeError);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then register our service, call our client with the same input, and assert that the API returns us the similar output by asserting on the rectangle object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serviceRegistry.addService(listFeaturesImpl);

client.listFeatures(1, 2, 3, 4);

assertEquals(Rectangle.newBuilder()
                .setLo(Point.newBuilder().setLatitude(1).setLongitude(2).build())
                .setHi(Point.newBuilder().setLatitude(3).setLongitude(4).build())
                .build(),
            rectangleDelivered.get());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify the service works for our negative scenario&lt;/p&gt;

&lt;p&gt;We perform below 4 steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a capture mechanism for errors.&lt;/li&gt;
&lt;li&gt;Verify that a message was received by the service (likely before the error).&lt;/li&gt;
&lt;li&gt;Verify that an RPC error occurred and capture the specific error.&lt;/li&gt;
&lt;li&gt;Assert that the captured error’s status matches the expected error status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see how this could be implemented using the Mockito library&lt;/p&gt;

&lt;p&gt;We check that the &lt;strong&gt;onMessage()&lt;/strong&gt; method of the mocked object &lt;strong&gt;testHelper&lt;/strong&gt; was invoked with the specific argument &lt;strong&gt;responseFeature1&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verify(testHelper).onMessage(responseFeature1);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define the &lt;a href="https://www.baeldung.com/mockito-argumentcaptor" rel="noopener noreferrer"&gt;ArgumentCaptor&lt;/a&gt; class from Mockito to capture any exception of type &lt;strong&gt;Throwable&lt;/strong&gt; to our mocked method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ArgumentCaptor&amp;lt;Throwable&amp;gt; errorCaptor = ArgumentCaptor.forClass(Throwable.class);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we ensure that &lt;strong&gt;onRpcError()&lt;/strong&gt; method of mocked &lt;strong&gt;testHelper&lt;/strong&gt; is called and by using &lt;strong&gt;capture()&lt;/strong&gt; method of &lt;strong&gt;ArgumentCaptor,&lt;/strong&gt; we capture the error returned by the GRPC service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verify(testHelper).onRpcError(errorCaptor.capture());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also assert that the captured error status matches expected &lt;strong&gt;fakeError.getStatus()&lt;/strong&gt;by retrieving the captured &lt;strong&gt;Throwable&lt;/strong&gt; using &lt;strong&gt;errorCaptor.getValue()&lt;/strong&gt; and convert it into a GRPC Status object using &lt;strong&gt;Status.fromThrowable()&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertEquals(fakeError.getStatus(), Status.fromThrowable(errorCaptor.getValue()));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Let’s recap what we learned in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We understood how listFeature() method is implemented and how can a gRPC server return a streaming response&lt;/li&gt;
&lt;li&gt;We understood how the client method is written for this service&lt;/li&gt;
&lt;li&gt;Later, we dove into how can we unit test the client 

&lt;ul&gt;
&lt;li&gt;We looked into how to write a fake service&lt;/li&gt;
&lt;li&gt;We implemented a positive case and checked if calls were made using Mockito verify()&lt;/li&gt;
&lt;li&gt;We implemented a negative case and then checked if error was returned using Mockito ArgumentCaptor&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;That was lot of ground 🌆, and this wraps up what I wanted to cover with unit testing. There are other aspects to it but I’ll leave that to you to explore with other methods.&lt;/p&gt;

&lt;p&gt;Please let me know if you have questions or thoughts in the comments.&lt;/p&gt;

&lt;p&gt;In the next post, we will grasp how to write a &lt;strong&gt;functional API test&lt;/strong&gt; for this to get confidence that the service works when dealing with live data.&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please share it and follow me ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time 👋, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaretesting</category>
      <category>testautomation</category>
      <category>grpc</category>
    </item>
    <item>
      <title>What I wish I knew about Software Testing and growth at the start of my career 🌱</title>
      <dc:creator>Gaurav Singh</dc:creator>
      <pubDate>Sun, 25 Feb 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/automationhacks/what-i-wish-i-knew-about-software-testing-and-growth-at-the-start-of-my-career-4cj5</link>
      <guid>https://dev.to/automationhacks/what-i-wish-i-knew-about-software-testing-and-growth-at-the-start-of-my-career-4cj5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53mkx4lmumdgh6jbwsdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53mkx4lmumdgh6jbwsdx.png" alt="An image of a humanoid with multiple smaller icons indicating growth, idea, productivity" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image powered by DALL-E 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I was reflecting on many conversations I’ve had around growth in testing with some peers in the community and up-and-coming testers and engineers. While it would take many blogs and talks to unpack these, I thought it would be fun to come up with a random unordered list of skills, approaches, habits, and growth ideas that I wish I had known at the start of my testing career to mature.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I think about testing now? 🤔
&lt;/h2&gt;

&lt;p&gt;Testing is a deep and multifaceted discipline with many things to learn and incredible power to deliver delightful outcomes for your customers. If done well, it can enable the whole company to move fast and ship with confidence. The ultimate goal of testing is to enable the building of a quality product and service that brings delight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills 🤹
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Learn Touch typing&lt;/li&gt;
&lt;li&gt;Learn mind mapping&lt;/li&gt;
&lt;li&gt;Learn how to gauge risk and increase test coverage at different layers systematically&lt;/li&gt;
&lt;li&gt;Learn the fundamentals of testing well and then don’t stop, keep going.&lt;/li&gt;
&lt;li&gt;Speak up in meetings and discussions (don’t be shy)&lt;/li&gt;
&lt;li&gt;Ask those dumb questions, and always be curious&lt;/li&gt;
&lt;li&gt;Be polyglot and learn a static and dynamic programming language and then some more&lt;/li&gt;
&lt;li&gt;Learn to code and read lots of code&lt;/li&gt;
&lt;li&gt;Learn to design efficient CI/CD pipelines&lt;/li&gt;
&lt;li&gt;The test pyramid is also an indication of what you can learn about different layers of testing&lt;/li&gt;
&lt;li&gt;Don’t focus on only UI or Backend. Look at the system as a whole.&lt;/li&gt;
&lt;li&gt;Focus on the customer and their UI/UX touchpoints. These should never break&lt;/li&gt;
&lt;li&gt;Learn the basic API of the tool or framework and then keep going deeper&lt;/li&gt;
&lt;li&gt;Learn to problem solve in leet code and understand data structures and system design&lt;/li&gt;
&lt;li&gt;Learn how to manage up&lt;/li&gt;
&lt;li&gt;Learn how to build your network up&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Approach and attitude 🙂
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Begin anywhere&lt;/li&gt;
&lt;li&gt;Be a tinkerer&lt;/li&gt;
&lt;li&gt;Be humble and grounded. Recognize that there is always room to grow&lt;/li&gt;
&lt;li&gt;Be a friend to someone new and help them grow along with you.&lt;/li&gt;
&lt;li&gt;Encourage psychological safety in your team.&lt;/li&gt;
&lt;li&gt;Find a mentor and learn from them on a set of topics. Then find another. Keep growing.&lt;/li&gt;
&lt;li&gt;Don’t restrict yourself to one area (web, mobile, backend, data, performance, security, CI) - Mix things up!&lt;/li&gt;
&lt;li&gt;Quality is everyone’s responsibility but someone needs to champion it&lt;/li&gt;
&lt;li&gt;If you are not learning in an environment, either change yourself or change your environment.&lt;/li&gt;
&lt;li&gt;Leverage the power of the internet and the wide and open testing community. You’ll learn better and faster&lt;/li&gt;
&lt;li&gt;Don’t give up when you see a problem; be comfortable saying “I don’t know this …… &lt;em&gt;YET”&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Learn something, teach it to others, and move on to other and better things to focus on.&lt;/li&gt;
&lt;li&gt;Don’t be a silo and a single point of failure&lt;/li&gt;
&lt;li&gt;Reading docs is your best friend&lt;/li&gt;
&lt;li&gt;You don’t need to wait for a course or a class to teach you something. Be self-taught as much as possible&lt;/li&gt;
&lt;li&gt;Clear writing is clear thinking&lt;/li&gt;
&lt;li&gt;Don’t focus on the title but on developing skills. You are not your title&lt;/li&gt;
&lt;li&gt;Always be a net positive contributor on your team, focus on outcomes&lt;/li&gt;
&lt;li&gt;Don’t limit yourself to only testing but also learn from engineering (Dev, DevOps, AI/ML, and PM) and nonengineering communities and their practices.&lt;/li&gt;
&lt;li&gt;Don’t restrict yourself to one persona of a tester but be a generalist software engineer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Habits ⌨️
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Build a note-taking system. It will compound you faster.&lt;/li&gt;
&lt;li&gt;Schedule consistent and deliberate learning time&lt;/li&gt;
&lt;li&gt;Maintain a summary and narrative of your work regularly&lt;/li&gt;
&lt;li&gt;Build the habit of reading books and summarising them&lt;/li&gt;
&lt;li&gt;Listen to podcasts while doing life chores. Make your chores fun!&lt;/li&gt;
&lt;li&gt;Read blogs and newsletters to learn from your peers&lt;/li&gt;
&lt;li&gt;Propose talks at conferences as a way of learning&lt;/li&gt;
&lt;li&gt;Adopt open source, be part of the community, and contribute however you can.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Growth 🌱
&lt;/h2&gt;

&lt;p&gt;All of the above and …&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build, learn, and share in the open as much as possible&lt;/li&gt;
&lt;li&gt;Develop a personal roadmap and OKRs and evaluate your progress&lt;/li&gt;
&lt;li&gt;Maintain a personal tech brand&lt;/li&gt;
&lt;li&gt;Explore working in diverse work environments like services, products, startups, scale-ups, and big tech to develop a broad perspective&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Did I miss any? Please let me know in the comments&lt;/p&gt;

&lt;p&gt;| Thanks for the time you spent reading this 🙌. If you found this post helpful, please share it with your friends and follow me ( &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/automationhacks"&gt;@automationhacks&lt;/a&gt;&lt;/strong&gt; ) for more such insights in Software &lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;. Until next time, Happy Testing 🕵🏻 and Learning! 🌱 | &lt;a href="https://newsletter.automationhacks.io/" rel="noopener noreferrer"&gt;Newsletter&lt;/a&gt; | &lt;a href="https://www.youtube.com/@automationhacks" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://automationhacks.io/" rel="noopener noreferrer"&gt;Blog&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/automationhacks/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://twitter.com/automationhacks" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. |&lt;/p&gt;

</description>
      <category>softwaretesting</category>
      <category>testautomation</category>
      <category>career</category>
    </item>
  </channel>
</rss>
