Azure-pipelines-tasks: How to use the app.config when running Tests using Visual Studio task in build vNext

Created on 15 Sep 2015  路  19Comments  路  Source: microsoft/azure-pipelines-tasks

Hello,

In our company, we developp in C#, we have an unit test project and there is an app.config into this project to specify the database used (connectionString). Our units tests use this database to perform the tests.
When we running tests using Visual Studio task in build vNext, the app.config doesn't seem to be taken in account : we got this message "Unable to create instance of class "...". Error: System.Data.SqlClient.SqlException: CREATE DATABASE permission denied in database 'master'".

How can we use this app.config for the Visual Studio Test in build vNext ?

Bests regards,
Gilles.

Most helpful comment

Hi Gilles,

The app.config file should be present next to the built test assemblies. We tested it out with Visual Studio test task and things worked fine.

Thanks
Atin.

All 19 comments

Hi Gilles,

The app.config file should be present next to the built test assemblies. We tested it out with Visual Studio test task and things worked fine.

Thanks
Atin.

Hi Atin,

When you say next to the built test assemblies, it's in the binairies folder ? (In my case "bin\Release).
Because I got my app.config at the root folder of my project but I got the message "Unable to create instance of class "...". Error: System.Data.SqlClient.SqlException: CREATE DATABASE permission denied in database 'master'".

Thanks.

Yes, it should be in the Release folder. You can set the property on the file to always copy to the output folder. Thanks!

This doesn't seem to work when it comes to assembling binding redirects.

I have a test project that requires this in the app.config:

<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.1.37.0" newVersion="1.1.37.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

And while that works fine when running locally, it appears that these binding redirects are ignored by the Visual Studio Test task. Setting the app.config to copy to the build output folder doesn't help.

@idg10
Hi Ian,

I tried to reproduce this and found that the assembly redirection is working fine for me. Can you please give us more details on [email protected] (logs)? We will follow up from there.

Thanks

was this resolved? what's the solution?

We did not get any mail. Are you facing the same issue?

resolved.
turned out it was a nuspec issue in separate package

ok, this has come back again. Getting

System.IO.FileLoadException : Could not load file or assembly 'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

for anything that tries to create an HttpClient with a JsonMediatTypeFormatter.
System.Net.Http.Fomatting refs v6 of Netwonsoft. But my app.config does the appropriate bingingRedirect. Everything works just fine locally.

But in vsts it seems to ignore the redirects.

Any clues or debugging techniques gratefully received

this exception comes from a test project (nunit3, nunit2 also fails). It is creating a "service client" from another package (written by me) that wraps an HttpClient configured with JsonMediaTypeFormatter.

The test project (and the code it is testing) and the service client package, all have redirects to Newtonsoft.Json v7.0.1

The exception is thrown from within the ctor of the service client. No line numbers are available even though I have ref'd the symbols version of the package.

another project in the same sln works fine. There must be a difference between them. No luck finding the diff yet though

I'm running into a related issue (the title would be about the same). I have many tasks and a single connection-string file referred to at the top of the configs for each test project:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
        <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
      </configSections>
      <connectionStrings configSource="connectionStrings.config" />
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
      </startup>

Error:

System.Configuration.ConfigurationErrorsException: Unable to open configSource file '.\connectionStrings.config'. 

No matter what relative path I give it, I always get the error. It's in the same directory. I've referred to it as both "connectionStrings.config" and ".\connectionStrings.config", as well as pointing to each of the directories above it. I looked in _VSTest.ps1_ (the PS script behind the task), in order to investigate more about the flow, but it refers to _Invoke-VSTest_, which isn't seeming defined as clear-text PowerShell logic anywhere on my disk. So, I'm assuming it's defined in an assembly somewhere.

I should mention that I'm running this from a release-process. Since this task doesn't seem to indicate that it is expected to run either only under build mode or release mode, I'm assuming that this is valid.

The task is passing $workingDirectory as a parameter to _Invoke-VSTest_. This is set to the release staging directory. In this/my case, this is "D:AgentsA3_work\464b36816". I also tried putting the connection-string file there, but it didn't make a difference.

Anyone?

We're getting a similar error which I think may be related.

One of our test projects tests some code which uses ClaimsPrincipalPermissionAttribute.

When we run the tests locally they work correctly but when we run the tests on the server the test runner complains that the system.identityModel element is missing from the configuration..

Use of ClaimsPrincipalPermission attribute has been attempted and possibly there is no configuration section defined

