Friday, July 19, 2024

Running .NET Core Tests and collecting coverage with Azure Pipelines

Installing Coverlet

Coverlet is an open source project that produces code coverage reports.

In Visual Studio install the coverlet.collector and coverlet.msbuild NuGet package.

nuget install coverlet.collector

nuget install coverlet.msbuild

Running Coverlet Locally

dotnet test /p:CollectCoverage=true

Viewing Coverage in Visual Studio

This Visual Studio 2022 extension allows you to view coverage line by line and as a summary

https://marketplace.visualstudio.com/items?itemName=FortuneNgwenya.FineCodeCoverage

Defining the Azure Pipeline

Using the wizard follow the steps below.

Use the .NET Core SDK

Select the .NET Core SDK appropriate for your project.



Perform a .NET Restore

A restore will download the Nuget packages for your project.



Perform a .NET Build



Run the Tests

Ensure to define the arguments to collect coverage



Publish the Code Coverage

Task version 1 will be deprecated. See this post from Microsoft:

https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/publish-code-coverage-results-v2?view=azure-pipelines




Tuesday, July 16, 2024

Running Jasmine Tests Using Karma and Azure Pipelines

In order to run Jasmine tests, the runner Karma must be used.  Karma requires Chrome.  So in Azure we will be installing all of that in a container in order to run the tests.  We will also be collecting coverage.  It is important that a full Linux distribution be used like Ubuntu.  We will be using the Azure pipeline wizard.

Install Jasmine

This will add Jasmine to your package.json

npm install --save-dev jasmine-core @types/jasmine @types/jasminewd2


Install Karma

This will add Karma to your package.json

npm install --save-dev karma karma-chrome-launcher karma-coverage karma-jasmine
karma-jasmine-html-reporter

Add a karma.conf.js to your project

Generating detailed code coverage reports can help you identify untested parts of your codebase. Add the coverageReporter to your karma.config.js

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: "",
    frameworks: ["jasmine", "@angular-devkit/build-angular"],
    plugins: [
      require("karma-jasmine"),
      require("karma-chrome-launcher"),
      require("karma-jasmine-html-reporter"),
      require("karma-coverage"),
      require("@angular-devkit/build-angular/plugins/karma"),
    ],
    client: {
      jasmine: {
        // you can add configuration options for Jasmine here
        // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
        // for example, you can disable the random execution with `random: false`
        // or set a specific seed with `seed: 4321`
      },
      clearContext: false, // leave Jasmine Spec Runner output visible in browser
    },
    jasmineHtmlReporter: {
      suppressAll: true, // removes the duplicated traces
    },
    coverageReporter: {
      dir: require("path").join(__dirname, "./coverage/angularapp"),
      subdir: ".",
      reporters: [{ type: "html" }, { type: "text-summary" }],
    },
    reporters: ["progress", "kjhtml", "coverage"],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ["Chrome", "ChromeHeadless"],
    singleRun: false,
    restartOnFileChange: true,
  });
};


Add test and test-headless to your package.json

  "scripts": {
    "test": "ng test",
    "test-headless": "ng test --code-coverage --watch=false --browsers=ChromeHeadless",
  },

In Azure Define the Pipeline



Specify the Source Repository



Using Bash to Install Chrome




Here are the commands
sudo apt-get update
sudo apt-get install -y wget
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt-get install -y ./google-chrome-stable_current_amd64.deb
google-chrome --version

Use Node

Use the appropriate version of node for your project.





Use NPM to install the Angular CLI




Use NPM to install the node packages





Use NPM to run the build




Use NPM to run the tests








Friday, February 9, 2024

Cleanup Local Git Branches

 Below is a Powershell script that will remove local Git branches except for a list of excluded branches.


# Usage:  .\RemoveGitBranches.ps1 -directoryPath "C:\path\to\your\repository"
param (
    [string]$directoryPath
)

# Define the branches to keep
$branchesToKeep = @("dev", "develop", "development", "main", "master", "qa", "test", "stage")

# Change to the specified directory
Set-Location -Path $directoryPath

