<?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>Forem: Felicia Walker</title>
    <description>The latest articles on Forem by Felicia Walker (@feliciawalker).</description>
    <link>https://forem.com/feliciawalker</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%2F1141593%2Fc295e9ff-4dcf-4216-96d6-355bc62aae77.jpeg</url>
      <title>Forem: Felicia Walker</title>
      <link>https://forem.com/feliciawalker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/feliciawalker"/>
    <language>en</language>
    <item>
      <title>An Extensible React Native App Automation Framework</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Sun, 20 Jul 2025 03:36:02 +0000</pubDate>
      <link>https://forem.com/feliciawalker/an-extensible-react-native-app-automation-framework-1ig2</link>
      <guid>https://forem.com/feliciawalker/an-extensible-react-native-app-automation-framework-1ig2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: I've had the bulk of this in draft forever so it may be a bit dated now. However, the overall approach is still useful and shows how you can combine multiple technologies together for a cohesive yet flexible solution. &lt;/p&gt;

&lt;p&gt;I'm going to keep this high level and cover parts of it in other posts. I will also defer detailed framework code stuff to future articles going over what this eventually evolved into.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was hired at a previous company to create an automation framework to do UI testing on a React native mobile app. Initially simulators would be used but support for real devices in the AWS device farm was be ideal.&lt;/p&gt;

&lt;p&gt;Cucumber needed to be integrated so tests would be easier for developers to write and non-technical people to understand. It needed to be written in Typescript and use a tool called Detox that they had been experimenting with. &lt;/p&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;Here is a brief description of the various components and packages that the framework uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cucumber&lt;/strong&gt; - Specifically we use &lt;a href="https://cucumber.io" rel="noopener noreferrer"&gt;cucumber.js&lt;/a&gt; which is a runner that links plain language tests, written in the Gherkin syntax, to actual test execution code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wix/Detox" rel="noopener noreferrer"&gt;Detox&lt;/a&gt; - Drives mobile apps by hooking into their underlying code. This allows Detox to know when operations are complete, which makes it more efficient in finding elements. This is what is normally used for testing since it is faster and easier to set up.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://appium.io/docs/en/latest/" rel="noopener noreferrer"&gt;&lt;strong&gt;Appium&lt;/strong&gt;&lt;/a&gt; - A server that allows you to drive mobile apps externally. It has more functionality than Detox, but is slower and harder to set up. This is needed because the AWS Device Farm does not support Detox. Also, some tests simply cannot be done with Detox’s limitations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webdriver.io" rel="noopener noreferrer"&gt;&lt;strong&gt;WebdriverIO&lt;/strong&gt;&lt;/a&gt; - A driver that talks to the Appium server and configures it to make using Appium much easier.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/device-farm/" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS Device Farm&lt;/strong&gt;&lt;/a&gt; - What the company wanted to use eventually. It supports Appium tests, and it should be possible to package up the framework along with the tests to run in their Node/Appium environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The main design focus was to make things easy to use an maintainable while being flexible. This means most of the complexity is encapsulated inside Cucumber steps and in the lower code that supports it. Also, the specifics for executing against Detox or Appium exists only in their own encapsulated classes that can be switched in and out. This was most everything can remain the same, including the actual tests, no matter what OS is used or test runner.&lt;/p&gt;

&lt;p&gt;Another aspect is the system for identifying elements inside the app. React supports an attribute called &lt;code&gt;testID&lt;/code&gt;. These needed to be added in the app code using &lt;a href="https://dev.to/feliciawalker/a-system-for-automatically-adding-testids-to-react-code-4b1j"&gt;a system to ensure they are unique and manageable&lt;/a&gt;. In the test framework, these are defined and associated with plain language phrases used in the Cucumber tests. To ensure that this is flexible, &lt;a href="https://dev.to/feliciawalker/a-system-for-dynamically-handling-element-names-in-cucumber-4mk6"&gt;a system was made to allow for multiple phrases to be used and support for regular expressions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The architecture does allow for the possibility of adding web in future, via Appium or adding in a new Selenium driver. A second set of tests would need to be created, however, since the web version has a totally different look and feel than the mobile version.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Level Architecture
&lt;/h2&gt;

&lt;p&gt;Here is a diagram of the system level with all packages and support files involved:&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%2Faqi9fy3uza6cjz3zuakc.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%2Faqi9fy3uza6cjz3zuakc.png" alt="System diagram for the framework" width="685" height="791"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When a Detox test is run
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Yarn&lt;/code&gt; is used to run Cucumber.js along with various arguments&lt;/li&gt;
&lt;li&gt;Cucumber.js executes &lt;em&gt;tests.setup.js&lt;/em&gt;, which creates a shared JS context object capturing command line options and pulls in the cucumber init files&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cucumber.init.ts&lt;/em&gt; setups up Detox, configures and runs a server, and hooks into Cucumber for results reporting&lt;/li&gt;
&lt;li&gt;Tests are now run with Detox executing actions against an appropriate simulator. It can do this since the installed app has been built with Detox instrumentation.&lt;/li&gt;
&lt;li&gt;Results are reported to the console&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  When an Appium test is run
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Yarn&lt;/code&gt; is used to run WebdriverIO along with various arguments, including the os type which is required&lt;/li&gt;
&lt;li&gt;WebdriverIO executes &lt;em&gt;tests.setup.js&lt;/em&gt;, which creates a shared JS context object capturing command line options and pulls in the WebdriverIO config file&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Wdio.conf.ts&lt;/em&gt; sets the capabilities of the device to run, sets up Cucumber, and configures/starts the Appium server&lt;/li&gt;
&lt;li&gt;Appium talks to the device, installs an app for executing actions against the app under test&lt;/li&gt;
&lt;li&gt;Tests are now run with WebdriverIO talking to the Appium server, which executes actions on the device&lt;/li&gt;
&lt;li&gt;Results are reported to the console&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Things to note
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The user can manually start an Appium server instead of the framework. This is useful to see what it is doing, and can be faster when running multiple tests.&lt;/li&gt;
&lt;li&gt;Local real devices can be used instead of simulators. This is trickier with Detox, and has not been fully investigated.&lt;/li&gt;
&lt;li&gt;Running in AWS is left to the future, and will require packaging everything up and uploading it there&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to use Detox or Appium
&lt;/h3&gt;

&lt;p&gt;You will normally want to use Detox locally because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detox is faster &lt;/li&gt;
&lt;li&gt;Detox is easier to work when developing&lt;/li&gt;
&lt;li&gt;Detox seems to be a bit less temperamental when running, probably due to fewer moving parts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, Appium does have some advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Appium has more capabilities than Detox. 

&lt;ul&gt;
&lt;li&gt;One big one is Detox can only find elements but not get any info about them. You can’t get the text of an element to verify with Detox, but you can with Appium.&lt;/li&gt;
&lt;li&gt;Appium is much better at dealing with native OS things like action sheets, and gives more control over them&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Appium is probably better for running on real devices&lt;/li&gt;

&lt;li&gt;Appium can be used to the web (future consideration)&lt;/li&gt;

&lt;li&gt;Appium is supported by the AWS Device Farm and probably others since it is a standard&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code Level Architecture
&lt;/h2&gt;

&lt;p&gt;Here is a diagram of the internals of the actual framework:&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%2Fn4uk6gvn89desu0fqdfg.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%2Fn4uk6gvn89desu0fqdfg.png" alt="Class diagram of the framework internals" width="731" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Features&lt;/strong&gt; - The tests are written here as plain language using the Gherkin syntax. Each feature file should focus on one particular area.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step Definitions&lt;/strong&gt; - These are cucumber files that link the feature keywords to actual code. There is one file per feature file with a common shared one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; - Test data is pulled into the steps using factories, but only one for user credentials currently exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pages&lt;/strong&gt; - Each page file represents one screen of the app. It defines the various elements, their &lt;code&gt;testIDs&lt;/code&gt;, and the phrases used to identify them in the feature tests. To ease use, a factory exists that can provide a page object based on a phrase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actions&lt;/strong&gt; - These classes implement a set of standard actions in a driver specific format. A factory exists to provide the proper one based on if Detox or Appium is used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;World Context&lt;/strong&gt; - This is a global object that captures any command line arguments and other info such as the framework type and os. Cucumber has a native version of this, but it is only accessible by the step definitions. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loggers&lt;/strong&gt; - Global loggers to give details about Cucumber, Detox, and Appium actions. The normal level is &lt;code&gt;DEBUG&lt;/code&gt; since that provides enough info to troubleshoot failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;There are differences in the specifics, but generally Detox and Appium work the same. The tester runs a &lt;code&gt;yarn&lt;/code&gt; command (predefined or ad-hoc) which launches packages that pulls configuration files in to set up Detox/Appium and also interpret Cucumber tests. In each test, the plain language of the feature file is linked to actual code via step definitions. The actual code is organized per screen with a standard set of actions available. During setup, the proper concrete implementations of the actions using Detox or Appium commands are linked to these page objects. Both Detox and Appium use their own servers to interact with the simulator or device under test, but via different mechanisms. Their results are interpreted by the Cucumber runner and reported back via the console.&lt;/p&gt;

&lt;p&gt;When a command is executed Detox/Appium examines the current screen and tried to locate the element to act on by the &lt;code&gt;testID&lt;/code&gt; attribute. This is a special React attribute for the specific purpose of automation. It is a unique identifier defined in the product code for elements used by automation. Since this &lt;code&gt;testID&lt;/code&gt; is not very user friendly, a mechanism exists in the page objects that links plain language phrases to the &lt;code&gt;testIDs&lt;/code&gt;. The same is also true for identifying which screen (and associated page object) to use in the actual tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This may not be the most in depth article, but hopefully it shows how you can build an automation framework that incorporates different technologies in a decoupled way. This allows for flexibility in the vehicles that actually drive an app but also how you can write the tests above.&lt;/p&gt;

