C# integration testing in AzureDevOps with Docker containers– SqlServer and Cachet example
Every software that we make depends on others. For Stankins , as a general ETL data, it is more important to be tested with real data providers.For example, we may want to take data from Sql Server and send to Cachet . How can we have a SqlServer and a Cachet up and running easy ? The obvious answer our days is Docker.
Let’s see how a test for SqlServer looks
using FluentAssertions; using Stankins.Alive; using Stankins.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xbehave; using Xunit; namespace StankinsTestXUnit { [Trait("ReceiverSqlServer", "")] [Trait("ExternalDependency","SqlServer")] public class TestReceiverSqlServer { [Scenario] [Example("Server=(local);Database=master;User Id=SA;Password = <YourStrong!Passw0rd>;")] public void TestReceiverDBServer(string connectionString) { IReceive status = null; IDataToSent data = null; $"Assume Sql Server instance {connectionString} exists , if not see docker folder".w(() => { }); $"When I create the ReceiverDBServer ".w(() => status = new ReceiverDBSqlServer(connectionString)); $"and receive data".w(async () => { data = await status.TransformData(null); }); $"the data should have a table".w(() => { data.DataToBeSentFurther.Count.Should().Be(1); }); $"and the result should be true".w(() => { data.DataToBeSentFurther[0].Rows[0]["IsSuccess"].Should().Be(true); }); } } }
and for cachet :
using FluentAssertions; using Stankins.FileOps; using Stankins.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using Stankins.Rest; using Xbehave; using Xunit; using static System.Environment; using Stankins.Trello; using Stankins.Cachet; namespace StankinsTestXUnit { [Trait("Cachet", "")] [Trait("ExternalDependency", "Cachet")] public class TestSenderCachet { [Scenario] [Example("Assets/JSON/CachetV1Simple.txt", 3)] public void TestSimpleJSON(string fileName,int NumberRows) { IReceive receiver = null; IDataToSent data=null; var nl = Environment.NewLine; $"Given the file {fileName}".w(() => { File.Exists(fileName).Should().BeTrue(); }); $"When I create the {nameof(ReceiveRest)} for the {fileName}".w(() => receiver = new ReceiveRestFromFile(fileName)); $"And I read the data".w(async () =>data= await receiver.TransformData(null)); $"Then should be a data".w(() => data.Should().NotBeNull()); $"With a table".w(() => { data.DataToBeSentFurther.Should().NotBeNull(); data.DataToBeSentFurther.Count.Should().Be(1); }); $"The number of rows should be {NumberRows}".w(() => data.DataToBeSentFurther[0].Rows.Count.Should().Be(NumberRows)); $"and now I transform with {nameof(SenderCachet)}".w(async ()=> data=await new SenderCachet("http://localhost:8000","5DiHQgKbsJqck4TWhMVO").TransformData(data) ); } } }
( I have use XBehave for extensions)
Nice and easy , right ? Not so!
For up and running SqlServer I have used a docker compose file
version: '3'
services:
db:
image: mcr.microsoft.com/mssql/server
ports:
- "1433:1433"
environment:
SA_PASSWORD: "<YourStrong!Passw0rd>"
ACCEPT_EULA: "Y"
healthcheck:
test: sqlcmd -S (local) -U SA -P '<YourStrong!Passw0rd>' -Q 'select 1'
and in AzureDevOps yaml start the containers, run the tests, collect the code coverage, stop the containers
docker-compose -f stankinsv2/solution/StankinsV2/StankinsTestXUnit/Docker/docker-sqlserver-instance-linux.yaml up -d
echo 'start regular test'
dotnet build -c $(buildConfiguration) stankinsv2/solution/StankinsV2/StankinsV2.sln
dotnet test stankinsv2/solution/StankinsV2/StankinsTestXUnit/StankinsTestXUnit.csproj --logger trx --logger "console;verbosity=normal" --collect "Code coverage"
echo 'coverlet'
coverlet stankinsv2/solution/StankinsV2/StankinsTestXUnit/bin/$(buildConfiguration)/netcoreapp2.2/StankinsTestXUnit.dll --target "dotnet" --targetargs "test stankinsv2/solution/StankinsV2/StankinsTestXUnit/StankinsTestXUnit.csproj --configuration $(buildConfiguration) --no-build" --format opencover --exclude "[xunit*]*"
echo 'compose down'
docker-compose -f stankinsv2/solution/StankinsV2/StankinsTestXUnit/Docker/docker-sqlserver-instance-linux.yaml down
Easy, right ? That’s because SqlServer is well behaved and has a fully functional image on Docker
That is not so easy with Cachet . Cachet requires configuration – and more, after configuration, it generates a random token for write data ( http://localhost:8000","5DiHQgKbsJqck4TWhMVO ) .
So it will be a task for docker to export the container and import again - easy stuff, right ? Again, not.
So I start a small docker container with
docker run -p 8000:8000 –name myCachetContainer -e APP_KEY=base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc= -e DEBUG=false -e DB_DRIVER=sqlite cachethq/docker
and then browsing to http://localhost:8000 I have configured and grab the token
Now it is time to export :
docker export myCachetContainer -o cachet.tar
And to import as an image
docker import cachet.tar mycac
And to run the image again
docker run -p 8000:8000 -e APP_KEY=base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc= -e DEBUG=false -e DB_DRIVER=sqlite cachethq/docker
And the image stopped! After many tries and docker inspect the initial image , I have resulted to
docker run -it -p 8000:8000 -e APP_KEY=base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc= -e DEBUG=false -e DB_DRIVER=sqlite --workdir /var/www/html --user 1001:1001 mycac "/sbin/entrypoint.sh"
So the workdir, user, and the entry point are not copied into the image and you should do youurself.
The final preparing for CI with Docker for Cachet ? I have docker push myimage to Docker Hub , and I will run it from docker compose.
So now my docker compose with sql server and cachet looks this way
version: '3'
services:
db:
image: mcr.microsoft.com/mssql/server
ports:
- "1433:1433"
environment:
SA_PASSWORD: "<YourStrong!Passw0rd>"
ACCEPT_EULA: "Y"
healthcheck:
test: sqlcmd -S (local) -U SA -P '<YourStrong!Passw0rd>' -Q 'select 1'cachet:
image: ignatandrei/ci_cachet
ports:
- "8000:8000"
environment:
APP_KEY: "base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc="
DEBUG: "false"
DB_DRIVER: "sqlite"
user: "1001"
working_dir: "/var/www/html"
entrypoint: "/sbin/entrypoint.sh"
And I have a nice C# integration tests with Azure Devops, Docker, Sql Server and Cachet ! You can see the code coverage report at https://codecov.io/gh/ignatandrei/stankins/src/master/stankinsv2/solution/StankinsV2/Stankins.Cachet/SenderCachet.cs