<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: tswiftma</title>
    <description>The latest articles on DEV Community by tswiftma (@tswiftma).</description>
    <link>https://dev.to/tswiftma</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%2F576667%2F5a158636-424f-4294-80d9-80b38f2bd67f.jpg</url>
      <title>DEV Community: tswiftma</title>
      <link>https://dev.to/tswiftma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tswiftma"/>
    <language>en</language>
    <item>
      <title>Trigger an automation pipeline after a Dev pipeline runs</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Wed, 21 Dec 2022 20:39:19 +0000</pubDate>
      <link>https://dev.to/tswiftma/trigger-an-automation-pipeline-after-a-dev-pipeline-runs-5eno</link>
      <guid>https://dev.to/tswiftma/trigger-an-automation-pipeline-after-a-dev-pipeline-runs-5eno</guid>
      <description>&lt;p&gt;In the perfect world you'd have multiple development environments, Dev/QA/Stage/Prod, especially if you have multiple microservices being developed concurrently. But in reality you might only have one Dev environment. So when do you run all of your automation integration tests? You might run them nightly on Dev or only on Stage after a release point. There are pros and cons to all these methods. Missing API regressions could be a very big con.&lt;/p&gt;

&lt;p&gt;Another option is to trigger your service-specific API tests in an integration pipeline right after the corresponding Azure Devops Dev pipeline for the actual service runs. This is pretty easy to do in Azure DevOps YML. In my test integration pipeline under "resources:" I added a "pipelines:" section.&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%2Fob6ep2n62gjf0fi0xmb4.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%2Fob6ep2n62gjf0fi0xmb4.png" alt="Image description" width="629" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The options "pipeline", "source", "project" and "trigger" are simple and explained in this &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/process/pipeline-triggers?view=azure-devops" rel="noopener noreferrer"&gt;Azure DevOps article&lt;/a&gt;. Basically your integration test pipeline is triggered  right after the Dev pipeline. In this example the Dev pipeline is named "pin-app-payor-job-service". Below is the trigger result in ADO Pipelines&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%2Fn3zb2i9h551hgzpndklj.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%2Fn3zb2i9h551hgzpndklj.png" alt="Image description" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My team writes failed test runs to a Teams channel so everyone can see them. I have DEV another article on how to do that &lt;a href="https://dev.to/tswiftma/write-pipeline-failures-to-a-teams-channel-23jj"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure your automation tests are scoped to the service under test. Running "Every test under the sun" is annoying and generates too much noise. We're currently using Specflow with XUnit so we tag our Scenarios according to the API that it tests. &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%2Fjngkgcxr63c92zzhhwzb.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%2Fjngkgcxr63c92zzhhwzb.png" alt="Image description" width="378" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then when we run tests in our pipeline yml with the "dotnet test" command we pass in the specific tag as a --filter Category for XUnit. &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%2F5iuixz057r1zjk703pjx.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%2F5iuixz057r1zjk703pjx.png" alt="Image description" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this article helps you run your automation at the right time!&lt;/p&gt;

</description>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Getting an Azure OAuth2 token with a Service Principal</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Wed, 16 Nov 2022 17:25:20 +0000</pubDate>
      <link>https://dev.to/tswiftma/getting-an-azure-oauth2-token-with-a-service-principal-519m</link>
      <guid>https://dev.to/tswiftma/getting-an-azure-oauth2-token-with-a-service-principal-519m</guid>
      <description>&lt;p&gt;Trying to call the Azure REST APIs requires an OAuth2 token. This seems simple right? Ha ha unfortunately it's not.&lt;/p&gt;

&lt;p&gt;There are a number of authentication methods to get a token using the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet"&gt;AzureDefaultCredential&lt;/a&gt; class. Out of these "fall through" authentication options for local development I couldn't get the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.visualstudiocredential?view=azure-dotnet"&gt;VisualStudioCredential&lt;/a&gt; option to work for me. This is the option to use your user credentials from Visual Studio. Seems like my company has some sort of difference between my local AD login and my Azure login. I was able to use the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.azureclicredential?view=azure-dotnet"&gt;Azure CLI login&lt;/a&gt; for local development by logging in via command line but that doesn't help me when the tests are running in Azure Devops. Tests running in Azure Devops can also use the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential?view=azure-dotnet"&gt;ManagedIdentityCredential&lt;/a&gt; to get a token but I hadn't gotten to that stage yet and I still needed to get an Azure OAuth2 token for local development. &lt;/p&gt;

&lt;p&gt;So I chose to try using an Azure Service Principal because it can be used when developing locally. This requires setting up an App Registration in Azure AD. This &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#register-an-application-with-azure-ad-and-create-a-service-principal"&gt;Azure article&lt;/a&gt; is very good on how to setup the App Registration. Note that setting up the App Registration and assigning the Service Principal to your Application Role requires sufficient permissions. In my case I was a Subscription Owner.&lt;/p&gt;