&lt;p&gt;As mentioned in the note at the beginning, some details will be covered in other posts and the code details in a series on what this eventually evolved into.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mapping Gherkin phrases to page locators - &lt;a href="https://dev.to/feliciawalker/a-system-for-dynamically-handling-element-names-in-cucumber-4mk6"&gt;Blog post&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;System to add automatic &lt;code&gt;testId&lt;/code&gt;s to React code - &lt;a href="https://dev.to/feliciawalker/a-system-for-automatically-adding-testids-to-react-code-4b1j"&gt;Blog post&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>appium</category>
      <category>detox</category>
      <category>testing</category>
    </item>
    <item>
      <title>GitLab Pipeline Annoyances</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Mon, 16 Jun 2025 05:10:16 +0000</pubDate>
      <link>https://forem.com/feliciawalker/gitlab-pipeline-annoyances-11e4</link>
      <guid>https://forem.com/feliciawalker/gitlab-pipeline-annoyances-11e4</guid>
      <description>&lt;p&gt;I used GitLab build pipelines in a previous position where I refactored two files with a complex layout of jobs and functionality. While trying to simplify the flow and reduce the amount of copied code, I ran into a number of annoyances. Some were due to wanting more programming concepts that are not supported but others were basic functionality. I also ran into some items that did not work even though the documentation indicated that they should.&lt;/p&gt;

&lt;p&gt;I'd like to gripe about these, but also offer the solutions I came up with in case others also run into these issues. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;br&gt;
I do not have the original files to refer to, but have only written about things I feel I remember well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Visualization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lack of connection lines
&lt;/h3&gt;

&lt;p&gt;You think there should be connecting lines between your stages, but frequently there is nothing. The problem is that most of this is figured out at runtime, so nothing can be shown in the visualization. Only links specified by &lt;code&gt;needs&lt;/code&gt; or &lt;code&gt;depends&lt;/code&gt; are defined ahead of time and can be shown.&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://forum.gitlab.com/t/pipeline-graph-doesnt-show-lines-connecting-jobs-stages/52001" rel="noopener noreferrer"&gt;a forum post&lt;/a&gt; about this.&lt;/p&gt;

&lt;h3&gt;
  
  
  No visualization when using other branches
&lt;/h3&gt;

&lt;p&gt;If your pipeline has any includes that are on a branch other than &lt;code&gt;main&lt;/code&gt;, you do not get any visualization. There is not much to say about this, unfortunately. The visualization in general doesn’t seem to handle includes very well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Changing global variables
&lt;/h3&gt;

&lt;p&gt;You cannot directly change global variables from a job once they are defined and set. Instead, you will need to make a step to change the values in a bash script then save as a dotenv artifact. Once saved, the values will be automatically be available to any future steps.&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://stackoverflow.com/questions/67707362/dynamically-save-gitlab-variable-for-job-that-runs-only-for-one-pipeline#67710775" rel="noopener noreferrer"&gt;a forum post&lt;/a&gt; with an example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Predefined variables not going to child pipelines
&lt;/h3&gt;

&lt;p&gt;The standard predefined CI/CD variables are not available to child pipelines by default. It seems like they should just be available everywhere, but alas no. If you want to use any of these, you will need to define  and pass them in the child pipeline step like any other variable you want to pass. &lt;/p&gt;

&lt;h2&gt;
  
  
  Shell Scripting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issues with echos and colons
&lt;/h3&gt;

&lt;p&gt;Putting a colon in any &lt;code&gt;echo&lt;/code&gt; statements can cause Gitlab to interpret it as actual syntax, leading to a syntax error. It appears to be an issue with the YAML parsing, not Gitlab itself.&lt;/p&gt;

&lt;p&gt;You can try &lt;a href="https://forum.gitlab.com/t/cant-add-in-echo-command/72509" rel="noopener noreferrer"&gt;quoting the whole string&lt;/a&gt;, but be careful if you are mixing quote styles. There are &lt;a href="https://stackoverflow.com/questions/68085161/how-to-escape-a-colon-in-gitlab-ci-yml#68085350" rel="noopener noreferrer"&gt;some other suggestions&lt;/a&gt; using templates, in case that does not work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiline echos
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;echo&lt;/code&gt; with a multi line quoted string may work if this is the only command, but once you use “&amp;gt;” or “|” for a multi step script, this fails. In this case, the additional lines in the &lt;code&gt;echo&lt;/code&gt; string are interpreted as another script command.&lt;/p&gt;

&lt;p&gt;I never found a good way around this except to split the long string and use multiple &lt;code&gt;echo&lt;/code&gt; commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use braces around a variable
&lt;/h3&gt;

&lt;p&gt;This was never clear to me since many examples usually do not use braces around a variable, both outside of script sections or within them. However, some do put braces around. I always tried to use the braces, but sometimes they seemed to cause problems.&lt;/p&gt;

&lt;p&gt;It appears that you cannot use braces unless some text immediately follows the variable. In this case, the braces explicitly group the variable name away from the other text. Here is &lt;a href="https://stackoverflow.com/questions/66862537/how-to-use-custom-variables-in-gitlab-ci-cd#66862872" rel="noopener noreferrer"&gt;a post about this&lt;/a&gt; with some examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Good References
&lt;/h2&gt;

&lt;p&gt;Here are some good references I came across trying to figure some of these things out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.devops.dev/seven-tips-for-writing-better-gitlab-pipelines-c94f348ad0b9" rel="noopener noreferrer"&gt;Seven tips for writing better GitLab pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/zenika/gitlab-ci-10-best-practices-to-avoid-widespread-anti-patterns-2mb5"&gt;Best practices to avoid anti-patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.sandra-parsick.de/2022/04/29/passing-variable-through-gitlab-pipelines/" rel="noopener noreferrer"&gt;Passing variables through pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cpcwood.com/blog/6-how-we-reduced-our-gitlab-ci-pipeline-duration-by-70-at-student-beans" rel="noopener noreferrer"&gt;Reducing pipelines duration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gitlab</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Thoughts On Interviewing</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Sat, 26 Oct 2024 21:40:59 +0000</pubDate>
      <link>https://forem.com/feliciawalker/thoughts-on-interviewing-30i5</link>
      <guid>https://forem.com/feliciawalker/thoughts-on-interviewing-30i5</guid>
      <description>&lt;p&gt;I have been doing a ton of candidate interviewing over the past year, and have formed some opinions about the process. So what does a software person of many years do when they have opinions? Write a blog article, of course!&lt;/p&gt;

&lt;p&gt;I have tried to think of things outside of just having specific experience or knowledge. Even if you nail these it does not mean you will do well on the job. Conversely, if you are weak on either it does not mean you cannot do a job well. What can be communicated to let the interviewer know that you are capable and have strategies for handling different situations (or how can you judge how well a candidate will do)? I am still figuring it out, but I wanted to share a few of the things I have come up with over the years.&lt;/p&gt;

&lt;h2&gt;
  
  
  General
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Learn to judge how detailed your answer should be
&lt;/h3&gt;

&lt;p&gt;Not every answer needs to have large amounts of detail in them. The urge is to show that you really know something, but that may not be what the interviewer is interested in. They may have specific follow up questions, which they may not be able to ask if you give a mini lecture.&lt;/p&gt;

&lt;p&gt;I have found balance by including enough information to cover what was asked (and maybe a little extra, if short), but then ask if that was enough or should I provide additional details. Sometimes it helps to mention you will shorten your answer in the interest of time.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's ok to not know something, but don't make up an answer
&lt;/h3&gt;

&lt;p&gt;No one knows everything and even if you do, you can temporarily forget. &lt;em&gt;StackExchange&lt;/em&gt; would not be around if this was not true. If you do not know something, just say so but also say what you would do about that. You can say how you would find out or how it is similar to something you do know.&lt;/p&gt;

&lt;p&gt;In any case, do NOT make something up. Your interviewers will most likely know you are trying to cover up not knowing. Doing this on the job leads to bad results and decreases trust in you. And in some cases interviewers are testing you to see what you do when you don't know something. Make a good impression.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be honest about your level of experience with X
&lt;/h3&gt;

&lt;p&gt;Everyone stretches their experience a bit on their resume, but do not give the impression you have much more experience with something than you do. Especially if it is something in the job description, you will likely be asked questions to ensure you do have the experience. &lt;/p&gt;

&lt;p&gt;If you are only familiar with a certain technology, just state that. You can use this as an opportunity to bring up similar items you are more knowledgeable about. You can also segue to an example where you had to learn a new technology/skill quickly and your success. Like the previous point, showing you are enthusiastic and have a plan while being honest can work in your favor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical And Coding Tasks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Talk through your thought process
&lt;/h3&gt;

&lt;p&gt;The most important thing to do when tackling a technical exercise is to let the interviewers know what you are thinking. Silence is awkward but also does not give information to the interviewers. You can ask for a moment to think, but even if you do not have complete thoughts you should describe what you have and where you are going.&lt;/p&gt;

&lt;p&gt;As you implement a solution, either explain as you go or produce a small amount of content and recap. Be sure to explain why you are making your choices in addition to what you are actually doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's ok to make a mistake
&lt;/h3&gt;

&lt;p&gt;Almost no one can produce a perfect solution the first time to reasonably complex problems. If you discover you made mistake, do not try to hide it. Admit the mistake and fix it immediately if you can. If the cause it not obvious, do some quick troubleshooting to determine if you can fix it in the remaining time. When in doubt, state what you think is the cause, then ask if you should keep troubleshooting or continue with the task as best you can.&lt;/p&gt;

&lt;p&gt;Making a mistake in an interview is difficult. It throws off your flow and can cause you to worry about the consequences. Try to push that aside for the moment and remember an important part of the interview is demonstrating how you handle these situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scope your solution to the time available
&lt;/h3&gt;

&lt;p&gt;It is much better to produce a sub-optimal answer than nothing at all. Do not try to come up with the more efficient or bullet proof solution at first. Mention that this is your initial iteration and it can be improved as needed. You can also give the overview of your idea and that you will figure some of the details out as you go. &lt;/p&gt;

&lt;p&gt;You can mention assumptions or leave out portions of the solution not directly relevant to solving the problem (ex: guard clauses to check parameter values). Of course, mention you though of it, but will skip due to time and an revisit if there is time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Be sure to give a complete implementation
&lt;/h3&gt;

