How to set specific settings for each running parallel thread

504 views
Skip to first unread message

Konzy

unread,
Jun 14, 2017, 11:55:12 AM6/14/17
to SpecRun
Hi there,

I'm attempting to create some selenium tests that run in parallel using SpecFlow+ and I'm finding that if a single scenario is run multiple times in parallel, and the same login credentials are used for each parallel thread, the behavior can be a bit unexpected. No doubt this is a symptom of the particular site I am testing. 

Is there anyway I can store and retrieve a specific set of login credentials to be used per thread? So for example if I was using Scenario Outline for a particular scenario and had 16 examples, with my testThreadCount set to 4, I would want each thread from the 4 to use a separate login. When the test is finished on the thread and starts another, that same login would be used. 

I'm aware that I can use tags to set targets which you can use to transform the app.config, but this seems to be at scenario level not thread level.  

Many thanks, 

Stephen McCafferty

unread,
Jun 16, 2017, 7:03:18 AM6/16/17
to SpecRun
You can transform that app.config file for different threads if you use the {TestThreadId} placeholder, which gives you the thread's integer ID.

This is typically used to ensure that the different threads all access separate database instances - preventing conflicts where one thread writes data to the database that interferes with the test being carried out in another thread. In the database connection string, you'd simply set the database instance to something like:
Instance{TestThreadId}

This means that thread 1 will connect to Instance1, thread 2 to Instance 2 etc.

If you can use an app.config transformation to achieve what you want, this should be the easiest solution if you give the accounts names like User1, User2, Admin1, Admin2 etc. Then thread 1 will only use User1/Admin1, which thread 2 will use User2/Admin2.

If that's not going to work for you, let me know. I figured that if you were already looking at config transformations as an option, that would be a good place to start.

Konzy

unread,
Jun 16, 2017, 9:27:15 AM6/16/17
to SpecRun
Thanks Stephen,

Unfortunately I don't have the knowledge to implement this.

This is my existing default.srprofile file which is very similar to the selenium grid example you have on GitHub

<?xml version="1.0" encoding="utf-8"?>
  <Settings projectName="SA.SEPA.Web.UI.Selenium" projectId="{87d3cf0c-91a7-4140-88d2-43688ee236c4}" />
  <Execution stopAfterFailures="3"
             testThreadCount="4"
             testSchedulingMode="Sequential"
             retryCount="0"
             apartmentState="MTA"  />
  <Environment testThreadIsolation="AppDomain" platform="x86"/>
  <TestAssemblyPaths>
    <TestAssemblyPath>SA.SEPA.Web.UI.Selenium.dll</TestAssemblyPath>
  </TestAssemblyPaths>
  <DeploymentTransformation>
    <Steps>
      <RelocateConfigurationFile target="CustomConfig{TestThreadId}.config"/>
      <ConfigFileTransformation configFile="App.config" >
        <Transformation>
          <![CDATA[<?xml version="1.0" encoding="utf-8"?>
                              <appSettings>
                                <add key="browser" value="{Target}" 
                                 xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />
                              </appSettings>
</configuration>
]]>
        </Transformation>
      </ConfigFileTransformation>
    </Steps>
  </DeploymentTransformation>
  <Targets>
    <Target name="IE">
      <Filter>Browser_IE</Filter>
    </Target>
    <Target name="Chrome">
      <Filter>Browser_Chrome</Filter>
    </Target>
    <Target name="Firefox">
      <Filter>Browser_Firefox</Filter>
    </Target>
  </Targets>
</TestProfile>

Without fully understanding I can try to deduce what is happening - The target name is injected into the browser field of the app.config for each customconfig on each thread. I assume it looks for tags on the scenario that match the <Filter>. 

For my issue regarding injecting data for each thread, irrespective of any tags I'm not sure whether I should be using <Targets> or <TestThreads>. 

  <TestThreads>
    <TestThread id="0">
      <TestAffinity>Username1</TestAffinity>
      <TestAffinity>Password1</TestAffinity>     
    </TestThread>
    <TestThread id="1">
      <TestAffinity>Username2</TestAffinity>
      <TestAffinity>Password2</TestAffinity>  
    </TestThread>
  </TestThreads>

If I am to use something like above how would I then reference that in a transformation of a  <add key="username" value=""/> and <add key="password" value=""/> field in my app.config?

Many thanks,

Stephen McCafferty

unread,
Jun 20, 2017, 9:36:00 AM6/20/17
to SpecRun
Having chewed this over for a bit with the team, we think this might be the easiest way to achieve what you want to do.

In your configuration file, you can define additional keys in the appSettings section. Use the {TestThreadId}placeholder to ensure that the key for the user name is different for each thread, i.e. "User{TestThreadId}" will give you User1, User2 etc. It should look something like this:
<add key="userName" value="User{testThreadId}"/>

You can then read the settings in your config file using ConfigurationManager.AppSettings. This will allow you to read in a different value for the user in each thread, based on the thread's unique ID. There is an example on the page I linked to showing how to read settings defined in the config file (see the ReadSetting(key) function in the code example).

Unfortunately, there is no way of querying the current thread ID from within your bindings. You need to use the config file transformation to be able to use different values per thread and to determine what the current thread is. If all you want is to know the thread ID itself, you can just add a key to the config file that only contains the numeric thread ID, i.e. something like:

<add key="threadID" value="{testThreadId}"/>

Querying the "threadID"
setting will then return the current thread ID. You can then obviously implement your own logic based on the thread ID if you prefer.

I hope that helps you find a solution that meets your needs.

Konzy

unread,
Jun 22, 2017, 4:23:56 AM6/22/17
to SpecRun
Hi Stephen,

Many thanks for the assistance.

Using <add key="userName" value="User{testThreadId}"/>  (or {TestThreadId}) directly in my App.config file with no other changes didn't seem to perform the substitution. It simply printed "User{testThreadId}" to the screen when I ran my test.

I did however get it to work by using...

<add key="userName" value=""/>

With the following transformation in my .srprofile file...

<add key="userName" value="User{TestThreadId}" 
xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />

You may have assumed I needed to do this as part of your proposed solution.

Matt

Stephen McCafferty

unread,
Jun 26, 2017, 7:28:56 AM6/26/17
to SpecRun
Yes, sorry, I got a bit confused writing it all up. Thanks for posting the final solution so that it's accessible to others.
Reply all
Reply to author
Forward
0 new messages