&lt;p&gt;Note that while setting up your App Registration you need to save the AzureSubscriptionId, ClientId, and ClientSecret(value) to use when requesting the OAuth2 token. Here's a method to get the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;string&amp;gt; GetAzureOAuthToken()
        {
            var requestUrl = AuthEndpoint + "/" + AzureSubscriptionId + "/oauth2/token";
            var client = CreateHttpClient();
            var values = new List&amp;lt;KeyValuePair&amp;lt;string, string&amp;gt;&amp;gt;();
            values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("client_id", ClientId));
            values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("client_secret", ClientSecret));
            values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("grant_type", GrantType));
            values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("resource", Resource));

            var requestBody = new FormUrlEncodedContent(values);
            var response = await client.PostAsync(requestUrl, requestBody);
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            string responseStr = await response.Content.ReadAsStringAsync();
            var content = JsonConvert.DeserializeObject&amp;lt;AzureADTokenResponse&amp;gt;(responseStr);

            return content.access_token;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other parameters needed for the above code:&lt;/p&gt;

&lt;p&gt;AuthEndpoint = "&lt;a href="https://login.microsoftonline.com"&gt;https://login.microsoftonline.com&lt;/a&gt;"&lt;br&gt;
grant_type = "client_credentials"&lt;br&gt;
resource = "&lt;a href="https://management.core.windows.net"&gt;https://management.core.windows.net&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;The object &lt;em&gt;AzureADTokenResponse&lt;/em&gt; in the code above is a C# class I created to deserialize the response from the request. Below is the code for this class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AzureADTokenResponse
    {
        public string? token_type { get; set; }

        public string? expires_in { get; set; }

        public string? ext_expires_in { get; set; }

        public string? expires_on { get; set; }

        public string? not_before { get; set; }

        public string? resource { get; set; }

        public string? access_token { get; set; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you deserialize the response without errors than grabbing the token is easy:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;return content.access_token;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok you've got your Azure OAuth2 token. Now you have to try and use it to call Azure REST APIs. I'll have another post on how to call Azure Logic App APIs soon :)&lt;/p&gt;

</description>
      <category>azure</category>
      <category>dotnet</category>
      <category>testing</category>
    </item>
    <item>
      <title>Write Pipeline Failures to a Teams Channel</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Mon, 29 Aug 2022 15:18:00 +0000</pubDate>
      <link>https://dev.to/tswiftma/write-pipeline-failures-to-a-teams-channel-23jj</link>
      <guid>https://dev.to/tswiftma/write-pipeline-failures-to-a-teams-channel-23jj</guid>
      <description>&lt;p&gt;It can be super useful to write Azure DevOps pipeline failure events to a Teams Channel. It’s a easy way for the whole development team to know when something has gone wrong.&lt;/p&gt;

&lt;p&gt;In the below example a failed automation pipeline run sends a notification to the an Automation Teams Channel that I created. The notification contains a link to the failed pipeline job in Azure DevOps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Feowqdbykmlrvkj2ii7vm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Feowqdbykmlrvkj2ii7vm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) Select or create a Teams Channel to write failures to. I recommend creating a new dedicated channel for this to reduce noise from other notifications.&lt;/p&gt;

&lt;p&gt;2) Select the Channel’s … menu then select “Connectors”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fzek7gvraw6i5954l8v6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fzek7gvraw6i5954l8v6n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3) On the Connectors page select “Incoming Webhook” then select the Configure button&lt;/p&gt;

&lt;p&gt;4) Give the Webhook a name and select the Create button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frw03sviyvxln2cql9z0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frw03sviyvxln2cql9z0d.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5) A Webhook URL will be created. Copy and save this URL then select the Done button. You will use this URL in your pipeline yml.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F685owkiac148p6l1bto3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F685owkiac148p6l1bto3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6) Add a step to your yml pipeline to write test failures to the Teams Channel. The step should run after your test execution. &lt;/p&gt;

&lt;p&gt;In the pipeline yml create a new &lt;em&gt;variable&lt;/em&gt; for your Team Channel Url using the Url value you copied above:&lt;/p&gt;

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

– name: TeamChannelUrl
value: 'https://nuancecommunications.webhook.office.com/webhookb2/2fe27450-5aff-4d80-9061-184ae51d90f2@29208c38-8fc5-4a03-89e2-9b6e8e4b388b/IncomingWebhook/6b8bfe1977214326ab198271cbf5fc93/b50aa873-d730-42b2-b5b2-26xa43abea7d' 


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

&lt;/div&gt;

&lt;p&gt;After adding the variable in the pipeline yml add a new &lt;em&gt;Powershell&lt;/em&gt; stage that writes failures to the Teams Channel. The step should be added after your automation tests (or pipeline) has been executed. &lt;/p&gt;