# Get a list of all local branches
$branches = git branch --format "%(refname:short)"

foreach ($branch in $branches) {
    $branch = $branch.Trim()

    # Check if the branch is not in the list of branches to keep
    if ($branch -notin $branchesToKeep) {
        # Delete the branch
        git branch -D $branch
    }
}

Friday, December 29, 2023

Using MOQ

MOQ is a popular mocking framework for .NET used to create mock objects for testing. MOQ allows you to isolate the class under test by replacing its dependencies with controlled mock objects, making it easier to test behavior in isolation.  Here are the steps to use it.  

1.  Install MOQ.  Add MOQ using the NuGet package manager.  https://www.nuget.org/packages/Moq

2.  Create a Mock Object. Use Mock<T> where T is the type you want to mock. For example:

_webAPIClientMock = new Mock<IWebAPIClient>();

3,  Setup Method Behavior. Define how the mock object should behave. For example:

_webAPIClientMock.Setup(client => client.GetApplicationSetting(It.IsAny<GetAppSettingRequest>()).Result).Returns(appSettingResponse);

4.  Inject the mock. Inject the mock object into the class you are testing.

_apiSettingsProvider = new ApiSettingsProvider(Mock.Of<ILogger<ApiSettingsProvider>>(), _webAPIClientMock.Object);

5.  Verify Interactions. Optionally, verify that certain interactions with the mock object occurred, like _webAPIClientMock.Verify(x => x.GetApplicationSetting(), Times.Once);

6.  Run Your Test.  Execute your test method as usual.

Full Example

[TestClass]
public class ApiSettingsProviderTests
{
private  Mock<IWebAPIClient> _webAPIClientMock;
private  ApiSettingsProvider _apiSettingsProvider;

[TestInitialize]
public void Initialize()
{
_webAPIClientMock = new Mock<IWebAPIClient>();
_apiSettingsProvider = new ApiSettingsProvider(Mock.Of<ILogger<ApiSettingsProvider>>(), _webAPIClientMock.Object);
}

[TestMethod]
public void GetAppSettingValue_ShouldReturnFirstValueFromAppSettingsResponse()
{
   // Arrange
var appSettingName = "TestSetting";
var settingValues = new string[] { "Value1", "Value2" };
var appSettingResponse = new GetAppSettingResponse
{
SettingValues = new List<string>(settingValues)
};
_webAPIClientMock.Setup(client => client.GetApplicationSetting(It.IsAny<GetAppSettingRequest>()).Result).Returns(appSettingResponse);
// Act
var value = _apiSettingsProvider.GetAppSettingValue(appSettingName);
// Assert
Assert.AreEqual("Value1", value);
}
}

Resources

Creating Angular Tests and Mocking with Jasmine

When creating Angular Components, by default a spec.ts file is created.  By definition, a unit test should not call any files, databases, or services.  We use a mock, or a stand in for services.  

Mocking Services

To mock services, the jasmine.createSpyObj is used. Example:

loggingService = jasmine.createSpyObj('LoggingService', ['Info','Error']); 

Providing Mocked Services

To use the mocked service, it is provied.  Example:

    TestBed.configureTestingModule({
      providers: [
        ErrorHandlerService, 
        { provide: LoggingService, useValue: loggingService },
        { provide: HttpTestingController, useValue: httpTestingController }
      ],
    });


Full Example

We have an error handler service and we want to ensure that the message is logged by using the Error method.

import { TestBed } from '@angular/core/testing';
import { HttpErrorResponse } from '@angular/common/http';
import { ErrorHandlerService } from './error-handler.service';
import { LoggingService } from './logging.service';
import { HttpTestingController } from '@angular/common/http/testing';

