How to set up CodeBuild test reports in CDK Pipelines (C#)
Tags: aws, cdk, devops
Published: 2021-09-01 11:52:54

I'm so happy to get into writing again - we’ve had a few challenging months: we had to self-isolate several times, the whole family was ill with a stomach bug, and our son is going through the terrible twos. So blogging, talks and working on professional development had to be put on the backburner.

I finally had some time to finish writing this blog post about CDK Pipelines I had been working on probably since the beginning of the year. I had been trying to figure out how to make CodeBuild test reports work with CDK Pipelines. Last week when I got back to this and started working on it again, I saw that the API that was used in Developer Preview has been updated (more information on it [here](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/pipelines/ORIGINAL_API.md)). And now it looks like it is easier to plug in the reports to be used with this high level construct. While the old API is still in use, I will focus on the new API.

The purpose of this blog post is to demonstrate the set-up of CodeBuild test reports in CDK Pipelines for C#.

I have written a simple .NET Core application which returns the day of the week when you pass in a date in the query string. There are also a couple of XUnit tests:

```
public class UnitTests
{
    [Fact]
    public void DateInPast_ReturnsCorrectResult()
    {
        var controller = new HomeController();
        var date = new DateTime(1983, 2, 3);
        var expected = $"{String.Format("{0:d}", date)} was Thursday";
        var actual = controller.Get(date) as OkObjectResult;
        Assert.Equal(expected, actual.Value);
    }

    [Fact]
    public void DateInFuture_ReturnsCorrectResult()
    {
        var controller = new HomeController();
        var date = new DateTime(2033, 12, 9);
        var expected = $"{String.Format("{0:d}", date)} will be Friday";            
        var actual = controller.Get(date) as OkObjectResult;
        Assert.Equal(expected, actual.Value);
    }
}
```

The file tree looks like this:
![file tree](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nn4gnjtmrxmgwoxyuol0.JPG)
![file tree](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k32fvyz5h6nl5rllm94u.JPG)

For the task of creating CodeBuild test reports only without actually deploying the app, we will only work with *CdkPipelinesPipelineStack.cs*. In my case this was the file created automatically on `cdk init`, and it will contain the main pipeline.

Firstly, before we build the pipeline, we need to create a connection to our Github repo and get its ARN. I wrote a post about it a while back – [AWS CDK Adventure Part 2: CDK Pipelines and GitHub fun](https://oxiehorlock.com/2021/03/15/cdk-pipelines-and-github-fun/)

```
namespace CdkPipelines
{
    public class CdkPipelinesPipelineStack : Stack
    {
        internal CdkPipelinesPipelineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var connectionArn = "arn:aws:codestar-connections:eu-west-1:01234567890:connection/12ae43b8-923e-4a01-ba4e-274454669859";
        }
    }
}
```

We then create a report group:
```
var reportGroup = new ReportGroup(this, "MyReports", new ReportGroupProps
{
    ReportGroupName = "MyReports"
});
```

After that, we use the *CodePipeline* construct in the *Amazon.CDK.Pipelines* namespace to create the pipeline. If we didn’t want to have any CodeBuild reports, we would set up the pipeline like so:
```
var pipeline = new Amazon.CDK.Pipelines.CodePipeline(this, "WhatDayOfWeekPipeline", new CodePipelineProps
{
    PipelineName = "WhatDayOfWeekPipeline",
    SelfMutation = false,
    Synth = new ShellStep("synth", new ShellStepProps()
    {
        Input = CodePipelineSource.Connection("OksanaH/CDKPipelines", "main", new ConnectionSourceOptions()
        {
            ConnectionArn = connectionArn
        }),
        InstallCommands = new string[] { "npm install -g aws-cdk" },
        Commands = new string[] { 
            "cd App", "dotnet restore WhatDayOfWeekTests/WhatDayOfWeekTests.csproj",
            "dotnet test -c release WhatDayOfWeekTests/WhatDayOfWeekTests.csproj --logger trx --results-directory ./testresults",
            "cd ..", 
            "cdk synth" }
    })
});
```
One of the *CodePipelineProps* is *SelfMutation*: when set to false, it’s quite handy when doing development work – you can just run `cdk deploy` and your local changes to the pipeline will be deployed bypassing the GitHub repo.

*Synth* property is used to set up the pipeline to pull from the GitHub repo, and also run the commands needed to produce the cloud assembly.

In order to set up the reports, we need to customize the CodeBuild project, and it can be done by using *CodeBuildStep* class instead of *ShellStep*. *CodeBuildStepProps* class, in turn, has a *PartialBuildSpec* property, which we can use to define the reports. The reports part of a *buildspec.yml* file usually looks like this:
```
version: 0.2
phases:
  ...

reports:
  XUnitTestResults:
    file-format: VisualStudioTrx
    files:
      - '**/*'
    base-directory: './testresults'
```

In CDK for C# the value of *PartialBuildSpec* has to be created using *Dictionary<string, object>*, and the reports bit translated to CDK is below:
```
var reports = new Dictionary<string, object>()
{                
    {
        "reports", new Dictionary<string, object>()
        {
            {
                reportGroup.ReportGroupArn, new Dictionary<string,object>()
                {
                    { "file-format", "VisualStudioTrx" },
                    { "files", "**/*" },
                    { "base-directory", "App/testresults" }
                }
            }
        }
    }
};
```

Another thing that needs to be created to be able to work with CodeBuild test reports is a policy, otherwise you might see an error like this when you try to deploy the stack:
![auth error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wle1t074486fd5qt1tnh.png)

The policy allows several report-related actions on the report group we have created:
```
var policyProps = new PolicyStatementProps()
{
    Actions = new string[] { 
        "codebuild:CreateReportGroup",
        "codebuild:CreateReport",
        "codebuild:UpdateReport",
        "codebuild:BatchPutTestCases",
        "codebuild:BatchPutCodeCoverages" 
    },
    Effect = Effect.ALLOW,
    Resources = new string[] { reportGroup.ReportGroupArn }
};
```
Next, we can define necessary *CodeBuildStepProps* to set up reports:
```
var step = new CodeBuildStep("Synth", new CodeBuildStepProps
{
    Input = CodePipelineSource.Connection("OksanaH/CDKPipelines", "main", new ConnectionSourceOptions()
    {
        ConnectionArn = connectionArn
    }),
    PrimaryOutputDirectory = "cdk.out",
    InstallCommands = new string[] { "npm install -g aws-cdk" },
    Commands = new string[] { 
        "cd App", 
        "dotnet restore WhatDayOfWeekTests/WhatDayOfWeekTests.csproj",                        
        "dotnet test -c release WhatDayOfWeekTests/WhatDayOfWeekTests.csproj --logger trx --results-directory ./testresults",
        "cd ..",
        "cdk synth"  
    },
    PartialBuildSpec = BuildSpec.FromObject(reports),
    RolePolicyStatements = new PolicyStatement[] { new PolicyStatement(policyProps) }
});
```

Now, what is left to do is to use the *CodeBuildStep* as the value of the *Synth* property:
```
var pipeline = new Amazon.CDK.Pipelines.CodePipeline(this, "WhatDayOfWeekPipeline", new CodePipelineProps
{
    PipelineName = "WhatDayOfWeekPipeline",
    Synth = step
}
```
After that we can commit the changes, run `cdk deploy` and check the CodeBuild test report in the console:
![codebuild test reports](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmx98edcm5tsm3ar4ke6.JPG)
Beautiful!

Useful links:
* [AWS Documentation on CDK Pipelines](https://docs.aws.amazon.com/cdk/latest/guide/cdk_pipeline.html)

* [CDK Workshop](https://cdkworkshop.com/40-dotnet/70-advanced-topics/100-pipelines.html)

* [CDK Pipelines in GitHub](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/pipelines)