VSTS Visual Studio Set Environment Variable Test Project - .net-core

I am building Integration tests in Visual Studio (2017) for Net Core applications built on VSTS and deployed from there. My projects are test projects, and right now my connection strings to the deployed API url, and the database are hardcoded, but I want to remove them from the code and place them in a VSTS build step that adds environment variables.
Right now, my Test .cs files look something like this:
[TestClass]
public class TestFeature
{
//Set up variables
private static string _connectionString = "server=localhost;port=5432;database=databaseName;user id=postgres;password=postgres";
[TestInitialize]
public void Initialize()
{
}
//And going into my test methods
}
How do I set up my project to read Environment Variables set from the Configuration on VSTS?
What research has dug up for me so far, is to Right Click on Properties of the test project, under Debug, set up Environment Variables with a key and value there.
Then I change my test project to take the GetEnvironmentVariable()
private static string _connectionString = Environment.GetEnvironmentVariable(nameOfVariable);
On the other side in VSTS, I'm trying to find a build step that will set the variable to be the connection string there. The best step I could find is "Set Variable".
However, this is not working for me. The program will not recognize the name of the new variable set in Environment Variables, to start.

You need to add your VSTS variables in the Variables tab.
Another option is to use Powershell to set them if you want to do it in a dynamic fashion using a Powershell script task.
Write-Host "##vso[task.setvariable variable=sauce]crushed tomatoes"
https://learn.microsoft.com/en-us/vsts/build-release/concepts/definitions/release/variables?tabs=powershell

The better way is building, deploying and testing together in a build definition, after the build succeed, then the pull request can be approved.
For this way, you can use the variable in current build (can add/update variable as Marcote said)
If you must do integration test in a separate build, you can set Trigger to Manual in Build validation of Pull Request policy, then you can queue build manually and specify variables’ values (Check Settable at queue time for the variables in build definition)

Related

Yaml pipeline. How to swap connstrings in Builds with Console App

I built a console app with .Net Core 3.1. I have it building using Yaml leaning heavily on the learn.microsoft.com documentation. The release is pushing to the correct box. But I have an appsettings.json file that has a conn string variable that is different between my TEST, QA and PROD regions. I knew how to do this with the xml file transforms in .NET and MVC but I can't get this to work. Any help would be great since I don't even know the term for what I am trying to do here.
How do you change the connectionstring in the appsettings.json based on a variable or do I have to create 3 branches each with settings and create 3 build and release pipelines?
Thank you.
In order to push to different environments you usually
Have seperate release pipelines that trigger from different branches.
You have one release pipeline with different stages that need pre-approval to move to the next stage TEST -> QA -> PROD.
In both cases you will make use of Stage.
There you need to add a task named "File transformation"
In the File Format select JSON
Now, any variable found in the appsettings.json file will be replaced by the variables you set in the pipeline.
Be careful because nested variables like
{
SerilogSettings: {
BatchSize: 100
}
}
need to be set with a "." instead like
SerilogSettings.BatchSize

Override publish folder (url) of publish profile during the build pipeline

We have one ASP.Net solution with several projects. each project have build.pubxml with unique folder path.
For example:
In project Test we have this line inside the build.pubxml:
<publishUrl>C:\publish\SolutionName\Test</publishUrl>
In project Exam we have this line inside the build.pubxml:
<publishUrl>C:\publish\SolutionName\Exam</publishUrl>
In the build pipeline (in TFS) with have MSBuild step with this argument:
/p:PublishProfile=build.pubxml
After the build we got 2 folders - Test and Exam in C:\publish\SolutionName.
So far so good.
The problem is we have few branches, and we want to separate the publish folder for each branch, so we added .pubxml for each branch and in the build pipeline we specified the correct one. but is make are a lot of work on each new branch created and can cause mistakes.
We tried to pass the /p:publishUrl=C:\publish\BranchName in the MSBuild but then we got a one folder with all the content of Test and Exam and not two folders.
The idea is to have only one .pubxml file for each project with a parameter and pass the value in the pipeline, for example:
<publishUrl>C:\publish\$(Parameter)\Test</publishUrl>
And in the build we will pass the parameter according to the branch.
It is possible to do something like this?
It is possible to do something like this?
The answer is Yes. Since msbuild accepts Global Properties in command-line. If we define one Property in build.pubxml like <publishUrl>C:\PublishFolders\$(BranchID)\xxx(Test,Exam...)</publishUrl>, then we can simply pass the value in msbuild arguments like this:
Then we'll get Test and Exam folders under C:\PublishFolders\NewTest. Also we can choose to pass the pipeline predefined variables to the command like: /p:BranchID=$(Build.SourceBranch)...
This works for build in local machine, tfs and Azure Devops pipeline. Hope all above helps :)