&lt;p&gt;Below is the &lt;em&gt;Powershell&lt;/em&gt; code you need that will grab the Azure Devops build information and send it to the Teams Channel. Update the title to reflect the automation/pipeline that was executed. In the below example I pass in the &lt;em&gt;test_environment&lt;/em&gt; and &lt;em&gt;test_category&lt;/em&gt; parameters that I am using in pipeline to further identify what failed.&lt;/p&gt;

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

– powershell: |
$body = [PSCustomObject][Ordered]@{
"@type"      = "MessageCard"
"@context"   = "http://schema.org/extensions"
"themeColor" = 'd72f00'
"title"      = "HAL Automation Pipeline Failure: Environment: ${{ parameters.test_environment }} Tests: ${{ parameters.test_category }} "
"text"       = "$(Build.DefinitionName)"
}
$body = ConvertTo-Json $body -Depth 100
$uri = "$(TeamChannelUrl)"
Invoke-RestMethod -uri $uri -Method Post -body $body -ContentType 'application/json'
displayName: Send Teams Notification
condition: failed()


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

&lt;/div&gt;

&lt;p&gt;Test your pipeline changes by running it. If you’re having trouble generating an automation or pipeline failure you can set the condition value above to &lt;em&gt;condition: always( )&lt;/em&gt;. Just remember to put it back to &lt;em&gt;condition: failed( )&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;7) Link to pipeline failures. Now on a pipeline failure a link will be generated in the new Teams Channel entry to the failed pipeline job. Anyone can click it and see the pipeline failure. Now time to start debugging!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fi1u4mayfayza6fdubd4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fi1u4mayfayza6fdubd4v.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F80doft9t6xoaonfwb709.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F80doft9t6xoaonfwb709.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>azure</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Software Engineering is a Total Trip</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Fri, 17 Jun 2022 22:10:56 +0000</pubDate>
      <link>https://dev.to/tswiftma/software-engineering-is-a-total-trip-4mjp</link>
      <guid>https://dev.to/tswiftma/software-engineering-is-a-total-trip-4mjp</guid>
      <description>&lt;p&gt;Software engineering can be really annoying at times but it is also hilarious and super intellectually challenging. I just switched Dev groups in my company. I worked on an internal Data Deletion GDPR service for almost 2.5 years. The project went nowhere and nobody actually wanted it. Requirements were vague. Devs and PMs left the project left and right. Yet the project lives on because some VP wants it to. IMHO, it is a huge waste of valuable developer time.&lt;/p&gt;

&lt;p&gt;But despite all of that I loved the daily hilarity of working on a doomed project with so many outstanding Devs on it. From a technical standpoint I wrote the most complex C# API automation code ever. For example, automating APIs that accept two totally different types of JWTs and having to update a database during tests to accelerate background processes. Great challenging stuff. Bittersweet describes my feelings leaving a team of great people and even this project. Even though being a Dev is frustrating at times it's also awesome. We are blessed to have a career that is challenging, constantly amusing, and financially rewarding.    &lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>testing</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Generate a JSON list for a request body payload in C# using Collection Initializers</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Thu, 31 Mar 2022 18:06:36 +0000</pubDate>
      <link>https://dev.to/tswiftma/generate-a-json-list-for-a-request-body-payload-in-c-using-collection-initializers-5fgd</link>
      <guid>https://dev.to/tswiftma/generate-a-json-list-for-a-request-body-payload-in-c-using-collection-initializers-5fgd</guid>
      <description>&lt;p&gt;I recently had to write a test for a new JSON REST POST API. The request body of the POST contained a formatted JSON list. Typically the body of various REST calls are JSON formatted between two brackets &lt;em&gt;{ json }&lt;/em&gt;. But in this case I needed list brackets with the JSON &lt;em&gt;[{ json }]&lt;/em&gt; for the body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbjleawyu0ihhhe3abwfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbjleawyu0ihhhe3abwfe.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's easy enough to create a C# class such that an object from it represents the JSON data between the brackets { }. In the below example the class is called SettingValue. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frpvb2qd696g458ik6ofb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frpvb2qd696g458ik6ofb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But how in C# do you create a initialized list of this JSON that can then be used in the body of the POST? You first create an object &lt;em&gt;settingValue&lt;/em&gt; of type SettingValue. This is our &lt;em&gt;{ json }&lt;/em&gt;. Then you create a List object &lt;em&gt;settingValueList&lt;/em&gt; of type SettingValue. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpnlyhtk1g414uozt2563.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpnlyhtk1g414uozt2563.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what is the &lt;em&gt;{ settingsValue }&lt;/em&gt; at the end of the List instantiation? It is a &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#:~:text=Collection%20initializers%20let%20you%20specify,expression%2C%20or%20an%20object%20initializer." rel="noopener noreferrer"&gt;Collection Initializer&lt;/a&gt; for the List with &lt;em&gt;settingValue&lt;/em&gt; as the Element Initializer. Only objects of type SettingValue can be added to the list and the list is now created with default &lt;em&gt;settingValue&lt;/em&gt; JSON. If you don't use an Element Initializer, the List will not have the default value we need. &lt;/p&gt;

