Cake and .NET Core: we must go deeper
Small story about tricky things in building .Net Core projects using Cake: versioning, tests running, coverage calculation and overall build duration
After reading the article Cross-Platform DevOps for .NET Core by Muhammad Rehan Saeed I was excited because I had a strong desire to setup builds for my projects both via AppVeyor and Travis CI but perspective of maintaining two versions of build scripts for Windows and Linux scared me. At that moment I realized that I can write one build script for both operation systems. However, it was not the first time when my requests were slightly outside the standard scenarios. And now I want to share some experience about things that seemed important for me.
Before beginning writing my first build script on Cake I made a small list of tasks which it should solve, here it is:
- Versioning of artifacts.
- Building solution.
- Nuget packages creation.
- Running tests based on xUnit.net.
- Getting unit testing code coverage metrics via OpenCover.
As I expected problems with solution building and NuGet packages creation didn’t arise, I just copied necessary parts from the example in the read article to my
build.cake and they worked perfectly. But with the rest of items it was not so easy, I spent hours to collect them together. And now looking back I will tell a complete story about how it was.
Versioning of artifacts
Maybe at first glance it seems not important, but one day in the past I couldn’t prove that data was corrupted because of using the old program version, although all pointed to that. This happened due to the fact that all versions of the program had the same version number. I realized the error and since then I paid special attention to the versioning process.
When the program was developed for .NET Framework we used to use something like
GlobalAssemblyInfo.cs and set versions into it during the build. Unfortunately, this approach doesn’t work for programs developed for .NET Core because versions in this case are stored in project files or provided via MSBuild properties for
dotnet utility. Idea of patching all project files in solution didn’t inspire me at all, so after watching the ASP.NET Monsters #103 I found my way and after small modifications of Build and Pack steps I finally got the desired.
This version of script can build solution, create NuGet packages and set the versions provided through the environment variables.
Running tests based on xUnit.net
However building and packaging is not enough, it is also necessary to know that everything works properly and passed tests can give us some confidence. Somebody can ask why xUnit.net, what about NUnit? Answer is rather simple, I get accustomed to use xUnit.net from the time when it was the single tests framework for .NET Core which could be used with Visual Studio without any magic.
Of course, tests can be simply run by
dotnet test command(
DotNetCoreTest Cake API), but xUnit.net provides an extension for .NET CLI called dotnet-xunit(documentation is avaliable here) wich provides additional functionality, for example, reports about tests running, etc. Cake has the special API for running .NET CLI Tools called
DotNetCoreTool which did exactly that I wanted after several tries.
This version of script can build solution, create NuGet packages, set versions provided through the environment variables and run tests.
Getting unit testing code coverage metrics via OpenCover
The last step was the most difficult among others. Even knowing that all tests were passed without understanding which code was tested does not mean that the program is working correctly. Thus getting unit testing code coverage metrics is strictly necessary. However, which tool to choose for coverage calculation?
After studying about available tools I chosed a bundle of OpenCover as a tool for coverage calculation and Codecov as a tool for coverage visualization. The choice was made by me bacause of the following reasons:
- both tools in my case can be used for free(OpenCover is open source and Codecov is free for open sorce projects);
- Cake has plugins with API for both tools;
- OpenCover after performing some tricks, described here, can be used with applications developed for .NET Core.
Tricks, mentioned early, were as follows:
- debug type should be changed from
fullfor all projects unit testing code coverage for which need to be obtained;
- OpenCover should be executed with
- OpenCover can be run only on Windows.
‘Okay,’ I thought, ‘I’ll run OpenCover during builds on the AppVeyor, let’s do it!’ and wrote final version of the
This final version of script can build solution, create NuGet packages, set the versions provided through the environment variables, run tests and calculate code coverage by them. And then after some commits I saw warnings on github.
That meant that all tasks were done.
In the end, the build process with Cake looked quite normal, but I soon noticed that build on theTravis CI took much more time than on AppVeyor. ‘It isn’t normal,’ I thought and after analysis of build logs noticed, that a lot of time was spent installing Mono, used by Cake standart bootstrap script. It seemed strange for me using Mono for running Cake on Linux considering that we have .NET Core and after quick search I discoveres Cake.CoreCLR, implementation of Cake based on .NET Core. I quickly decided rewrite bootstrap scripts to use it. Unfortunately, Cake.CoreCLR is based on .NET Core v1.0.4 yet and in the bootstrap script for the Travis CI(in AppVeyor’s image “Visual Studio 2017” it has been installed already) I ought to install it, but it still took less time than installing Mono.
Now I’m looking forward to merging of the pull request [WIP] [GH1781] .NET Core 2 support to remove .NET Core v1.04 installation and make scripts fully completed.