Read environment variables in ASP.NET Core

Running my ASP.NET Core application using DNX, I was able to set environment variables from the command line and then run it like this:
set ASPNET_ENV = Production
dnx web
Using the same approach in 1.0:
set ASPNETCORE_ENVIRONMENT = Production
dotnet run
does not work - the application does not seem to be able to read environment variables.
Console.WriteLine(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
returns null
What am I missing?
Your problem is spaces around =.
This will work (attention to space before closing quote):
Console.WriteLine(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT "));
The space after ASPNETCORE_ENVIRONMENT in this code is not a typo! The problem in the question was having extra space (in SET...), so you must use the same space in GetEnvironmentVariable().
As noted by Isantipov in a comment, an even better solution is to remove the spaces entirely from the SET command:
set ASPNETCORE_ENVIRONMENT=Production
This should really be a comment to this answer by #Dmitry (but it is too long, hence I post it as a separate answer):
You wouldn't want to use 'ASPNETCORE_ENVIRONMENT ' (with a trailing space) - there are features in ASP.NET Core which depend on the value of 'ASPNETCORE_ENVIRONMENT'(no trailing space) - e.g. resolving of appsettings.Development.json vs appsettings.Production.json. (e.g. see Working with multiple environments documentation article
And also I guess if you'd like to stay purely inside ASP.NET Core paradigm, you'd want to use IHostingEnvironment.Environment(see documentation) property instead of reading from Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") directly (although the former is of course set from the latter). E.g. in Startup.cs
public class Startup
{
//<...>
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
Console.WriteLine("HostingEnvironmentName: '{0}'", env.EnvironmentName);
//<...>
}
//<...>
}
If you create the Environment variables at runtime during development then you will get null every time. You have to restart the visual studio because VS read EV at startup only.

Enterprise Library not logging in setup project

I need your opinion on this: Is it possible to use enterprise library logging dll in the setup project?
Here's what I did:
I created a setup project which will call a windows form to install the database. When I installed the project, it did call the windows form. However, when I click on the "Install" button, it seems that there's a problem and I don't know where it is. Then another popup message is displayed which said that it cannot locate the logging configuration.
But the config file for the windows form is there which includes the configuration for the logging dll. I don't have any idea where to look into.
Please help me with this?
Below is the error message:
UPDATE
I observed that when I run the exe file as is, the enterprise library logging config works. But with the setup project, it does not look for it. Any help on this?
Below is the code for this:
[RunInstaller(true)]
public partial class IPWInstaller : Installer
{
public IPWInstaller()
{
InitializeComponent();
}
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
string targetPath = Context.Parameters["TargetDir"];
InstallDatabase db = new InstallDatabase(targetPath);
DialogResult dbResult = db.ShowDialog();
if (dbResult != DialogResult.OK)
{
throw new InstallException("Database is not installed.");
}
ConfigureFiles config = new ConfigureFiles(targetPath);
DialogResult configResult = config.ShowDialog();
if (configResult != DialogResult.OK)
{
throw new InstallException("Config files are not saved correctly.");
}
}
}
LATEST UPDATE:
I tried to set the value of a certain configuration to my messagebox. This is the result of it when I run the install project.
Is there a way to call my app.config in the setup project
There are at least a couple of things that can go wrong.
The app is not running as it would if you ran it as an interactive user. It is being called from an msiexec.exe process that knows nothing about your intended environment, such as working directory. None of the automatic things that happen because you run from an explorer shell will happen. Any paths you use need to be full and explicit. I think you may need to explicitly load your settings file.
Something else that can happen in a per machine install is that custom actions run with the system account so any code which assumes you have access to databases, user profile items like folders can fail.
Another problem is that Windows Forms often don't work well when called from a VS custom action. It's not something that works very well because that environment is not the STA threading model that is required for window messages etc.
In general it's better to run these config programs after the install the first time the app starts because then you are in a normal environment, debugging and testing is straightforward, and if the db gets lost the user could run the program again to recreate it instead of uninstalling and reinstalling the setup.