&lt;p&gt;Now you can update or add to the list and pass &lt;em&gt;settingValueList&lt;/em&gt; to use as the body for your JSON POST. So your &lt;em&gt;settingValueList&lt;/em&gt; list might end up looking something like this:&lt;br&gt;
&lt;a href="https://media.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%2F5b1pmf0cuglwo00gw9y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5b1pmf0cuglwo00gw9y4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The solution seems simple to me now but for some reason in years of API automation I never have had to do it before. Previously I had only seen nested JSON Lists [ ] within JSON brackets { } and nesting lists in JSON can be done using nested C# classes, which would be the subject of another blog post :)&lt;/p&gt;

&lt;p&gt;PS: Thanks to Dan Post and Bob Geary for a technical review of the post&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>testing</category>
      <category>json</category>
      <category>csharp</category>
    </item>
    <item>
      <title>WebdriverIO Automation Protocol State of Confusion</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Thu, 30 Sep 2021 21:58:13 +0000</pubDate>
      <link>https://dev.to/tswiftma/webdriverio-automation-protocol-state-of-confusion-2ca8</link>
      <guid>https://dev.to/tswiftma/webdriverio-automation-protocol-state-of-confusion-2ca8</guid>
      <description>&lt;p&gt;I'm still somewhat of a WebdriverIO noob and I learned an important lesson this week. WebdriverIO supports two different automation protocols, Webdriver and Devtools and they can behave differently! Here's a &lt;a href="https://webdriver.io/docs/automationProtocols" rel="noopener noreferrer"&gt;link&lt;/a&gt; to the WebdriverIO doc for more details.&lt;/p&gt;

&lt;p&gt;1) Using Webdriver. &lt;a href="https://w3c.github.io/webdriver/" rel="noopener noreferrer"&gt;Webdriver&lt;/a&gt; is the older Selenium protocol and uses a middle man browser specific driver to drive the browser. It's very good for cross browser testing support. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use it in WebdriverIO:&lt;/strong&gt; If you specify to use a service that updates browser drivers in your wdio.conf.js file you are most likely using Webdriver. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Test runner services
  // Services take over a specific job you don't want to take care of. They enhance
  // your test setup with almost no effort. Unlike plugins, they don't add new
  // commands. Instead, they hook themselves up into the test process.

  services: ['chromedriver', 'edgedriver'],

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

&lt;/div&gt;



&lt;p&gt;These services are all added via NPM packages. 'Selenium-Standalone' is another driver service. &lt;/p&gt;

&lt;p&gt;2) Using Devtools: &lt;a href="https://github.com/WICG/devtools-protocol/" rel="noopener noreferrer"&gt;Devtools&lt;/a&gt; was developed with Chrome as an API to drive a browser directly without a specific driver. It also uses Puppeteer to drive the browser. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use it in WebdriverIO:&lt;/strong&gt; If you don't specify to use a service in wdio.conf.js than you will default to using Devtools. Yes this confused the heck out of me because I was using the &lt;em&gt;chromedriver&lt;/em&gt; service (aka Webdriver) for running Chrome and just specifying 'MicrosoftEdge' for a browser Capability which defaulted to using Devtools. If you look at the runtime logs you can see what WebdriverIO is using:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fotux4nf1kd97d60ndrpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fotux4nf1kd97d60ndrpw.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what protocol do you want to use?&lt;/strong&gt; Well I found out that Devtools doesn't support all WebdriverIO commands including &lt;em&gt;browser.maximizeWindow( )&lt;/em&gt;. To keep the peace on my automation team I used Webdriver for all browsers. Also Devtools might not have the best cross browser support. But it does seem to be the future of browser automation and supports some pretty advanced dev features including network inspection so it might be worth a revisit in the future now that I'm less confused :)&lt;/p&gt;

</description>
      <category>webdriverio</category>
      <category>testing</category>
      <category>chrome</category>
      <category>edge</category>
    </item>
    <item>
      <title>WebdriverIO DevTools Edge driver options</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Wed, 29 Sep 2021 18:03:06 +0000</pubDate>
      <link>https://dev.to/tswiftma/webdriverio-edge-driver-options-5038</link>
      <guid>https://dev.to/tswiftma/webdriverio-edge-driver-options-5038</guid>
      <description>&lt;p&gt;I recently needed to add Microsoft Edge browser coverage to my Webdriver.IO tests. But when I added 'edge' (or 'MicrosoftEdge') as the browserName in the wdio.config.js capabilities, a couple of problems occurred:&lt;/p&gt;