That setting is definitely there in our test projects app.config but presumably that's not the app.config that's being used in the context of the test runner.

How can we get this setting into that context?

@B3nCr Can you try manually executing the test assemblies build generated by Build Task, using vstest console command and see if the execution passes/fails?
Vstest console exe's location would be somthing like: "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe".

I've set the app.config to copy local = true on the test project and it's miraculously fixed all our tests.

This seems like some black magic. What happens if we set more than one projects app.config to copy local = true?

@B3nCr if you set copy local=true for all dlls the app.config will be renamed to dllname.config and will be copied to binaries directory next to dll. It wont be copied as app.config and hence there wont be a conflict.

Generally, only the app.config of the _executing assembly_ will be used. Hence, a test project generally can't use the app.config of the tested project, unless you do something like mentioned above (copy the .config file into the same folder etc.)

Generally, only the app.config of the executing assembly will be used. Hence, a test project generally can't use the app.config of the tested project, unless you do something like mentioned above (copy the .config file into the same folder etc.)

I don't quite get why tested project cannot have app.config as an application does. A test project has every reason as an application has to take into account of an app.config

you can use this code in your app

`
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Text;
using System.Xml.XPath;
using System.IO;

namespace Configuration
{
///


/// Provides an testable ConfigurationManager lite based on System.Configuration.ConfigurationManager
/// using the section.SectionInformation.GetRawXml() member to get sections definition.
/// Try to store add elemnt information in a namevalue collection
///

///
/// var tcm = new TestableConfigurationManager("MyFullname_App.config");
/// var nvcDlF = tcm.GetSection("saveTheWorldSection");
/// Assert.AreEqual(12-5, saveTheWorldSection.Count);
///

public class TestableConfigurationManager
{
public IDictionary Sections = new Dictionary();

    /// <summary>
    /// Stores all existing error free processable  @keys @value pairs of add elements from all sections in a configuration file 
    /// in namevalue collection according to its section sectionname.
    /// </summary>
    /// <param name="pathToConfigFile">FullPath of a configuration file</param>
    /// <remarks>
    /// - missing propper error handling (what if you have identical keys in a configuration)
    /// - missing namespace handling
    /// - unknown whats happens with appsettings
    /// . feel free to make version 1.1
    /// </remarks>
    public TestableConfigurationManager(string pathToConfigFile)
    {
        //check
        if (!File.Exists(pathToConfigFile))
        {
            throw new ConfigurationErrorsException($"Konfigurationsdatei {pathToConfigFile} nicht vorhanden oder fuer User {Environment.UserName} nicht erreichbar.");
        }

        //read and loop all sections
        ExeConfigurationFileMap customConfig = new ExeConfigurationFileMap
        {
            ExeConfigFilename = pathToConfigFile
        };
        var eSections = ConfigurationManager.OpenMappedExeConfiguration(customConfig, ConfigurationUserLevel.None).Sections.GetEnumerator();
        while (eSections.MoveNext())
        {
            try
            {
                var nvc = new NameValueCollection();
                var section = ((ConfigurationSection)eSections.Current);
                var sectionName = section.SectionInformation.SectionName;
                //reding configuration sections xml
                var csXml = section.SectionInformation.GetRawXml();
                if (!string.IsNullOrWhiteSpace(csXml))
                {
                    var docNav = new XPathDocument(new MemoryStream(Encoding.UTF8.GetBytes(csXml)));
                    var nav = docNav.CreateNavigator();

                    //loop over all add elements in section
                    var addIterator = nav.Select(".//add");
                    while (addIterator.MoveNext())
                    {
                        //pupulate NameValueCollection assuming if there is an key you will have an value also
                        var key = addIterator.Current.SelectSingleNode("@key");
                        if (key != null)
                        {
                            var name = addIterator.Current.SelectSingleNode("@key").Value;
                            var value = addIterator.Current.SelectSingleNode("@value").Value;
                            nvc.Set(name, value);
                        }
                    }
                }
                //store namevalue collection according to sections sectionname
                Sections.Add(sectionName, nvc);
            }
            catch (Exception)
            {
                //todo errorhandling
                //throw;
            }
        }
    }

    /// <summary>
    /// Gets Section like System.Configuration.ConfigurationManager would do in an application for its app.config
    /// </summary>
    /// <param name="sectionName">Section namne of the section</param>
    /// <returns></returns>
    public NameValueCollection GetSection(string sectionName)
    {
        return ((NameValueCollection)Sections[sectionName]);
    }
}

}

`

Was this page helpful?
0 / 5 - 0 ratings