&lt;p&gt;A continuation of the previous topic, you need to be able to deliver a complete solution within the time you are given. Maybe it will not satisfy all the requirements, but better than an incomplete solution that leaves the interviewers guessing how you would turn your ideas into something concrete. Time management is an important skill to demonstrate.&lt;/p&gt;

&lt;p&gt;And if you do run out of time and are incomplete, quickly give an overview of what if missing. You probably do not need to say why you could not finish. If appropriate, you may ask if you can send a complete solution via email. However, this is typically not needed unless you were very close to finishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope these thoughts were helpful. Interviewing is difficult, but try to remember that how you convey your abilities is as important as your knowledge. Fortunately, just vocalizing your thought process, scoping your answers, and asking if more information is needed can help. And remember, no one is perfect. How you handle this is important information for the interviewers.&lt;/p&gt;

&lt;p&gt;Good luck out there!&lt;/p&gt;

</description>
      <category>interview</category>
      <category>tips</category>
      <category>jobhunting</category>
      <category>career</category>
    </item>
    <item>
      <title>Using Your Own Node Modules With Playwright</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Tue, 15 Oct 2024 03:27:20 +0000</pubDate>
      <link>https://forem.com/feliciawalker/using-your-own-node-modules-with-playwright-206d</link>
      <guid>https://forem.com/feliciawalker/using-your-own-node-modules-with-playwright-206d</guid>
      <description>&lt;p&gt;Most of the time your supporting Playwright code will probably live side by side in directories next to your spec files. However, if you need to share your supporting code with other teams or release it to the public, you may consider using a Node module. &lt;/p&gt;

&lt;p&gt;I did just this to share UI, API, and load libraries of supporting Playwright code with other teams. They all had their own repositories and CI/CD pipelines and did not want to clone the test repository and deal with it. Publishing as node packages to a private registry seemed like a good solution. This way other teams could just pull them in like any other module. However, there were complications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VS code and linters wanted the modules to be in ESM format&lt;/li&gt;
&lt;li&gt;Playwright wanted the modules to be in CJS format&lt;/li&gt;
&lt;li&gt;You need type files for intellisense&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It took a lot of experimenting, borrowing from other open source projects, and swearing in order to get something that fixed all of these. Hopefully I can save you some of that pain with this article.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Much of this article overlaps with &lt;a href="https://dev.to/feliciawalker/k6-development-beyond-the-basic-setup-2h99"&gt;my earlier one&lt;/a&gt; on setting something similar up for K6&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Here is a slightly simplified version of the Playwright setup I created:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is sample code you can refer to at my &lt;a href="https://github.com/felicia-walker/playwright-template-complex-dev" rel="noopener noreferrer"&gt;playwright-template-complex-dev respository&lt;/a&gt; which covers all sections of this article. It is a simplified version of the diagram below.&lt;/p&gt;
&lt;/blockquote&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%2F354xt8vzcb7x0owz3ny8.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%2F354xt8vzcb7x0owz3ny8.png" alt="UML diagram of a simplified code setup" width="741" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are three custom libraries which reside in their own repositories. The data library are container objects for various app entities. This creates a standard way to pass things around among the various parts. The UI library abstracts UI actions and implements the Playwright ones at a low level. The API library constructs and executes product specific API calls.&lt;/p&gt;

&lt;p&gt;These three libraries are published to a private NPM repository. The Playwright code pulls from this, as well as the public repository, for the libraries it needs. It can also import any local files specific to its tests. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I won't cover how to set things up to access a private NPM registry since it depends on a lot of things outside the scope of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Building You Own Node modules
&lt;/h3&gt;

&lt;p&gt;I found you had to compile and bundle your package correctly or you would have issues with your IDE not recognizing types, Playwright not compiling properly, or Playwright blowing up when it tried to run your script. Again, I am working from memory so hopefully I have captured all of the details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In &lt;a href="https://github.com/felicia-walker/playwright-template-complex-dev" rel="noopener noreferrer"&gt;my example&lt;/a&gt; each custom library was originally it was in it's own repository and published as an NPM package to a private registry. However, in the example they are just other directories for simplicity. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea is you need both the &lt;a href="https://blog.logrocket.com/commonjs-vs-es-modules-node-js/" rel="noopener noreferrer"&gt;CSJ and ESM&lt;/a&gt; versions in the package. Typescript and modern Javascript want the ESM versions for use with 'import', but Playwright wants the older CJS version for use with 'require' in its compilation process.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to do it
&lt;/h3&gt;

&lt;p&gt;You will want to have this in your &lt;em&gt;package.json&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/cjs/index.js"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/esm/index.mjs"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.d.ts"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typesVersions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"somedir/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"dist/types/somedir/*.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/types/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/esm/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/cjs/index.js"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"./somedir/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/types/somedir/*.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/esm/somedir/*.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/cjs/somedir/*.js"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"./package.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./package.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two blocks with "somedir" will need to be repeated for each directory within your package. These additions tell how to find the CJS and ESM versions of your modules files as well as the type files for each.&lt;/p&gt;

&lt;p&gt;You also will need an &lt;em&gt;/index.ts&lt;/em&gt; file in your source root that exports every file in your modules. For the example above, this could look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./somedir/xyzzy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./somedir/plugh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;FrobozzService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./somedir/frobozz.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ended up &lt;a href="https://github.com/felicia-walker/playwright-template-complex-dev/blob/main/load-library/scripts/bundle.ts" rel="noopener noreferrer"&gt;borrowing and modifying a build script&lt;/a&gt; from the &lt;a href="https://fakerjs.dev/" rel="noopener noreferrer"&gt;FakerJS&lt;/a&gt; project for building and bundling. To build you need to make the CJS version, the ESM version, and put a copy of the CSJ version in the ESM directory.&lt;/p&gt;

&lt;p&gt;Once all of this built, you can publish and import into Playwright with no issues. Playwright should also not complain when you run your script and everything is compiled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Packaging your Playwright code into node modules for use by Playwright and an IDE  can be tricky. You will likely need to include ESM and CSJ versions of your code to satisfy both contexts.&lt;/p&gt;

&lt;p&gt;Setting up your development environment to accomplish this is not too difficult. It mainly requires the use of a bundler configured to pull in the proper files and deal with them. When using you own node modules, however, you must make sure you build them correctly or issues will arise with your IDE, webpack, or Playwright. &lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>automation</category>
      <category>node</category>
    </item>
    <item>
      <title>A System For Automatically Adding TestIds To React Code</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Wed, 11 Oct 2023 00:17:03 +0000</pubDate>
      <link>https://forem.com/feliciawalker/a-system-for-automatically-adding-testids-to-react-code-4b1j</link>
      <guid>https://forem.com/feliciawalker/a-system-for-automatically-adding-testids-to-react-code-4b1j</guid>
      <description>&lt;p&gt;While at &lt;a href="//offerup.com"&gt;OfferUp&lt;/a&gt; I had to create automation using &lt;a href="https://wix.github.io/Detox/" rel="noopener noreferrer"&gt;Detox&lt;/a&gt; and &lt;a href="https://appium.io/docs/en/2.1/" rel="noopener noreferrer"&gt;Appium&lt;/a&gt; for a React native app. To ensure we could reliably find UI elements, it was decided to use the &lt;code&gt;data-testid&lt;/code&gt; HTML attribute.&lt;/p&gt;

&lt;p&gt;React has its own specific one for the purpose of identifying elements during automation named &lt;code&gt;testID&lt;/code&gt; that gets rendered in HTML &lt;code&gt;data-testid&lt;/code&gt;. It does not interfere with any other React functionality. TestIDs should have unique values to avoid fragility and simplify test code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem And The Solution
&lt;/h2&gt;

&lt;p&gt;Usually testIDs are coded in by developers when adding markup in their React code. However, this is unreliable since people forget to do it, assign too many or too few, can create duplicate values, and use inconsistent naming schemes. This makes it difficult to write tests when you are not sure what to look for or if it exists at all. Adding/fixing testIDs while writing tests is inefficient and painful, and leads to a giant mess.&lt;/p&gt;

&lt;p&gt;It would be nice if there was a system in place to automatically assign testIDs in the markup of the React components. Since a React app is typically a hierarchy of custom components, it should be possible to assigned unique values to each component, concatenate these as child components are added, then use the final value as the &lt;code&gt;testID&lt;/code&gt; of a React tag.&lt;/p&gt;

&lt;p&gt;This solution would also fix another wrinkle. Sometimes Detox/Appium cannot access the &lt;code&gt;data-testid&lt;/code&gt; attribute for whatever reason. In this case, the &lt;code&gt;accessibilityLabel&lt;/code&gt; attribute can be used instead. In order for this to work, we need to defined it as well in the product code with the same value as the React &lt;code&gt;testID&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;TestIDs are generally constructed by defining a top level base string at a root component, passing it to children, then having them append to it. At the lowest level, where the actual React tags are used, the value is assigned to the &lt;code&gt;testID&lt;/code&gt; and &lt;code&gt;accessibilityLabel&lt;/code&gt; attributes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Example overview
&lt;/h3&gt;

&lt;p&gt;For this article we will lay out things like this for a React native app:&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%2F2kyl5l6ooqf0belc9fb0.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%2F2kyl5l6ooqf0belc9fb0.png" alt="Diagram of the example component layout" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are our own components which can contain other custom components or React ones. There is a component library (CL) at the low level which define our custom version of UI primitives (ie: buttons, text, etc...). These can use one or more React components.&lt;/p&gt;

&lt;p&gt;Assigning our testIDs is mostly done at the CL level, but can also be done at higher levels where &lt;code&gt;View&lt;/code&gt; or &lt;code&gt;Touchable&lt;/code&gt; are used. Constructing the IDs, however, happens throughout the hierarchy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Top level components
&lt;/h3&gt;

