Reputation: 1565
I don't know if this question is appropriate for this forum.
I am developing a C# ASP.NET Core webservice and a client-side library that uses this webservice. At this time, both the code for the server-side part and the client-side part live in the same Git repository and Visual Studio solution. The server-part is intended to run in Azure.
I have a combination of unit tests and integration tests, including end-to-end integration tests that test both client and server at the same time. I have written tests that can run the client either (depending on configuration) against an instance of the server in Azure (with a URI that gets injected) OR against a local in-memory server instance using Microsoft.AspNetCore.TestHost.
This works great. The in-memory server instance is very convenient when coding. Our Continuous Integration (Jenkins) server runs the integration tests against a cloud-deployed server (i.e., an instance of our ASP.NET Core webservice that has been deployed to Azure). This testing-against-the-cloud provides vital value but is too cumbersome to do while coding locally.
Here comes the problem: The server is intended to run on .NET 5. The client part needs to be able to run on both .NET Framework 4.8 and .NET 5. We want to test that a client running 4.8 can work correctly against a server running .NET 5 (i.e., an instance of our ASP.NET Core webservice that has been compiled against .NET 5).
It is of course possible to run integration tests using .NET 4.8 against a cloud-deployed server that uses .NET 5.
It is probably not possible - or at least not convenient - to run these tests locally (since the same test process would have to run both .NET 4.8 and .NET 5). But I guess we can live with that.
Of course I can build the server part against .NET 5 and deploy it to the server and run ONE integration test against that. But that doesn't solve my long-term problem. I want to be able to regularly run all my integration tests. More specifically, what I would like to achieve is:
I can achieve 1-3 by making a copy of my integration test project and letting this new project target .NET 4.8 and always run against the cloud. But that would mean maintaining two sets of tests.
Do you have any advice for how I can achieve all 4 objectives?
Thanks in advance!
EDIT 1: Regarding platforms: Our developer machines and Jenkins hosts run Windows. The cloud-deployed server also runs on Windows.
EDIT 2: Simply multi-targeting the test project (i.e., letting the test project compile against both .NET 4.8 and .NET 5) is not possible. The test project has a reference to the server part because it needs to be able to run the server locally. The server part needs .NET 5; it cannot compile against .NET 4.8 (it relies on libraries that don't exist for .NET 4.8). Hence the test project cannot compile against .NET 4.8 either.
Upvotes: 1
Views: 989
Reputation: 1565
I ended up solving it with preprocessor directives as @vernou suggested.
I have the following projects:
My integration test CSPROJ contains this:
<PropertyGroup>
<TargetFrameworks>netframework4.8;net5.0</TargetFrameworks>
</PropertyGroup>
<!-- NuGet packages that we always need -->
<ItemGroup>
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
</ItemGroup>
<!-- NuGet packages that require .NET 5 -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.3" />
</ItemGroup>
<!-- Project references that we always need -->
<ItemGroup>
<ProjectReference Include="ClientLibrary.csproj" />
</ItemGroup>
<!-- Project references that require .NET 5 -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net5.0'">
<ProjectReference Include="WebAPI.csproj" />
</ItemGroup>
My Jenkinsfile sets an environment variable to point to the Azure-deployed instance of my cloud application:
def deployed_uri = powershell (
script: "pulumi stack output EndPoint",
returnStdout: true
)
env.integrationtest_service_uri = deployed_uri
My tests do something like this:
private IService CreateService(Uri? uri)
{
if (uri is null)
{
// This is a built-in preprocessor directive: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives
#if NET5_0
_output.WriteLine("Connecting to locally hosted service");
return CreateLocallyHostedService();
#else
throw new JenkinsOnlyIntegrationTestException(
"Cannot start service because URI is null; we will assume that this test is only intended to run on Jenkins");
#endif
}
else
{
_output.WriteLine($"Connecting to service on URI: {uri}");
return CreateServiceAgainstRemoteUri(uri);
}
}
[SkippableFact(typeof(JenkinsOnlyIntegrationTestException))]
public void TestThatServiceWorks(){
var uri = GetUriOrNullFromEnvironmentVariableSetByJenkins()
var service = CreateService(uri);
// Test the service.
}
That way:
Upvotes: 1