&lt;p&gt;1)  browser.maximizeWindow( ) in my test project stopped working with the following error "Error: Command "maximizeWindow" is not yet implemented". It still works for chrome however. I found that quite a few people were hitting this problem but there really wasn't a solution available. I think that the problem lies with the actual MicrosoftEdge driver itself. So as a workaround I now always start the browsers in wdio.config.js with the args option '--start-maximized'. I also stopped using the call browser.maximizeWindow() altogether.&lt;/p&gt;

&lt;p&gt;2) When the Edge browser came up it only used a defaultViewport of 1200x800 even if the browser was maximized. This was weird because my web app would only use part of the browser display. Looking at the WebdriverIO run logs showed that devtools:puppeteer was starting Edge with the option "defaultViewport":{"width":1200,"height":900}. I have no idea why it would default to this! The workaround was to set a 'wdio:devtoolsOptions' value for defaultViewport to null.&lt;/p&gt;

&lt;p&gt;Below is the code to add to wdio.conf.js. Hope this is helpful in getting Microsoft Edge to run with WebdriverIO!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    capabilities: [
      // more capabilities defined here
      // or override capabilities from wdio.conf.js      
      {
        maxInstances: 1,
        browserName: 'edge',
        'ms:edgeOptions': {
          args: ['--start-maximized']
        },
        acceptInsecureCerts: true,
        //
        // Edge launches via devtools/puppeteer.
        // Need to set defaultViewport to null to use the whole browser
        'wdio:devtoolsOptions': {
          defaultViewport: null
        }
      }
    ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdriverio</category>
      <category>testing</category>
      <category>chrome</category>
      <category>edge</category>
    </item>
    <item>
      <title>Store your automation credentials in Azure KeyVault</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Fri, 24 Sep 2021 18:47:08 +0000</pubDate>
      <link>https://dev.to/tswiftma/store-your-automation-credentials-in-azure-keyvault-4hn4</link>
      <guid>https://dev.to/tswiftma/store-your-automation-credentials-in-azure-keyvault-4hn4</guid>
      <description>&lt;p&gt;Note: updated on 4/30/24 with a more complete solution&lt;/p&gt;

&lt;p&gt;There's a common problem storing login credentials for automation that runs in Production or near-Production (aka Stage), where do you store them? You absolutely shouldn't check them into source code! I guess that you could encrypt them somehow but a better solution is to store them in Azure KeyVault and pull them out when you need them. So how can you do this? &lt;/p&gt;

&lt;p&gt;Let's consider the two locations where automation will run:&lt;/p&gt;

&lt;p&gt;1) In an IDE like Visual Studio (aka local) when developing tests.&lt;br&gt;
2) In a pipeline like Azure DevOps to run tests on a schedule.&lt;/p&gt;

&lt;p&gt;They both represent different authentication challenges. First let's tackle the local Visual Studio scenario.&lt;/p&gt;

&lt;p&gt;In Azure create a new KeyVault for your test credentials and create a couple of secrets that represent a username and password. The secrets could also represent a ClientId and ClientSecret if you're creating tokens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fh9cik4ovejzboi8c2f8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fh9cik4ovejzboi8c2f8r.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To access the KeyVault we will use the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet" rel="noopener noreferrer"&gt;DefaultAzureCredential&lt;/a&gt; class. This class provides an authentication workflow and tries different authentication methods automatically. In our case the class will use &lt;em&gt;VisualStudioCredential&lt;/em&gt; to access the KeyVault from our local machine. To enable this option you need to sign in to Visual Studio with your Azure account at Tools/Options/Azure Service Authentication/Account Selection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ff37zmuiuqqaxuegesh9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ff37zmuiuqqaxuegesh9c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get the KeyVault values you need to add the following packages to your code to support using DefaultAzureCredential:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;using Azure.Identity;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;using Azure.Security.KeyVault.Secrets;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's some sample code to get the KeyVault data. We check if our tests are running locally using a bool to get the VS edition. Then if we are running local we create a new &lt;em&gt;SecretClient&lt;/em&gt; using &lt;em&gt;DefaultAzureCredential&lt;/em&gt; and we query the KeyVault for the secrets we're looking for. &lt;/p&gt;

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

// Check if automation is running in VS or from cmd line (aka pipeline)
var isRunningInVS = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VisualStudioEdition"))

// Get KeyVault Uri and credential for local run
string kvUri = "https://myKeyVault.vault.azure.net";
var clientKV = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());

var values = new List&amp;lt;KeyValuePair&amp;lt;string, string&amp;gt;&amp;gt;();
values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("grant_type", _GrantType));                

// Get API Credentials
if (authMode == AuthMode.Candidates)
{
    if (isRunningInVS)
    {
        clientKV = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
        _ClientId = (await clientKV.GetSecretAsync(ClientIdName)).Value.Value;
        _ClientSecret = (await clientKV.GetSecretAsync(ClientSecretName)).Value.Value;
    }

    values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("client_id", _ClientId));
    values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("client_secret", _ClientSecret));
    values.Add(new KeyValuePair&amp;lt;string, string&amp;gt;("audience", _AudiencePayor));
}


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