&lt;p&gt;At the top we have a screen container. The relevant code should look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyScreen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topLevelTestID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// A CL component, so add a descriptor to the id&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyCLButton&lt;/span&gt; &lt;span class="na"&gt;baseTestID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;topLevelTestID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.do-not-press&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Do not press&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MyCLButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 

  &lt;span class="c1"&gt;// An intermediate component, so just pass the base and it will append more specific info to the id&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChildThing&lt;/span&gt; &lt;span class="na"&gt;baseTestID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;topLevelTestID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For ease of use, we define a top level testID base string, &lt;code&gt;topLevelTestID&lt;/code&gt;, which will have additional id segments appended to it. This modified string is passed to lower components via their &lt;code&gt;baseTestID&lt;/code&gt; attributes. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The name "baseTestID" was used to indicate that the id is not final and to not be confused with React's &lt;code&gt;testID&lt;/code&gt;. This attribute is enforced on children via an interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the child component is a CL one, a suffix based on the type, such as ".button" or ".text" will be appended inside the CL class. Therefore, all we need to provide here is a more specific descriptor for the component. The code above will end up assigning &lt;code&gt;my-screen.do-not-press.button&lt;/code&gt; to the CL button.&lt;/p&gt;

&lt;p&gt;If the component is an intermediate one, we just pass the top level id as a base. This component will then append its own specific identifying info and repeat the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intermediate level components
&lt;/h3&gt;

&lt;p&gt;There are a few things we need for the &lt;code&gt;ChildThing&lt;/code&gt; component. The first is an interface to define its properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ChildThingProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseTestable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want it to have the &lt;code&gt;baseTestId&lt;/code&gt; attribute and the best way to that is with an interface that can be applied to all of our child components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BaseTestable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;baseTestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this interface consistently helps enforce the testIDs and also labels components that use this system.&lt;/p&gt;

&lt;p&gt;Now our component itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ChildThing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChildThingProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;baseTestID&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Assign a default baseID in case one was not passed in&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localTestID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseTestID&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.child-thing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="c1"&gt;// A React component so we need to assign the testID and accessibilityLabels directly&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;testID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChildThing&lt;/span&gt; &lt;span class="na"&gt;baseTestID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyContainer&lt;/span&gt; &lt;span class="na"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyCLButton&lt;/span&gt; &lt;span class="na"&gt;baseTestID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.actions.donuts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Donuts, please&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MyCLButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MyContainer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;   
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component first gets the passed in base id and appends a string that describes the component. This is stored in a &lt;code&gt;localTestID&lt;/code&gt; variable, which serves as the new base. We also take care to assign a default value in case a &lt;code&gt;baseTestID&lt;/code&gt; was not passed in.  &lt;/p&gt;

&lt;p&gt;There is a React component is here, and we need to assign the &lt;code&gt;testID&lt;/code&gt; and &lt;code&gt;accessibilityLabel&lt;/code&gt; attributes directly. For most other components, this happens in the CL class which wraps React primitives.&lt;/p&gt;

&lt;p&gt;You can also see how there can be components which have a &lt;code&gt;testID&lt;/code&gt; attribute instead of a &lt;code&gt;baseTestID&lt;/code&gt; one. In this case, the component just uses the passed id as is. The attribute name “testID” is used to connote that it is a final value and will not be appended to. This is mostly used for container components, since we mostly don’t care what type of container it is, so we don’t need to specify any additional information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low level components
&lt;/h3&gt;

&lt;p&gt;Finally we have the lowest level components: &lt;code&gt;MyCLButton&lt;/code&gt; and &lt;code&gt;MyContainer&lt;/code&gt;. &lt;code&gt;MyCLButton&lt;/code&gt; is part of our component library, which are leaf nodes in our UI tree. &lt;code&gt;MyContainer&lt;/code&gt; is not a leaf node, but is considered a primitive. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;MyCLButton&lt;/code&gt; will use the &lt;code&gt;baseTestId&lt;/code&gt; property since it will append the control type to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyCLButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseTestable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also like the mid level components it will append information to the testID (here, the control type), but then assign that directly to the &lt;code&gt;testID&lt;/code&gt; and &lt;code&gt;accessibilityLabel&lt;/code&gt; attributes on the React tags used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyCLButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyCLButtonProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseTestID&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localTestID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseTestID&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 

    &lt;span class="c1"&gt;// React components so we need to assign the testID and accessibilityLabels directly&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Touchable&lt;/span&gt; &lt;span class="nx"&gt;testID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="na"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localTestID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;buttonText&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;Touchable&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;   
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MyContainer&lt;/code&gt; is very similar, but uses &lt;code&gt;testID&lt;/code&gt; instead of &lt;code&gt;baseTestID&lt;/code&gt; as its property. This is enforced by having &lt;code&gt;MyContainerProps&lt;/code&gt; extend &lt;code&gt;FinalTestable&lt;/code&gt; instead of &lt;code&gt;BaseTestable&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyContainerProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;FinalTestable&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FinalTestable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testID&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  

    &lt;span class="c1"&gt;// A React component so we need to assign the testID and accessibilityLabels directly&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;testID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;    
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the View in the previous section,&lt;code&gt;MyContainer&lt;/code&gt; is a React tag and needs a final testID. The child controls will already have their testID information, so they just get passed along.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is helpful to print out the final testID value in the CL components right before using the React tags. This helps the development of UI tests since you know exactly what is on a screen and what the names are. &lt;/p&gt;

&lt;p&gt;A lot of info can be dumped, but you can always send it through a custom logger with options to clean it up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Here is a diagram of the testID values as they exist in our UI hierarchy:&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%2Ftu6ir8by9lcuc6lfhkbb.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%2Ftu6ir8by9lcuc6lfhkbb.png" alt="Tree diagram of final testID values from the example" width="800" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see how a top level base testID is defined and passed down to children, which append their own information and pass that on. Containers do not append their own information since it add noise to its children. Finally, leaf components append their own details about the control type and assign the values directly to the React tag's &lt;code&gt;testID&lt;/code&gt; and &lt;code&gt;accessibilityLabel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This system works pretty well, but can be tricky to use with components like navigation bars and modal dialogs because of their complexity. However, it is not too difficult to implement, creates unique, descriptive names, and reduces the burden on everyone to generate and keep track of testIDs.&lt;/p&gt;

</description>
      <category>react</category>
      <category>automation</category>
      <category>testing</category>
      <category>ui</category>
    </item>
    <item>
      <title>K6 Development: Beyond The Basic Setup</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Tue, 26 Sep 2023 03:40:04 +0000</pubDate>
      <link>https://forem.com/feliciawalker/k6-development-beyond-the-basic-setup-2h99</link>
      <guid>https://forem.com/feliciawalker/k6-development-beyond-the-basic-setup-2h99</guid>
      <description>&lt;p&gt;k6 is a pretty great load testing tool. However, like many test tools it seems easy to use based on the &lt;a href="https://k6.io/docs/get-started/running-k6/" rel="noopener noreferrer"&gt;getting started documentation&lt;/a&gt; or &lt;a href="https://dev.to/viniciussouza/creating-load-tests-with-k6-3lad"&gt;most&lt;/a&gt; &lt;a href="https://www.lambdatest.com/blog/k6-testing-tutorial/" rel="noopener noreferrer"&gt;articles&lt;/a&gt; you find on the web, but get tricky pretty fast if you want to have a more complex development environment. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am not knocking these articles, because they are valuable and many people are probably fine with the basic setup. However, it does not work well for distributing to multiple teams or long term scalability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;What makes a more complicated development environment? Well, here are some things I ran into while developing a load test suite based on k6:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using Typescript&lt;/li&gt;
&lt;li&gt;Using Node modules (external and from a custom API library)&lt;/li&gt;
&lt;li&gt;Sharing k6 utility files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There were many more things that went into making a robust k6 solution, but I will cover them in future articles. This one will focus on setting up your development environment to be able to handle the above list.&lt;/p&gt;

&lt;p&gt;A slightly simplified version of what I was building looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is sample code you can refer to at my &lt;a href="https://github.com/felicia-walker/playwright-template-complex-dev" rel="noopener noreferrer"&gt;playwright-template-complex-dev respository&lt;/a&gt; which covers all sections of this article. It is a simplified version of the diagram below.&lt;/p&gt;
&lt;/blockquote&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%2F354xt8vzcb7x0owz3ny8.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%2F354xt8vzcb7x0owz3ny8.png" alt="UML diagram of a simplified code setup" width="741" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The k6 functionality of calling API endpoints was put into its own library. This was done to make sharing with other teams easier. This library in turn depends on external libraries as well as another custom API library. The API library helps build web request urls and payloads for an API test suite. It made sense to leverage this instead of recoding all of those endpoints in k6.&lt;/p&gt;

&lt;p&gt;Both this load library and the API library are published to a private NPM repository. The actual k6 script pulls from this, as well as the public repository, for the libraries it needs. It can also import any local files specific to its tests. The use of the load library greatly reduced the size and duplication of code in the k6 scripts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I won't cover how to set things up to access a private NPM registry since it depends on a lot of things outside the scope of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a final note, the load library and scripts each reside in different Git repositories. This was done for NPM package version reasons, but also to keep the checkin histories and processes cleaner. It was also suppose to make it easier for other teams to update the libraries, if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Bundler: The Key To Everything
&lt;/h2&gt;

&lt;p&gt;A bundler is software that combines Javascript files and dependencies into a single file. Since the k6 engine is Go based and runs scripts in an embedded way, it expects to only pull in single JS files with no dependencies. Therefore, if you do anything with &lt;code&gt;import&lt;/code&gt;s or non-vanilla JS, you will need to bundle.&lt;/p&gt;

&lt;p&gt;There are many bundlers out there, but for this article we will be using &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;webpack&lt;/a&gt;. It is a bit older, but it's what k6 uses in &lt;a href="https://k6.io/docs/using-k6/modules/" rel="noopener noreferrer"&gt;its examples&lt;/a&gt; and what I got to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Typescript To Work
&lt;/h2&gt;