How to do mocks for Web tests?

I want to write a few web tests (over WatiN/Selenium + CassiniDev web server) for my asp.net web application.
Problem I encountered is that I dont know what to do in such situations:
there is a page where user can click the button to call some third-party service. In my web test i want to create mock of this service, which will always return static value (some value in these test case and other value in other test case).
How can i do that?
Currently i use IoC/DI container Microsoft Unity. And my pages gets his dependencies in a manner described in http://msdn.microsoft.com/en-us/library/ff664622%28v=pandp.50%29.aspx.
The only solution that comes to my head is: place all dependencies in web.config for each test case and copy necessary web.config on SetUp of test. This solution completly painful!
Any ideas?
I use WatiN and Cassini-dev in my integration tests as well and have had to deal with similar issues. In my setup fixture I deploy my Asp.Net web application to a temporary folder in my test folder which allows me to play around with the configuration before starting up cassini-dev. I use Windsor for my CI which allows me to change injected components at the configuration level. You may also be able to acheive this with Unity.
If the service you are referring to is a web service you just mock out a web service using the interface you have been coding to.
Here are the steps that I take when running my integration tests:
Create a temp web directory
Publish the Asp.Net web application to the temp directory (I use MSBuild to do this)
Deploy temp database (Using MSbuild and database projects but could be done a number of ways)
Deploy temp membership database (see my blog post on how to do this in code)
Update the web.config of the deployed Asp.Net web application to point to the temp databases and change any other settings relevant for testing.
Start up the website using Cassini-Dev. I also hit the site with a http request so that I can verify the site is up before running any tests.
Run the tests.
After running the tests you should clean up.
Stop cassini-dev
Delete the temp hosting folder
Delete the temp databases. I use Sql server SMO objects that allow me to query the Sql Server which I use to delete up any old databases that have been left lying around after any previously failed test runs.
How to deploy a website using MSbuild in code
var properties = new Dictionary<string, string>
{
{"Configuration", isDebug ? "Debug" : "Release"},
{"WebProjectOutputDir", tempHostingDirectory.FullName},
{"DeployToDatabase", "true"},
{"OutDir", Path.Combine(tempHostingDirectory.FullName, "bin\\")}
};
using (var engine = new ProjectCollection(properties))
{
engine
.LoadProject(<web project path>, "4.0")
.Build(new[] {"Build", "ResolveReferences", "_CopyWebApplication"});
}
Unity configuration section usage: http://www.pnpguidance.net/Post/UnityContainerUnityConfigurationSectionAppConfigWebConfig.aspx
Generating asp.net membership database in code: http://bronumski.blogspot.com/2011/06/generating-creating-aspnet-application.html
Msbuild ProjectCollection on MSDN: http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.aspx
It sounds like you are trying to mock a web service.
Web services usually inherit from MarshalByRefObject, this means you can create a mock by inheriting from RealProxy to create a transparent proxy that pretends to be the webservice:
class Mock : RealProxy
{
public Mock()
: base(typeof(IStuff)) { }
public IStuff GetStuff()
{
return (IStuff)GetTransparentProxy();
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage message = (IMethodCallMessage)msg;
// the message object provides the MethodInfo that was called
// as well as the arguments.
// <Insert logic here>
return new ReturnMessage(new NotImplementedException("comming soon to a test near you ..."), message);
}
}
I belieave NMock2 uses RealProxy for it's mocks, so you should be able to use it to mock the web service instead.

Resources