&lt;/div&gt;

&lt;p&gt;You now can securely pull data from KeyVault when running tests locally. &lt;/p&gt;

&lt;p&gt;The second challenge is how does automation pull credential data when the same tests are running in a pipeline? In the case of using Azure DevOps you can store your credentials securely in Azure Library Variable Groups.&lt;/p&gt;

&lt;p&gt;Create a Library Variable Group specific to your environment. The below example shows one for Development. You can then add your credential values and mask out any critical values such as ClientSecret:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6locrffgsmbd4l1iz3ak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6locrffgsmbd4l1iz3ak.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that Library Variable Groups do have an option to directly link to your KeyVault but at this time I didn't have permissions to get it to work correctly.&lt;/p&gt;

&lt;p&gt;Next is to access the Variable Group values in your pipeline yaml. Under the &lt;em&gt;variables&lt;/em&gt; section, specify the &lt;em&gt;group&lt;/em&gt; value as the name of your ADO Library Variable Group. When running the pipeline it will have access to the data in the variable group. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0tnepq8xx6a639f7x1tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0tnepq8xx6a639f7x1tb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note in the above example &lt;em&gt;$(AuthorizationClientId)&lt;/em&gt; is the value that is pulled from the variable group and assigned to the yaml variable named &lt;em&gt;Authorization.ClientId"&lt;/em&gt;. You now have secure access to your credentials in your pipeline.&lt;/p&gt;

&lt;p&gt;There's a few other pipeline steps that I won't go over in detail but here's a few tips:&lt;/p&gt;

&lt;p&gt;Tip1: All of your variables for your automation should be stored in an appSettings.json file that is typically read during startup using the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationbuilder?view=net-8.0&amp;amp;viewFallbackFrom=dotnet-plat-ext-8.0" rel="noopener noreferrer"&gt;ConfigurationBuilder&lt;/a&gt; class. Remember not to store credentials in these files!&lt;/p&gt;

&lt;p&gt;Tip2: At pipeline runtime you can substitute variables like we just created from the Library Variable Groups above using the &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/file-transform-v1?view=azure-pipelines" rel="noopener noreferrer"&gt;FileTransform@1&lt;/a&gt; task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fqgvh9jqyv8sh5ep7yreb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fqgvh9jqyv8sh5ep7yreb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good luck and let me know if you have any questions!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>testing</category>
      <category>keyvault</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Help I need to choose a UI Automation tool!</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Thu, 08 Apr 2021 19:09:07 +0000</pubDate>
      <link>https://dev.to/tswiftma/help-i-need-to-choose-a-ui-automation-tool-ngi</link>
      <guid>https://dev.to/tswiftma/help-i-need-to-choose-a-ui-automation-tool-ngi</guid>
      <description>&lt;p&gt;Our team has the opportunity to choose a new UI Automation tool for a new web front end. We're considering Python/Selenium, WebdriverIO, Test Café or Cypress. Here's what we need to support for technology:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user interface is developed on Node.js with React.&lt;/li&gt;
&lt;li&gt;Need some support for iframes automation as some legacy apps will be plugged in.&lt;/li&gt;
&lt;li&gt;Need the ability to pass in test script parameters including &lt;strong&gt;Host Header support&lt;/strong&gt; for K8S and so we can run tests in CI/CD pipelines.&lt;/li&gt;
&lt;li&gt;An open source tool with an active user community would be nice! We might be able to convince the company to pay for a tool that is totally awesome :)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We've done some basic automation POCs (Proof of Concepts) with Python/Selenium and WebdriverIO. Our automation people have some good coding skills in Java/C# but more limited skills in Python/Javascript. &lt;/p&gt;

&lt;p&gt;What do you think? Thanks!&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>selenium</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using cmd switches with pytest to share data between tests</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Thu, 25 Mar 2021 15:52:29 +0000</pubDate>
      <link>https://dev.to/tswiftma/using-cmd-switches-with-pytest-to-share-data-between-tests-3mj0</link>
      <guid>https://dev.to/tswiftma/using-cmd-switches-with-pytest-to-share-data-between-tests-3mj0</guid>
      <description>&lt;p&gt;I'm kind of new to using Python Selenium with pytest and I needed to pass in come custom cmd line options when running tests with pytest. Specifically I needed the ability to pass in a custom url, hostheader, email and password value to my tests so they could run in different environments. I would think that everyone needs this for CI/CD tests today!&lt;/p&gt;