&lt;p&gt;Basically, you install Typescript, set up a bundler, and use that to compile the Typescript into a single executable file. This &lt;a href="https://github.com/grafana/k6-template-typescript" rel="noopener noreferrer"&gt;sample repository&lt;/a&gt; is pretty spot on for doing just this. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;a href="https://github.com/felicia-walker/k6-template-complex-dev" rel="noopener noreferrer"&gt;example repository for this article&lt;/a&gt; also works, but contains more than just the basic Tyepscript setup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using the Typescript only repository above, you really just need to copy the following files into your script location:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tsconfig.json&lt;/li&gt;
&lt;li&gt;webpack.config.js&lt;/li&gt;
&lt;li&gt;.bablerc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And make sure the following &lt;code&gt;devDependencies&lt;/code&gt; are in the &lt;em&gt;package.json&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.13.16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/plugin-proposal-class-properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.13.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/plugin-proposal-object-rest-spread"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.13.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.13.15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.13.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/k6"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.43.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/webpack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.28.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"babel-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.2.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"babel-plugin-module-resolver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"clean-webpack-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.0.0-alpha.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"copy-webpack-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^9.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.7.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"webpack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.76.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"webpack-cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"webpack-glob-entries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;yarn install&lt;/code&gt; to install, and you should be set. Now just build with &lt;code&gt;yarn webpack&lt;/code&gt; and a single output JS file will show up in the &lt;em&gt;/dist&lt;/em&gt; directory. You can run k6 with this file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing Node Modules
&lt;/h2&gt;

&lt;p&gt;Since k6 is not Node.js compatible, it does not know how to resolve node modules. Therefore, you need to use a bundler to pull them in. There is some &lt;a href="https://k6.io/docs/using-k6/modules/#bundling-node-modules" rel="noopener noreferrer"&gt;documentation on using node modules&lt;/a&gt;, but it is fairly basic. The Typescript section above already has most of what is needed for most libraries out there. &lt;/p&gt;

&lt;p&gt;From the Typescript only example, you need to make one modification to the &lt;em&gt;webpack.config.js&lt;/em&gt; file. It has the &lt;em&gt;/node_modules&lt;/em&gt; directory as excluded, but I seem to remember having to undo this. Replace these sections of this file with this code, and you should be good to go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;resolve:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;extensions:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'.ts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'.mjs'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;modules:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'node_modules'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;module:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;rules:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;test:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/\.ts$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;use:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'babel-loader'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Your own Node modules
&lt;/h3&gt;

&lt;p&gt;Things get tricky when you start importing your own node modules. In &lt;a href="https://github.com/felicia-walker/k6-template-complex-dev" rel="noopener noreferrer"&gt;my example&lt;/a&gt; this is the &lt;code&gt;load-library&lt;/code&gt;. Originally it was in it's own repository and published as an NPM package to a private registry, but in the example it is just another directory for simplicity. &lt;/p&gt;

&lt;p&gt;I found you had to compile and bundle your package correctly or you would have issues with your IDE not recognizing types, webpack not compiling properly, or k6 blowing up when it tried to run your script. Again, I am working from memory so hopefully I have captured all of the details.&lt;/p&gt;

&lt;p&gt;The idea is you need both the &lt;a href="https://blog.logrocket.com/commonjs-vs-es-modules-node-js/" rel="noopener noreferrer"&gt;CJS and ESM&lt;/a&gt; versions in the package. Typescript and modern Javascript want the ESM versions for use with 'import', but Node and k6 want the older CJS version for use with 'require'. In addition, I found you had to copy the CJS version into the ESM destination directory or k6 would be unhappy. There is probably a more elegant solution, but I could not figure it out.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to do it
&lt;/h3&gt;

&lt;p&gt;You will want to have this in your &lt;em&gt;package.json&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/cjs/index.js"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/esm/index.mjs"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.d.ts"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typesVersions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"somedir/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"dist/types/somedir/*.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/types/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/esm/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/cjs/index.js"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"./somedir/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/types/somedir/*.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/esm/somedir/*.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/cjs/somedir/*.js"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"./package.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./package.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two blocks with "somedir" will need to be repeated for each directory within your package. These additions tell how to find the CJS and ESM versions of your modules files as well as the type files for each.&lt;/p&gt;

&lt;p&gt;You also will need an &lt;em&gt;/index.ts&lt;/em&gt; file in your source root that exports every file in your modules. For the example above, this could look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./somedir/xyzzy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./somedir/plugh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;FrobozzService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./somedir/frobozz.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ended up &lt;a href="https://github.com/felicia-walker/k6-template-complex-dev/blob/main/load-library/scripts/bundle.ts" rel="noopener noreferrer"&gt;borrowing and modifying a build script&lt;/a&gt; from the &lt;a href="https://fakerjs.dev/" rel="noopener noreferrer"&gt;FakerJS&lt;/a&gt; project instead of using webpack. To build you need to make the CJS version, the ESM version, and put a copy of the CJS version in the ESM directory.&lt;/p&gt;

&lt;p&gt;Once all of this built, you can publish and import into k6 with no issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Non-local k6 JavaScript Libraries
&lt;/h2&gt;

&lt;p&gt;There are a number of &lt;a href="https://jslib.k6.io/" rel="noopener noreferrer"&gt;k6 provided utility libraries&lt;/a&gt; written in Javascript, but are not installed with k6 itself. You will need to download a local copy of the file or use a dynamic import via HTTP. &lt;/p&gt;

&lt;p&gt;For a local file, if you don't store the file next to your script you can just use a relative path when you import. However, you may want to make the file available for easier sharing by including in your own library module (especially if you make modifications). This will require the same setup and build process as the &lt;strong&gt;Your own Node modules&lt;/strong&gt; section above. You just need to include the k6 JS files in the &lt;em&gt;index.ts&lt;/em&gt; and &lt;em&gt;package.json&lt;/em&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Getting k6 to work with Typescript, node modules, and using JS files can be tricky. You will likely need to do one or more of these if you build out a k6 based load framework.&lt;/p&gt;

&lt;p&gt;Setting up your development environment to use Typescript, Node modules, or multiple files is not too difficult. It mainly requires the use of a bundler configured to pull in the proper files and deal with them. When using you own node modules, however, you must make sure you build them correctly or issues will arise with your IDE, webpack, or k6. You must build similarly if you want share any k6 provided JS utilities or modify them to have any dependencies.&lt;/p&gt;

</description>
      <category>k6</category>
      <category>loadtesting</category>
      <category>tutorial</category>
      <category>automation</category>
    </item>
    <item>
      <title>Getting Started In Software Testing</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Thu, 14 Sep 2023 18:36:23 +0000</pubDate>
      <link>https://forem.com/feliciawalker/getting-started-in-software-testing-8gj</link>
      <guid>https://forem.com/feliciawalker/getting-started-in-software-testing-8gj</guid>
      <description>&lt;p&gt;There are a number of &lt;a href="https://dev.to/testrigtech/a-comprehensive-guide-how-to-start-your-career-in-software-testing-39jh"&gt;articles&lt;/a&gt; out there about &lt;a href="https://www.indeed.com/career-advice/finding-a-job/software-testing-engineer" rel="noopener noreferrer"&gt;how&lt;/a&gt; to get &lt;a href="https://www.udemy.com/course/your-guide-to-start-software-testing-career/" rel="noopener noreferrer"&gt;started&lt;/a&gt; in software testing, but this is mine. I know several people who are interested in testing and I would like to collect some thoughts and resources in a shared place for them. So, this will be more informally written but hopefully still useful for folks in general.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Links within the article point to something that serves as an intro to that topic, and hopefully a jumping off point for further research.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Software Testers Do
&lt;/h2&gt;

&lt;p&gt;If asked, most people would say software testers find bugs. This is true, but it is part of a larger mission of assessing  the state of the product and reporting the risks to decision makers (borrowed loosely from &lt;a href="https://www.satisfice.com/" rel="noopener noreferrer"&gt;James Bach&lt;/a&gt;). Testers use a product, sometimes in a focused or novel way, to see what works and what does not. They report their findings to others who can prioritize and make decisions about releasing in this state. Sometimes bugs need to be fixed. Sometimes it's ok to risk releasing in a less than perfect condition. Testers are the ones who gather the detailed information to aid in those decisions.&lt;/p&gt;

&lt;p&gt;Day to day this involves creating, writing, and executing tests or other test related activities. Documentation and analyzing results are also frequent tasks. As a tester progresses in their skills, they may start to write automation code to help speed up their testing or execute tedious portions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Skills
&lt;/h2&gt;

&lt;p&gt;Even at entry level there are a number of skills that are good to have, or at least be familiar with. Ideally these would be taught to you as a new employee, but that's not how things generally work these days. Fortunately there is the lovely internet to help fill the gap.&lt;/p&gt;

&lt;p&gt;While technical skills are important, to be successful at testing long term you need certain soft skills as well. These are important even in the most robotic form of testing where you just execute test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Familiarity with the SDLC
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Systems_development_life_cycle" rel="noopener noreferrer"&gt;SDLC&lt;/a&gt; is the Software Development Life Cycle. Basically, it is the process a company uses for turning business requirements into features, getting them into a product, and shipping that product. &lt;/p&gt;

&lt;p&gt;There are many types of processes, but the two you should be familiar with are &lt;a href="https://www.tutorialspoint.com/sdlc/sdlc_waterfall_model.htm" rel="noopener noreferrer"&gt;waterfall&lt;/a&gt; and &lt;a href="https://monday.com/blog/rnd/agile-sdlc/" rel="noopener noreferrer"&gt;agile&lt;/a&gt;. Waterfall is the more classic, linear version and agile is more modern. It was created to address inefficiencies with waterfall.&lt;/p&gt;

&lt;h4&gt;
  
  
  General test concepts
&lt;/h4&gt;

&lt;p&gt;There are a few general test concepts that are good to know. I would be surprised if they did not come up in an interview.&lt;/p&gt;