describe('ErrorHandlerService', () => {
  let errorHandlerService: ErrorHandlerService;
  let loggingService: LoggingService;
  let httpTestingController: HttpTestingController;
  
  beforeEach(() => {
    loggingService = jasmine.createSpyObj('LoggingService', ['Info','Error']);
    TestBed.configureTestingModule({
      providers: [
        ErrorHandlerService, 
        { provide: LoggingService, useValue: loggingService },
        { provide: HttpTestingController, useValue: httpTestingController }
      ],
    });
    errorHandlerService = TestBed.inject(ErrorHandlerService);
    httpTestingController = TestBed.inject(HttpTestingController);
  });
  it('should be created', () => {
    expect(errorHandlerService).toBeTruthy();
  });
  it('should handle an error and log it', () => {
    spyOn(console, 'error');
    const error = new Error('Test error message');
    errorHandlerService.handleError(error);
    expect(console.error).toHaveBeenCalledWith(error);
    expect(loggingService.Error).toHaveBeenCalledWith(error.message, [error]);
  });
  
});



Thursday, October 5, 2023

Angular with .NET Core Powershell Setup Script

Here is a Powershell script that I made to automatically install everything that is needed to develop with Angular 16 and .NET Core 7.

Here's a breakdown:


1. Chocolatey Installation Check: At the beginning, the script checks if Chocolatey, a package manager for Windows, is already installed. If it is found at the specified location (`C:\ProgramData\chocolatey`), it prints a message indicating that Chocolatey is installed. If it is not found, the script adjusts the execution policy to allow the script to run, then it fetches and executes the installation script for Chocolatey from its official website.

2.  API Software Installation: Through the Chocolatey package manager, the script proceeds to install the .NET 7.0 SDK, Visual Studio 2022 Community edition, web and data workloads for Visual Studio 2022, SQL Server Management Studio, and Postman. The flags `-y`, `--allow-empty-checksums`, and `--ignore-checksum` are used to automatically approve installations and bypass checksum verifications, which are useful in automated environments but can present security risks if the sources are not trusted.

3. UI Software Installation: For front-end or user interface development, the script installs Node.js LTS version and Visual Studio Code (VSCode) using Chocolatey.

4. VS Code Extensions Installation: Once VSCode is installed, the script installs a series of extensions for it. These extensions range from linters like ESLint, support for Angular development, CSS formatting tools, IntelliSense for paths and classes, and other utility extensions like auto-rename tags, icons, Prettier formatter, and GitHub's Copilot and Copilot Chat.

5. Angular CLI Installation: Finally, the script installs the Angular Command-Line Interface (CLI) globally using npm, the Node.js package manager. The Angular CLI is a vital tool for Angular developers, allowing them to create, manage, and deploy Angular applications with ease.

In summary, this script serves as a one-stop solution to set up a development environment with all the necessary tools and extensions for both API and UI development, primarily targeting .NET and Angular technologies.


# Install Chocolatey if it does not exist
if (Test-Path 'C:\ProgramData\chocolatey') {
    Write-Host "Chocolatey is installed."
} else {
    Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
}

# API Software
& choco install dotnet-7.0-sdk -y --allow-empty-checksums --ignore-checksum
& choco install visualstudio2022community -y --allow-empty-checksums --ignore-checksum
& choco install visualstudio2022-workload-netweb -y --allow-empty-checksums --ignore-checksum
& choco install visualstudio2022-workload-data -y --allow-empty-checksums --ignore-checksum
& choco install sql-server-management-studio -y --allow-empty-checksums --ignore-checksum
& choco install postman -y --allow-empty-checksums --ignore-checksum

# UI Software
& choco install nodejs-lts -y --allow-empty-checksums --ignore-checksum
& choco install vscode -y --allow-empty-checksums --ignore-checksum

# VS Code Extensions
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension dbaeumer.vscode-eslint
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension johnpapa.Angular2
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension aeschli.vscode-css-formatter
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension Angular.ng-template
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension Zignd.html-css-class-completion
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension christian-kohler.path-intellisense
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension formulahendry.auto-rename-tag
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension vscode-icons-team.vscode-icons
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension esbenp.prettier-vscode
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension GitHub.copilot
& "C:\Program Files\Microsoft VS Code\bin\code" --install-extension GitHub.copilot-chat

# Install Angular CLI
& npm install -g @angular/cli