&lt;p&gt;After lots of Googling for solutions I found that the suggestions on how to use switches were all over the place! I finally figured out to use the &lt;em&gt;conftest.py&lt;/em&gt; file with the &lt;em&gt;pytest_addoption&lt;/em&gt; method. Now there seems to be a lot of discussion on where to place the conftest.py file. I placed it above my \Tests folder, I haven't figured out the other complexities of it yet. Here's the content of conftest.py:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X98IMmwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvta7z0lksbenix5908y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X98IMmwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvta7z0lksbenix5908y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that in the &lt;em&gt;pytest_addoption(parser)&lt;/em&gt; function I set default values for the switches that are for my most common test environment. The &lt;em&gt;params(request):&lt;/em&gt; function is the key to returning an array of the params to the tests.&lt;/p&gt;

&lt;p&gt;So how do you access the data in your tests? Really easy! Just pass &lt;em&gt;params&lt;/em&gt; into your test functions and reference the &lt;em&gt;params&lt;/em&gt; using &lt;em&gt;params['MyParam']&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ANpJ4vjY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kmexheinm2ep6267faw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ANpJ4vjY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kmexheinm2ep6267faw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the tests: &lt;/p&gt;

&lt;p&gt;I can run my tests using only the &lt;em&gt;pytest&lt;/em&gt; command because it has default values for the command line switches or I can substitute values by adding the actual switches themselves. Now you can run the tests anywhere in your CI/CD environment by substituting environment vars for the command line switches :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RxgioNg4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/133z0qfutl994fie9a2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RxgioNg4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/133z0qfutl994fie9a2w.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>pytest</category>
      <category>selenium</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why I'm switching from C# to Python with Selenium</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Thu, 18 Mar 2021 15:17:51 +0000</pubDate>
      <link>https://dev.to/tswiftma/why-i-m-switching-from-c-to-python-with-selenium-2oap</link>
      <guid>https://dev.to/tswiftma/why-i-m-switching-from-c-to-python-with-selenium-2oap</guid>
      <description>&lt;p&gt;I was recently tasked with automating our product's new login wrapper. This involved hitting the server UI page, redirecting to a login page and validating if the login succeeds or fails. Another requirement was to have the ability to run the tests in Azure DevOps pipelines.&lt;/p&gt;

&lt;p&gt;I'm typically not a UI automation guy, I usually focus on Rest APIs and code in C#. So I started with Selenium C#. While it was fairly easy to automate the login steps I was immediately blocked when I found out that I need to support host headers to run the tests in a Kubernetes (K8S) environment. &lt;a href="https://github.com/SeleniumHQ/selenium/issues/7131"&gt;Well Selenium out of the box doesn't support host headers&lt;/a&gt;...&lt;/p&gt;

&lt;p&gt;I stared searching for possible solutions and the best one I found by far was &lt;a href="https://pypi.org/project/selenium-wire/"&gt;Seleniumwire&lt;/a&gt;. Seleniumwire supports access to underlying requests/responses. You can modify them and get http response codes as well. I felt like I was back in my API testing world! The only difference was that Seleniumwire only supports PYTHON and not C#. But I had to have the Seleniumwire functionality even if my Python skills were somewhat noob-ish. &lt;/p&gt;

&lt;p&gt;Lets look how easy it is to set a host header with Seleniumwire&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pytest
from seleniumwire import webdriver

def test_isAlive(url, hostheader):
    web_driver = webdriver.Chrome()
    web_driver.maximize_window()
    # check to set host header    
    if hostheader != 'none':
            web_driver.header_overrides = {'Host' : hostheader}

    isAliveUrl = '/api/isAlive'
    UIEndpoint = url
    loginURL = UIEndpoint + isAliveUrl
    web_driver.get(loginURL)

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

&lt;/div&gt;



&lt;p&gt;Now the above isn't the whole script but you get the picture. This solved a huge issue that wasn't possible with Selenium C#. Note that I check for a host header value of "none" if I don't want to use a host header. &lt;/p&gt;

&lt;p&gt;Another cool Seleniumwire feature is to grab an http response code which I can then use in an assert to check that the page loaded. This can be an additional test to the builtin Selenium page load response check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Access requests via the `requests` attribute
    for request in web_driver.requests:
        if request.response:
            print(
                request.url,
                request.response.status_code,
                request.headers                
            )    

    assert request.response.status_code == 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These Seleniumwire features might seem minor but they are actually huge helps in UI Automation. Selenium should get off of it's high  "we need to act like a browser" horse and incorporate this library in all versions! Until they do I'm now a Python automation guy :)&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>python</category>
      <category>csharp</category>
      <category>testing</category>
    </item>
    <item>
      <title>Use a host header value for testing services on Kubernetes</title>
      <dc:creator>tswiftma</dc:creator>
      <pubDate>Tue, 02 Mar 2021 15:53:55 +0000</pubDate>
      <link>https://dev.to/tswiftma/use-a-host-header-value-for-testing-services-on-kubernetes-4fbd</link>
      <guid>https://dev.to/tswiftma/use-a-host-header-value-for-testing-services-on-kubernetes-4fbd</guid>
      <description>&lt;p&gt;What is a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host"&gt;Host Header&lt;/a&gt;? Basically it's an address that the web server (or ingress service) will send a request to.   &lt;/p&gt;