&lt;p&gt;The biggest general concept is &lt;a href="https://theqalead.com/topics/testing-pyramid/" rel="noopener noreferrer"&gt;the test pyramid&lt;/a&gt;. The idea is that most tests should be targeted, quick &lt;a href="https://www.guru99.com/unit-testing-guide.html" rel="noopener noreferrer"&gt;unit tests&lt;/a&gt;, then fewer &lt;a href="https://www.lambdatest.com/learning-hub/integration-testing" rel="noopener noreferrer"&gt;integration tests&lt;/a&gt; to ensure components work together, and a small amount of &lt;a href="https://www.freecodecamp.org/news/end-to-end-testing-tutorial/" rel="noopener noreferrer"&gt;end to end tests&lt;/a&gt;, which are slow and can be flaky. While not perfect, the pyramid is a bit of a standard. Looking into it is also a good jumping off point for test types and techniques.&lt;/p&gt;

&lt;p&gt;Another general concept is defect tracking. Every company has a different way to track the discovered defects, but the flow is generally the same. Knowing how to &lt;a href="https://techbeacon.com/app-dev-testing/how-write-effective-software-defect-reports" rel="noopener noreferrer"&gt;document a defect&lt;/a&gt;, how to report it, and what happens to it during and post fix are important.&lt;/p&gt;

&lt;p&gt;I should mention &lt;a href="https://www.softwaretestinghelp.com/what-is-exploratory-testing/" rel="noopener noreferrer"&gt;exploratory testing&lt;/a&gt; somewhere. This is a test concept based on exploration and the skill of the tester instead of a checklist of test cases. It gaining more traction and is worth reading about. Personally, I think it is a much better way to test than more traditional paradigms.&lt;/p&gt;

&lt;h4&gt;
  
  
  Basic test techniques and heuristics
&lt;/h4&gt;

&lt;p&gt;There are a huge number of test techniques that can be applied. You do not need to know all of them, but know these at a minimum: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Black/white/grey box&lt;/li&gt;
&lt;li&gt;Unit/integration&lt;/li&gt;
&lt;li&gt;Load and performance&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Internationalization/localization&lt;/li&gt;
&lt;li&gt;Accessibility&lt;/li&gt;
&lt;li&gt;Usability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also heuristics, or guidelines, of activities you can do within each of the above areas. These include things like decision tables, flow charts, domain analysis, and many others. &lt;/p&gt;

&lt;p&gt;Here are some comprehensive lists with more details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.ministryoftesting.com/articles/ab1cd85c?s_id=15770095" rel="noopener noreferrer"&gt;Heuristic cheat sheet&lt;/a&gt; - Bounds to check, pearls or wisdom, and other suggestions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.satisfice.com/download/heuristic-test-strategy-model" rel="noopener noreferrer"&gt;Heuristic Test Strategy Model&lt;/a&gt; - From James Bach, this doc has several large lists of heuristics to keep in mind&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Coding
&lt;/h4&gt;

&lt;p&gt;Knowing how to program is not required to test software, but it does help greatly. It will help you view the product source code so you can troubleshoot better and understand the unit tests. Coding allows you to create utilities to help with your testing. Even if this is not full test automation, small ad-hoc programs are useful for verification and generation of large data sets. You can also automate tedious tasks.&lt;/p&gt;

&lt;p&gt;I would recommend learning &lt;a href="https://developer.mozilla.org/en-US/docs/Web/javascript" rel="noopener noreferrer"&gt;Javascript&lt;/a&gt; or &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt; as a first language. Both are very common, easier to pick up, and do not require a compiler (although there are plenty of online development environments now). If you choose Python, use the 3.x version, not the legacy 2.x version.&lt;/p&gt;

&lt;p&gt;Here are two good sites for learning how to code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.codecademy.com/" rel="noopener noreferrer"&gt;Codeacademy.com&lt;/a&gt; - Free beginner coding lessons&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://exercism.org/" rel="noopener noreferrer"&gt;Exercism.com&lt;/a&gt; - Another free site for learning programming languages. It offers quite a few, 67 total, including more advanced languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Soft
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Lateral thinking
&lt;/h4&gt;

&lt;p&gt;I've come to realize that &lt;a href="https://en.wikipedia.org/wiki/Lateral_thinking" rel="noopener noreferrer"&gt;lateral thinking&lt;/a&gt; is incredibly important for software testing. The ability to not only have a Plan B, but a C, D, E, and beyond is necessary since things frequently do not go as planned. This not just for creating test cases, but setting up the conditions for testing and how to work around issues and troubleshoot them.&lt;/p&gt;

&lt;p&gt;Besides having a lot of computer or testing experience, it is difficult to develop this skill. Personally I have had success by working on &lt;a href="https://www.puzzlehunt.net/list" rel="noopener noreferrer"&gt;puzzle hunts&lt;/a&gt; and playing &lt;a href="https://boardgamegeek.com/thread/1601627/top-100-best-heavy-games-results" rel="noopener noreferrer"&gt;heavier board games&lt;/a&gt;. You get exposed to a variety of puzzles and mechanics that require you to think outsie of the box. These activities also help you build tenacity.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tenacity/Grit
&lt;/h4&gt;

&lt;p&gt;The ability to stick with a problem and see it through to the end is important. This usually arises when trying to narrow down a defect's behavior or executing difficult test cases. It may not be easy to figure out what to do next, unravel complex behavior, or avoid getting frustrated. Of course, you should also know when something is too difficult and how to ask for help. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.lifehack.org/884651/what-is-grit" rel="noopener noreferrer"&gt;standard advice&lt;/a&gt; is to try something you are not good and, practice a lot, and don't give up when it is hard. Good news is that this will happen as you pursue your testing career. Bad news is that it is very difficult in any context. You could also try some of the suggestions above under &lt;strong&gt;Lateral Thinking&lt;/strong&gt;. Puzzles and board games can be difficult, but also more bite sized activities.&lt;/p&gt;

&lt;h4&gt;
  
  
  Documentation/organization
&lt;/h4&gt;

&lt;p&gt;Being able to record ideas, activities, and findings are very key for a software tester. You will need to document tests you create and in a format that is easy to follow. You will need to document your findings and write up defect reports. Cleanly written reports make the bug fixing process more efficient and cuts down on your amount of work in the long run, developers and program managers will spend less time getting clarifications with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Education / Certifications
&lt;/h2&gt;

&lt;p&gt;Most testing positions, even entry level, will want you to have a Bachelor's degree in computer science or a similar field. And now my very personal opinion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I started none of the testers had this. I have two electrical engineering degrees, but others had degrees in history, applied match, and analytical chemistry. We all did fine. It's not like a CS degree will teach you anything about testing, so the requirement seems like gatekeeping. Maybe having the college experience is valuable, but I know people who are amazing engineers who don't have a degree at all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I guess what I want to say is don't feel like you have to have a four year degree in CS before applying. With the internet of today you can learn much more applicable skills online and create a portfolio to demonstrate them. It may be harder to break through that initial round of interviews, but I would also hope companies at least consider everything else on a resume before rejection. Probably a bit naive of me, but I have to try and think positive when I can.&lt;/p&gt;

&lt;h3&gt;
  
  
  Online courses
&lt;/h3&gt;

&lt;p&gt;There are many places that offer courses in software testing. Many cost, but are probably worth the money (especially compared to a university). I know there are also lots of YouTube and probably TikTok channels that offer content as well. The following have software testing specific courses, and are generally regarded as top learning sites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.udemy.com/courses/search/?src=ukw&amp;amp;q=software+testing" rel="noopener noreferrer"&gt;Udemy&lt;/a&gt; - A popular online learning site. Most courses are paid, but frequently discounted. Here is a &lt;a href="https://www.udemy.com/course/your-guide-to-start-software-testing-career/" rel="noopener noreferrer"&gt;free intro course&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.coursera.org/search?query=software%20testing&amp;amp;" rel="noopener noreferrer"&gt;Coursera&lt;/a&gt; - I have not used it, but it is frequently recommended and has free courses&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://app.pluralsight.com/id" rel="noopener noreferrer"&gt;Pluralsight&lt;/a&gt; - You need to log in to view the catalog. There is a 10 day trial, but it can be a bit pricey after that. There is not much testing content, but a ton of software stuff in general. I've used them for years and found it worth the money.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  About certifications
&lt;/h3&gt;

&lt;p&gt;Many articles talk about &lt;a href="https://www.istqb.org/" rel="noopener noreferrer"&gt;ISTQB&lt;/a&gt; certifications and how those are alternatives to school or a supplement to it. Many organizations also list them as job requirements. However, they are expensive. Cue my personal opinion again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From what I've seen these certifications teach an older and more limited idea of what software testing is. Worse, it passes their content off as the industry standard and if you don't follow it, you are lacking. I also have issue with certification programs in general, since they frequently seem like cash grabs and don't offer much in terms of learning skills.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That said, it is worth looking at ISTQB content, but I would not pay their expensive costs. Also, the content provides a decent basis of testing skills and ideas, but &lt;strong&gt;they are not the end all be all of testing&lt;/strong&gt;! Please don't spend all your time focusing on this. It may seem like a smart move for getting started, but your time and money can be better spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Experience
&lt;/h2&gt;

&lt;p&gt;And now the hardest part of getting started, obtaining experience. Ideally you would get this in an entry level position, but for some reason companies want testing experience even for entry level jobs. This is doubly baffling to me since its much easier and common to have programming experience than testing. But there are ways to gain experience outside having an internship as a student.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crowd sourced freelancing
&lt;/h3&gt;

&lt;p&gt;There are a number of companies that use freelancers to perform test work for clients. The clients supply the tests to run and what devices to run them on, the company distributes that to their freelancers, who run the tests and report back, and all the aggregated results get returned to the client. These freelancers are anyone who signs up and completes some basic training. I do not have experience with this, but it seems like an excellent way to get experience and get paid at the same time. I'm sure the amount is not much, but still.&lt;/p&gt;

&lt;p&gt;Here are a few resources to check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.softwaretestinghelp.com/crowdsourced-testing-companies/" rel="noopener noreferrer"&gt;Top 10 list&lt;/a&gt; of crowdsourced test companies&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://utest.com/" rel="noopener noreferrer"&gt;uTest&lt;/a&gt; - Seems like a popular option&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://testlio.com/" rel="noopener noreferrer"&gt;Testlio&lt;/a&gt; - Used by a company I worked a contract for. They seemed happy with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pair with mentor
&lt;/h3&gt;

