Skip to content

Commit

Permalink
Merge pull request #702 from DFE-Digital/refactor-page-unit-tests
Browse files Browse the repository at this point in the history
Refactor trust page unit tests
  • Loading branch information
dynamictulip authored Jan 24, 2025
2 parents a1fce92 + 79225e3 commit 5807a59
Show file tree
Hide file tree
Showing 32 changed files with 1,294 additions and 2,834 deletions.
22 changes: 22 additions & 0 deletions docs/run-tests-locally.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Use this documentation to run tests locally.

- [Unit tests](#unit-tests)
- [Analyse test coverage](#analyse-test-coverage)
- [UI tests](#ui-tests)
- [Installation](#installation)
- [Running UI tests](#running-ui-tests)
Expand All @@ -22,6 +23,26 @@ dotnet test
> [!NOTE]
> The FIAT db tests will fail unless the Docker engine is running - see [ADR 15. Use Test Containers to unit test FIAT database configuration, usages and change tracking][adr-15] on why and how this works.
### Analyse test coverage

This project uses a mutation score to analyse effective test coverage when opening up a new pull request.
[Stryker.Net][stryker-docs] is included in our dotnet-tools manifest for checking mutation score locally.

Open a terminal at the root folder and type the following to run and open a Stryker report:

```bash
# Install stryker and other tools (only need to do this after a version update)
dotnet tool restore

# Analyse all unit tests and open a html report afterwards
dotnet stryker -o --concurrency 6

# Only mutate one file
dotnet stryker -o --project "DfE.FindInformationAcademiesTrusts.csproj" --mutate "**/TrustService.cs" --concurrency 6
```

You will be able to find all reports in the `StrykerOutput` folder in your project root.

## UI tests

### Installation
Expand Down Expand Up @@ -99,4 +120,5 @@ dotnet test

[adr-15]: adrs/0015-use-test-containers-to-unit-test-fiat-database.md
[docker-run]: ./docker.md#how
[stryker-docs]: https://stryker-mutator.io/docs/
[test-approach]: ./test-approach.md
27 changes: 5 additions & 22 deletions docs/supercharge-your-dev-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

Use this documentation to help supercharge your dev environment. We recommend using Rider or Visual Studio with ReSharper.

- [Supercharge your dev environment](#supercharge-your-dev-environment)
- [Set up continuous testing](#set-up-continuous-testing)
- [Analyse test coverage](#analyse-test-coverage)
- [Configure linting and code cleanup](#configure-linting-and-code-cleanup)
- [Linting markdown](#linting-markdown)
- [Linting cypress tests](#linting-cypress-tests)
- [Formatting cypress tests](#formatting-cypress-tests)
- [Set up continuous testing](#set-up-continuous-testing)
- [Configure linting and code cleanup](#configure-linting-and-code-cleanup)
- [Linting markdown](#linting-markdown)
- [Linting cypress tests](#linting-cypress-tests)
- [Formatting cypress tests](#formatting-cypress-tests)

## Set up continuous testing

Expand All @@ -18,21 +16,6 @@ We recommend setting Rider to run unit tests on save, for fast feedback on chang
- Go to Settings -> Build, Execution, Deployment -> Unit Testing -> Continuous Testing and select 'Automatically start tests in continuous testing sessions on **Save**'
- Go to or open a Unit Tests session (Tests -> Create New Session), open the 'Continuous testing modes' menu and select 'Run all tests'

## Analyse test coverage

This project uses a [mutation score](https://stryker-mutator.io/docs/) to analyse effective test coverage when opening up a new pull request.
Stryker.Net is included in our dotnet-tools manifest for checking mutation score locally.

Install tools by running `dotnet tool restore`.

Open a terminal at the root folder and type the following to run and open a Stryker report:

```bash
dotnet stryker -o
```

You will be able to find all reports in the `StrykerOutput` folder in your project root.

## Configure linting and code cleanup

We recommend setting Rider to clean code on save.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using DfE.FindInformationAcademiesTrusts.Data;
using DfE.FindInformationAcademiesTrusts.Data.Enums;
using DfE.FindInformationAcademiesTrusts.Pages;
using DfE.FindInformationAcademiesTrusts.Pages.Trusts.Academies;
using DfE.FindInformationAcademiesTrusts.Services.DataSource;
using DfE.FindInformationAcademiesTrusts.Services.Export;
using DfE.FindInformationAcademiesTrusts.Services.Trust;
using Microsoft.AspNetCore.Mvc;

namespace DfE.FindInformationAcademiesTrusts.UnitTests.Pages.Trusts.Academies;

public abstract class BaseAcademiesPageModelTests<T> : BaseTrustPageTests<T>, ITestTabPages where T : AcademiesPageModel
{
protected readonly Mock<IExportService> MockExportService = new();
protected readonly Mock<IDateTimeProvider> MockDateTimeProvider = new();

[Fact]
public async Task OnGetExportAsync_ShouldReturnFileResult_WhenUidIsValid()
{
// Arrange
byte[] expectedBytes = [1, 2, 3];

MockExportService.Setup(x => x.ExportAcademiesToSpreadsheetAsync(TrustUid))
.ReturnsAsync(expectedBytes);

// Act
var result = await Sut.OnGetExportAsync(TrustUid);

// Assert
result.Should().BeOfType<FileContentResult>();
var fileResult = result as FileContentResult;
fileResult?.ContentType.Should().Be("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
fileResult?.FileContents.Should().BeEquivalentTo(expectedBytes);
}

[Fact]
public async Task OnGetExportAsync_ShouldReturnNotFound_WhenUidIsInvalid()
{
// Arrange
var uid = "invalid-uid";

MockTrustService.Setup(x => x.GetTrustSummaryAsync(uid))
.ReturnsAsync((TrustSummaryServiceModel?)null);

// Act
var result = await Sut.OnGetExportAsync(uid);

// Assert
result.Should().BeOfType<NotFoundResult>();
}

[Fact]
public async Task OnGetExportAsync_ShouldSanitizeTrustName_WhenTrustNameContainsIllegalCharacters()
{
// Arrange
var uid = TrustUid;
var expectedBytes = new byte[] { 1, 2, 3 };

MockTrustService.Setup(x => x.GetTrustSummaryAsync(uid))
.ReturnsAsync(DummyTrustSummary with { Name = "Sample/Trust:Name?" });
MockExportService.Setup(x => x.ExportAcademiesToSpreadsheetAsync(uid))
.ReturnsAsync(expectedBytes);

// Act
var result = await Sut.OnGetExportAsync(uid);

// Assert
result.Should().BeOfType<FileContentResult>();
var fileResult = result as FileContentResult;
fileResult?.ContentType.Should().Be("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
fileResult?.FileContents.Should().BeEquivalentTo(expectedBytes);
fileResult?.FileDownloadName.Should().NotBeEmpty();

// Verify that the file name is sanitized (no illegal characters)
var fileDownloadName = fileResult?.FileDownloadName ?? string.Empty;
var invalidFileNameChars = Path.GetInvalidFileNameChars();

// Check that the file name doesn't contain any invalid characters
var containsInvalidChars = fileDownloadName.Any(c => invalidFileNameChars.Contains(c));
containsInvalidChars.Should().BeFalse("the file name should not contain any illegal characters");
}

[Fact]
public override async Task OnGetAsync_sets_correct_data_source_list()
{
TrustSummaryServiceModel fakeTrust = new("1234", "My Trust", "Multi-academy trust", 3);
MockTrustService.Setup(t => t.GetTrustSummaryAsync(fakeTrust.Uid)).ReturnsAsync(fakeTrust);
Sut.Uid = fakeTrust.Uid;

_ = await Sut.OnGetAsync();
MockDataSourceService.Verify(e => e.GetAsync(Source.Gias), Times.Once);
MockDataSourceService.Verify(e => e.GetAsync(Source.ExploreEducationStatistics), Times.Once);
Sut.DataSourcesPerPage.Should().BeEquivalentTo([
new DataSourcePageListEntry(ViewConstants.AcademiesDetailsPageName,
[new DataSourceListEntry(GiasDataSource)]),
new DataSourcePageListEntry(ViewConstants.AcademiesPupilNumbersPageName,
[new DataSourceListEntry(GiasDataSource)]),
new DataSourcePageListEntry(ViewConstants.AcademiesFreeSchoolMealsPageName, [
new DataSourceListEntry(GiasDataSource, "Pupils eligible for free school meals"),
new DataSourceListEntry(EesDataSource, "Local authority average 2023/24"),
new DataSourceListEntry(EesDataSource, "National average 2023/24")
])
]);
}

[Fact]
public override async Task OnGetAsync_should_set_active_NavigationLink_to_current_page()
{
_ = await Sut.OnGetAsync();

Sut.NavigationLinks.Should().ContainSingle(l => l.LinkIsActive)
.Which.LinkText.Should().Be("Academies (3)");
}

[Fact]
public override async Task OnGetAsync_should_configure_TrustPageMetadata_PageName()
{
_ = await Sut.OnGetAsync();

Sut.TrustPageMetadata.PageName.Should().Be("Academies");
}

[Fact]
public async Task OnGetAsync_sets_SubNavigationLinks_toEmptyArray()
{
_ = await Sut.OnGetAsync();
Sut.SubNavigationLinks.Should().Equal();
}

[Fact]
public abstract Task OnGetAsync_should_configure_TrustPageMetadata_TabPageName();
}
Loading

0 comments on commit 5807a59

Please sign in to comment.