&lt;p&gt;At my company we are in the process of moving some of our services from Azure App Services to AKS. To run my C# API Integration tests against the Kubernetes pods our DevOps guys told me that I needed to use host header values to hit the internal K8S endpoints of the services. &lt;/p&gt;

&lt;p&gt;This was fairly easy to accomplish in C# as I just added a default parameter for the host header to my Post method (as well as my other REST methods).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Helper method to add headers for PostAsync operation with no data.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name="httpClient"&amp;gt;HttpClient.&amp;lt;/param&amp;gt;
/// &amp;lt;param name="url"&amp;gt;url.&amp;lt;/param&amp;gt;
/// &amp;lt;param name="hostHeader"&amp;gt;Host header.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;HttpResponseMessage.&amp;lt;/returns&amp;gt;
public static Task&amp;lt;HttpResponseMessage&amp;gt; PostAsJsonAsync(this HttpClient httpClient, string url, string hostHeader = "none")
{
    var stringContent = new StringContent(string.Empty);
    stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
    var dataAsString = stringContent.ToString();

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url)
    {
      Content = new StringContent(dataAsString, Encoding.UTF8, "application/json"),
    };

    if (hostHeader != "none")
    {
        // Host name for K8S tests
        httpClient.DefaultRequestHeaders.Host = hostHeader;
    }

    return httpClient.PostAsync(url, stringContent);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default the Host Header value is "none" for normal automation runs. When the tests are run against the AKS deployment we pass the host header variable value into the K8S yaml pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- stage: Test_Online
    pool:
      name: 'linux-agents'
    displayName: Test Online
    condition: and(succeeded(),eq(${{parameters.switch}}, true))
    variables:
      - ${{ if eq(parameters.envtype, 'prod') }}:
        - name: MyApiHostHeader
          value: ${{parameters.deployenv}}-${{parameters.service}}-online.${{parameters.region}}.${{parameters.deployenv}}.test.com
      - ${{ if eq(parameters.envtype, 'nonprod') }}:
        - ${{ if eq(parameters.deployenv, 'dev') }}:
          - name: MyApiHostHeader
            value: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my Visual Studio solution for the API automation I'm reading the hostHeader value from appSettings.json. In the K8S yaml pipeline we have to do a bunch of steps to run the job, including a FileTransform task to substitute the HostHeader variable we added earlier into the solution for the tests to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
      - job: Test_Offline
        steps:
        - ${{ if eq(parameters.skipTests, false) }}:
          - checkout: automation
            persistCredentials: true

          - script: |
              apt-get update
              apt-get install mono-complete
            displayName: Install Dotnet prerequisites

          - task: UseDotNet@2
            displayName: 'Use .NET Core'
            inputs:
              version: $(dotnet_core_version)
          - task: NuGetToolInstaller@1
            displayName: 'Use NuGet'
            inputs:
              versionSpec: $(nuget_version)
          - task: NuGetCommand@2
            displayName: 'NuGet restore'
            inputs:          
              restoreSolution: '**\MyAPITests.sln'

          #this task performs envvar substitution. we need to set the vars to override in lib var group or 
          #the env: block does NOT seem to work for passing in variable to task. we set these in global variables above
          - task: FileTransform@1
            inputs:      
              folderPath: '$(System.DefaultWorkingDirectory)'
              fileType: json
              targetFiles: '**/**/appSettings.json'

          - script: |
              cat MyAPITests/MyAPITests/appSettings.json
            displayName: show transform

          - task: DotNetCoreCLI@1
            displayName: 'Build solution: $(solution)'
            inputs:
              command: 'build'
              projects: |
                MyAPITests/$(solution)

          - task: DotNetCoreCLI@1
            displayName: 'Run API tests in solution: $(solution)'
            inputs:
              command: test
              projects: |
                HAPAPITests/**/*Tests/*.csproj
              arguments: '-c release -v d --filter Category=${{ parameters.testCategory }} --logger "trx;verbosity=detailed;LogFileName=DotNetApiAutomationTestResults.trx"'

          - task: PublishTestResults@2
            displayName: 'Publish Offline Test Results'
            inputs:
              searchFolder: MyAPITests
              testResultsFormat: 'VSTest'
              testResultsFiles: '**/*.trx'
              testRunTitle: '${{ parameters.service }} - Offline ApiAutomation ${{ parameters.deployenv }}'
              publishRunAttachments: true
              failTaskOnFailedTests: false
            condition: succeededOrFailed()

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

&lt;/div&gt;



&lt;p&gt;There's a lot of moving parts here with VS, Azure DevOps pipelines and AKS but we got it working!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>testing</category>
      <category>azure</category>
      <category>azuredevops</category>
    </item>
  </channel>
</rss>