&lt;p&gt;If you can, find a senior software tester who can be a mentor. Besides passing on skills, they can offer suggestions on how things are done in the real world and ideas on what to study. You could also work with a mentor on pair testing or other activities such as reading discussions. &lt;/p&gt;

&lt;p&gt;Finding a mentor can be difficult. If you know people in tech, ask them if they know of anyone. You can also check out local meetups and groups. Finally, some of the larger testing sites on the internet have mentoring resources. Here is one from &lt;a href="https://www.thetesttribe.com/mentor/" rel="noopener noreferrer"&gt;The Test Tribe&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test random things and write posts or keep notebook
&lt;/h3&gt;

&lt;p&gt;You can always test things around you and record the results. It doesn't just have to be just software. You can look at any piece of technology, or the system it is a part of, and start creating test cases, strategies, and configurations. You could also do a bit of &lt;a href="https://www.softwaretestinghelp.com/what-is-exploratory-testing/" rel="noopener noreferrer"&gt;exploratory testing&lt;/a&gt; and note down what you did, much like a &lt;a href="https://www.science.org/content/article/how-keep-lab-notebook" rel="noopener noreferrer"&gt;scientist recording research&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developsense.com/" rel="noopener noreferrer"&gt;Michael Bolton&lt;/a&gt; does this &lt;a href="https://michaelbolton.net/presentations/ExploratoryTestingRecordingAndReporting.pdf" rel="noopener noreferrer"&gt;with a notebook&lt;/a&gt;, but you may want to use something that can be shared online. A tool like &lt;a href="https://miro.com/index/" rel="noopener noreferrer"&gt;Miro&lt;/a&gt; or an online note taking app would work. Of course, you could always just take pictures of your notebook pages and share as an image gallery for portfolio purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open source contributions
&lt;/h3&gt;

&lt;p&gt;Contributing to an open source project is something that you can link to and demonstrates you have worked in an actual software development flow. Take a look at the list of issues for projects you already use. Some may need further testing or be narrowed down. You could do that and add your findings. You could also test on your own and submit any defects you find.&lt;/p&gt;

&lt;p&gt;The testing you do does not need to be limited to functional cases. You could run accessibility tools, security tools, or review the unit tests. Heck, maybe they don't have unit tests at all, so you could ask to establish some. Doing all this will also give you a feel for what it is like to work with a developer to improve the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I know if has been forever since I started my software testing career and that the bar is higher since then. However, there are tons of resources out there to help you. I hope what I've presented here is useful. I thought about what I look for when I interview junior testers and what is really important about being a good tester. Some of it is a little non-standard, but it's important to recognize when existing things need to evolve.&lt;/p&gt;

&lt;p&gt;If you have any questions, please post in the comments below or send me an email.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>career</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>A System For Dynamically Handling Element Names In Cucumber</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Fri, 08 Sep 2023 22:46:24 +0000</pubDate>
      <link>https://forem.com/feliciawalker/a-system-for-dynamically-handling-element-names-in-cucumber-4mk6</link>
      <guid>https://forem.com/feliciawalker/a-system-for-dynamically-handling-element-names-in-cucumber-4mk6</guid>
      <description>&lt;p&gt;This is a followup post to &lt;a href="https://dev.to/feliciawalker/my-cucumber-style-guide-cm7"&gt;my cucumber style guide&lt;/a&gt;. I felt it was getting too long, so I split this topic off into its own post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Sooner or later you will have steps in your feature file that are the same in many places except for a word or phrase. Usually this is something like a user name, screen element, action, or index. For example, if you had the following scenarios (or more than this of the same form):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; One
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is on the home screen
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user sees the search bar element
      &lt;span class="nf"&gt;And &lt;/span&gt;sees the categories bar element
      &lt;span class="err"&gt;...&lt;/span&gt;

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Two
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is on the settings screen
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user sees the username element
      &lt;span class="nf"&gt;And &lt;/span&gt;sees the password element
      &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line would need its own step definition, but what those steps do would basically be the same. In this case the &lt;code&gt;Given&lt;/code&gt; statements would verify we are on the proper screen and the others verify some UI element is present.&lt;/p&gt;

&lt;p&gt;It would be better if there was a way to eliminate duplicate code in the step definitions by collapsing these similar steps. Fortunately, this is possible and there is a way to expand upon that to be even more flexible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Simple Solution
&lt;/h2&gt;

&lt;p&gt;The simple way to collapse the step code is to make use of &lt;a href="https://cucumber.io/docs/cucumber/api/?lang=java#step-arguments" rel="noopener noreferrer"&gt;step arguments&lt;/a&gt;. Step arguments allow dynamic values to be extracted during the step matching process. See &lt;a href="https://cucumber.io/docs/cucumber/step-definitions/?lang=javascript#expressions" rel="noopener noreferrer"&gt;here&lt;/a&gt; for details about how to do this via regular or Cucumber expressions.&lt;/p&gt;

&lt;p&gt;Refactoring the above example involves making the screen name  a parameter for the "is on" steps and the element name a parameter for the "sees the" steps. While you can change the step matchers only, it is a good idea to add some delimiters around the dynamic values. This marks them clearly in the feature file as well as making it easier to write the matchers.&lt;/p&gt;

&lt;p&gt;The feature file now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; One
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is on the &lt;span class="s"&gt;"home"&lt;/span&gt; screen
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user sees the &lt;span class="s"&gt;"search bar"&lt;/span&gt; element
      &lt;span class="nf"&gt;And &lt;/span&gt;sees the &lt;span class="s"&gt;"categories dropdown"&lt;/span&gt; element
      &lt;span class="err"&gt;...&lt;/span&gt;

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Two
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is on the &lt;span class="s"&gt;"settings"&lt;/span&gt; screen
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user sees the &lt;span class="s"&gt;"username"&lt;/span&gt; element
      &lt;span class="nf"&gt;And &lt;/span&gt;sees the &lt;span class="s"&gt;"password"&lt;/span&gt; element
      &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The step code changes to this (note, it is written in Typescript and uses regular expressions for matching):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^The user is on the "&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;" screen$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isScreenPresent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;settingsScreen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isScreenPresent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid screen name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^The user sees the "&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;" element$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;categories dropdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isElementVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;settingsScreen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isElementVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid element name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see how we capture everything within the quotes, which gets passed as the single value to the associated method. A switch statement executes code specific to this value. As seen in the second step, &lt;code&gt;case&lt;/code&gt; statements can be grouped together if the call to supporting code is the same.&lt;/p&gt;

&lt;p&gt;While this is an improvement over single steps for everything, it is very likely the code called in the &lt;code&gt;case&lt;/code&gt; statements will still be very similar to each other. If we are clever, we may be able to further collapse the step code.&lt;/p&gt;

&lt;p&gt;There is also the problem with name collisions in the &lt;code&gt;switch&lt;/code&gt; statements. However, these can be fixed with rules around how we name values passed to the step definitions. In our example, if two screens had an email field, we would need to name them "screen 1 email" and "screen 2 email". The screen name must be passed to the step in order to have two unique &lt;code&gt;case&lt;/code&gt; statements.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Solution
&lt;/h2&gt;

&lt;p&gt;The cleverness mentioned above involves the use of &lt;a href="https://refactoring.guru/design-patterns/factory-method" rel="noopener noreferrer"&gt;factories&lt;/a&gt; and &lt;a href="https://www.baeldung.com/cs/program-to-interface" rel="noopener noreferrer"&gt;interfaces&lt;/a&gt;. A factory is used to instantiate the object to act upon instead of using shared global instances. Since these objects will all be similar, they can implement an interface of common actions. Together this allows the lengthy &lt;code&gt;switch&lt;/code&gt; statements to be replaced with a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^The user is on the "&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;" screen$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ScreenFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isScreenPresent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^The user sees the "&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt; &amp;gt; &lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;" element$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ScreenFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isItemVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our example the factory returns a screen instance. We need to pass the name of the screen to get, which is extracted directly from the cucumber text. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note, to get the screen name when specifying a element we introduced the naming rule of "Screen &amp;gt; Element". The use of "&amp;gt;" makes the extraction regular expression much easier to code.&lt;/p&gt;

&lt;p&gt;It also helps make the feature file text better by clearly dividing sections of the name. This is especially beneficial with deeper elements (Ex: &lt;em&gt;Settings &amp;gt; Integrations &amp;gt; Email &amp;gt; Server&lt;/em&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once we have our object to act on, we can call the actual action with the appropriate parameters. Since the factory generated objects all implement the same interface, we guarantee that these actions are valid all of the time. If we do happen to have any exceptions, we can still have some logic to account for those as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;No matter what you are automating using Cucumber, patterns in the text will form. By imposing simple rules about naming the dynamic shared portions, you can simplify the underlying step definition code. Also, by using the factory pattern along with interfaces the step definition code become cleaner and even more compact.&lt;/p&gt;

&lt;p&gt;Obviously I am highly biased, but I like this pattern since it results in simple, readable step definition code as well as very clear feature file text. I hope you will give it a try.&lt;/p&gt;

</description>
      <category>cucumber</category>
      <category>gherkin</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>My Cucumber Style Guide</title>
      <dc:creator>Felicia Walker</dc:creator>
      <pubDate>Sat, 26 Aug 2023 01:43:21 +0000</pubDate>
      <link>https://forem.com/feliciawalker/my-cucumber-style-guide-cm7</link>
      <guid>https://forem.com/feliciawalker/my-cucumber-style-guide-cm7</guid>
      <description>&lt;p&gt;While at &lt;a href="https://offerup.com" rel="noopener noreferrer"&gt;Offerup.com&lt;/a&gt; I had to use &lt;a href="https://cucumber.io/" rel="noopener noreferrer"&gt;Cucumber&lt;/a&gt; to run &lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;Gherkin&lt;/a&gt; style tests. I had not used this style of test writing before. While it seems easy and straight forward at first, I quickly realized that its flexibility would allow the wording and structure of tests to quickly diverge and become unmanageable.&lt;/p&gt;

&lt;p&gt;Here is my approach to writing decent tests, Gherkin best practices, and how to name elements. These guidelines are designed to make the tests as understandable, concise, and flexible while reducing duplicated code. A lot of ideas were taken from this &lt;a href="https://automationpanda.com/2017/01/30/bdd-101-writing-good-gherkin/" rel="noopener noreferrer"&gt;Automation Panda post&lt;/a&gt;, which is good reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  General
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Describing behavior in a concise manner is the most important thing&lt;/li&gt;
&lt;li&gt;You should strive to use short phrases to accurately convey what each line is doing&lt;/li&gt;
&lt;li&gt;What you write should describe what you are doing, not how or why&lt;/li&gt;
&lt;li&gt;Feature titles should follow the &lt;strong&gt;Scenario&lt;/strong&gt; rules below&lt;/li&gt;
&lt;li&gt;Do not use the &lt;code&gt;Rules&lt;/code&gt; keyword. More than one is not supported by &lt;em&gt;cucumber.js&lt;/em&gt;, which makes it very awkward&lt;/li&gt;
&lt;li&gt;Try to not use the &lt;code&gt;Background&lt;/code&gt; keyword. Since it is common setup, it can render the &lt;code&gt;Given&lt;/code&gt; sections of tests irrelevant. Leaving out the &lt;code&gt;Given&lt;/code&gt; then makes the flow odd.

&lt;ul&gt;
&lt;li&gt;You could use &lt;code&gt;Background&lt;/code&gt; then make a step definition for “Given The Background task passed” and have it do nothing. This would just serve to create decent Gherkin in the spec file.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Create a new account using valid information
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user starts on the create account screen
    &lt;span class="nf"&gt;When &lt;/span&gt;The user enters valid account information
      &lt;span class="nf"&gt;And &lt;/span&gt;presses the &lt;span class="s"&gt;"Create Account &amp;gt; Create Account"&lt;/span&gt; button
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user arrives at the &lt;span class="s"&gt;"Secure Your Account"&lt;/span&gt; screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wording is concise and describes what is being done so anyone can easily see what this test does. It is also worded in a way that allows for flexibility with dynamic items (phrases in quotes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedded Test Data
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don't use scenario outlines or data tables to execute extra cases you don’t need just because you can

&lt;ul&gt;
&lt;li&gt;This increases the execution time and increases the change of a random failure&lt;/li&gt;
&lt;li&gt;Most of the time one element of a behavioral set is fine to verify the behavior of all. For example, you don’t need to open up every search category if they all look the same, but you may want to do one of one type and one of another if they can result in different results.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; The first message page contains the proper elements
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is logged in
    &lt;span class="nf"&gt;When &lt;/span&gt;The user taps the &lt;span class="s"&gt;"Item &amp;gt; Inquire"&lt;/span&gt; button
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user sees these elements on the &lt;span class="s"&gt;"Send Message"&lt;/span&gt; page
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;Send&lt;/span&gt; &lt;span class="nv"&gt;A&lt;/span&gt; &lt;span class="nv"&gt;Message&lt;/span&gt; &lt;span class="nv"&gt;Heading&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;Profile&lt;/span&gt;           &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="n"&gt;Details&lt;/span&gt;           &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;      &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A data table is appropriate here since we want to verify a number of elements are present. One may be sufficient if we just wanted to know if we were on that page, but the Scenario clearly says we want to verify all proper elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario Outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Send a first message with each suggested message
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is logged in
    &lt;span class="nf"&gt;When &lt;/span&gt;The user taps the &lt;span class="s"&gt;"Send A Message &amp;gt; &amp;lt;suggested response&amp;gt;"&lt;/span&gt; button
    &lt;span class="nf"&gt;Then &lt;/span&gt;The first sent message is &lt;span class="s"&gt;"&amp;lt;suggested response&amp;gt;"&lt;/span&gt;

    &lt;span class="nn"&gt;Examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;index&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;suggested&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt;             &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;0&lt;/span&gt;     &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Hi,&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;still&lt;/span&gt; &lt;span class="n"&gt;available?&lt;/span&gt;   &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;1&lt;/span&gt;     &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Hi,&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;buy&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt;         &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;2&lt;/span&gt;     &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Hi,&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;meet?&lt;/span&gt;              &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;3&lt;/span&gt;     &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Will&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;ship&lt;/span&gt; &lt;span class="n"&gt;via&lt;/span&gt; &lt;span class="n"&gt;Frobozz?&lt;/span&gt;     &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the previous example this makes sense since we want to test each option presented to the user and each results in a different behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario Text
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scenario titles should be one line, any additional info can go in the description

&lt;ul&gt;
&lt;li&gt;Watch out for usage of "and" and "or" which can indicate the need to split into multiple scenarios&lt;/li&gt;
&lt;li&gt;"Because", "since", and "so" portions can be left out since they describe why you are doing this, not what&lt;/li&gt;
&lt;li&gt;No "verify" or "should" since that is about assertions, not describing behavior. This belongs in the steps.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario Outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Send a first message with each suggested message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not bad, but the "suggested message" part could probably be cut or moved to the &lt;code&gt;Description&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Selecting the email login method takes you to that landing page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "takes you to that landing page" part has an implicit "should" in front of it and should probably be removed. This phrase describes a result, and that should be covered by a subsequent &lt;code&gt;Then&lt;/code&gt; statement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario Content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scenarios should be 10 lines or less&lt;/li&gt;
&lt;li&gt;Try to have only one &lt;code&gt;Given/When/Then&lt;/code&gt; flow per scenario. This is the idea of one scenario equals one behavior.&lt;/li&gt;
&lt;li&gt;Lengthy sections can be encapsulated into a single step definition. This could also enable reuse of behavior since it is done outside of the &lt;em&gt;spec&lt;/em&gt; file.&lt;/li&gt;
&lt;li&gt;It may be possible to skip long setup sections by directly navigating to the start of the actual test. This is most appropriate for UI tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;        &lt;span class="nf"&gt;Then &lt;/span&gt;The user sees the &lt;span class="s"&gt;"Account &amp;gt; Public Profile Name"&lt;/span&gt; element
            &lt;span class="nf"&gt;And &lt;/span&gt;sees the &lt;span class="s"&gt;"Account &amp;gt; Public Profile Ratings"&lt;/span&gt; element
            &lt;span class="nf"&gt;And &lt;/span&gt;sees the &lt;span class="s"&gt;"Account &amp;gt; Public Profile Bought Count"&lt;/span&gt; element
            &lt;span class="err"&gt;etc...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of these conditions could be collapsed into a single step definition and replaced with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;     &lt;span class="nf"&gt;Then &lt;/span&gt;The Account page contains all expected elements
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes you may want to break the single &lt;code&gt;Given/When/Then&lt;/code&gt; block in the scenario: If a test depends on the state of a previous test and there is not an easier way to set up those conditions atomically. You could make a step definition and use it twice in two tests. However, this could greatly increase run time since you are executing a long section of behavior twice. &lt;/p&gt;

&lt;p&gt;Instead, you could do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Scrollable items can be scrolled
    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is on the &lt;span class="s"&gt;"Home"&lt;/span&gt; screen
    &lt;span class="nf"&gt;When &lt;/span&gt;The user swipes &lt;span class="s"&gt;"left"&lt;/span&gt; on the &lt;span class="s"&gt;"Home &amp;gt; Category Bar"&lt;/span&gt; section
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user does not see the &lt;span class="s"&gt;"Home &amp;gt; All Categories"&lt;/span&gt; button

    &lt;span class="nf"&gt;Given &lt;/span&gt;The previous block passed
    &lt;span class="nf"&gt;When &lt;/span&gt;The user scrolls the &lt;span class="s"&gt;"Home &amp;gt; Items Grid"&lt;/span&gt; section &lt;span class="s"&gt;"down"&lt;/span&gt;
    &lt;span class="nf"&gt;Then &lt;/span&gt;The user does not see the &lt;span class="s"&gt;"Home &amp;gt; Main Search Bar"&lt;/span&gt; element
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The step “Given The previous block passed” actually does nothing in it’s step definition code. It is just here to provide proper Gherkin by saying what is going on. This allows you to build off previous functionality, but still break things up into individual &lt;code&gt;Given/When/Then&lt;/code&gt; blocks that are atomic.&lt;/p&gt;

&lt;p&gt;The drawback is this is really multiple tests per scenario, and will be reported as such in the results. But the time savings and code reuse is worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Each step should be less than 120 characters&lt;/li&gt;
&lt;li&gt;Reading each line by itself should tell you exactly what it is doing. Use the form: subject, action, predicate&lt;/li&gt;
&lt;li&gt;Don't spell out content that is implied, such as pressing &lt;strong&gt;Submit&lt;/strong&gt; after doing something&lt;/li&gt;
&lt;li&gt;Try to not depend on exact data if it doesn't matter (ex: if you just care the list has content, look for a first item, not "item equals X" in your data)&lt;/li&gt;
&lt;li&gt;Hide data in step definitions instead of polluting the feature with it, unless it is critical to having the step text make sense&lt;/li&gt;
&lt;li&gt;Like scenario titles, if you have “and” or “or” in the text, split it into multiple steps&lt;/li&gt;
&lt;li&gt;If you have more than one or two &lt;code&gt;And&lt;/code&gt;s or &lt;code&gt;But&lt;/code&gt;s, you probably need to encapsulate them into a new keyword with a more descriptive name&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;    &lt;span class="nf"&gt;Given &lt;/span&gt;The user is logged in as &lt;span class="s"&gt;"Test Seller"&lt;/span&gt;
      &lt;span class="nf"&gt;And &lt;/span&gt;has at least one message
    &lt;span class="nf"&gt;When &lt;/span&gt;The user opens message number 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the steps are short and declarative. There is some data included, but it is needed to specify an exact object acted upon. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;And&lt;/code&gt; clause is a separate behavior and split off like this. It also is made more generic since we just care that we have a message, not what it is or any other details.&lt;/p&gt;

</description>
      <category>cucumber</category>
      <category>gherkin</category>
      <category>styleguide</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
