Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(237003): replace Moq with NSubstitute in C&S unit tests #963

Closed
wants to merge 7 commits into from
36 changes: 15 additions & 21 deletions tests/Dfe.PlanTech.Web.UnitTests/Content/ContentServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
using Dfe.PlanTech.Domain.Content.Queries;
using Dfe.PlanTech.Web.Content;
using FluentAssertions;
using Moq;
using NSubstitute;
using Xunit;

namespace Dfe.PlanTech.Web.UnitTests.Content
{
public class ContentServiceTests
{
private readonly Mock<IModelMapper> _mapperMock;
private readonly Mock<IGetContentSupportPageQuery> _getContentSupportPageQueryMock;
private readonly IModelMapper _mapperMock;
private readonly IGetContentSupportPageQuery _getContentSupportPageQueryMock;

public ContentServiceTests()
{
_mapperMock = new Mock<IModelMapper>();
_getContentSupportPageQueryMock = new Mock<IGetContentSupportPageQuery>();
_mapperMock = Substitute.For<IModelMapper>();
_getContentSupportPageQueryMock = Substitute.For<IGetContentSupportPageQuery>();
}

private ContentService GetService() => new(_mapperMock.Object, _getContentSupportPageQueryMock.Object);
private ContentService GetService() => new(_mapperMock, _getContentSupportPageQueryMock);

[Fact]
public async Task GetContent_Calls_Query_Once()
Expand All @@ -28,25 +28,20 @@ public async Task GetContent_Calls_Query_Once()
var sut = GetService();
var contentSupportPage = new ContentSupportPage { Slug = slug };

_getContentSupportPageQueryMock
.Setup(o => o.GetContentSupportPage(slug, It.IsAny<CancellationToken>()))
.ReturnsAsync(contentSupportPage);
_getContentSupportPageQueryMock.GetContentSupportPage(slug, Arg.Any<CancellationToken>())
.Returns(contentSupportPage);

await sut.GetContent(slug);

_getContentSupportPageQueryMock.Verify(o =>
o.GetContentSupportPage(slug, It.IsAny<CancellationToken>()),
Times.Once
);
await _getContentSupportPageQueryMock.Received(1).GetContentSupportPage(slug, Arg.Any<CancellationToken>());
}

[Fact]
public async Task GetContent_EmptyResponse_Returns_Null()
{
var slug = "slug1";
_getContentSupportPageQueryMock
.Setup(o => o.GetContentSupportPage(slug, It.IsAny<CancellationToken>()))
.ReturnsAsync((ContentSupportPage?)null);
_getContentSupportPageQueryMock.GetContentSupportPage(slug, Arg.Any<CancellationToken>())
.Returns((ContentSupportPage?)null);

var sut = GetService();

Expand All @@ -62,12 +57,11 @@ public async Task GetContent_Returns_Mapped_Result()
var expectedPage = new ContentSupportPage { Slug = slug };
var expectedCsPage = new CsPage { Slug = slug }; // Adjust this mapping as needed

_getContentSupportPageQueryMock
.Setup(o => o.GetContentSupportPage(slug, It.IsAny<CancellationToken>()))
.ReturnsAsync(expectedPage);
_getContentSupportPageQueryMock.GetContentSupportPage(slug, Arg.Any<CancellationToken>())
.Returns(expectedPage);

_mapperMock.Setup(m => m.MapToCsPage(expectedPage))
.Returns(expectedCsPage);
_mapperMock.MapToCsPage(expectedPage)
.Returns(expectedCsPage);

var sut = GetService();
var result = await sut.GetContent(slug);
Expand Down
3 changes: 1 addition & 2 deletions tests/Dfe.PlanTech.Web.UnitTests/Content/ModelMapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Dfe.PlanTech.Web.Configuration;
using Dfe.PlanTech.Web.Content;
using FluentAssertions;
using Moq;
using Xunit;

namespace Dfe.PlanTech.Web.UnitTests.Content;
Expand Down Expand Up @@ -124,7 +123,7 @@ public void BasicComponentReturned()
[Fact]
public void Unknown_Returns_Unknown()
{
var testValue = It.IsAny<string>();
var testValue = "Any string value";

var sut = GetService();
var result = sut.ConvertToRichTextNodeType(testValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using NSubstitute;
using NSubstitute.ReceivedExtensions;
using Xunit;

namespace Dfe.ContentSupport.Web.Tests.Controllers;

public class ContentControllerTests
{
private readonly Mock<ILogger<ContentController>> _loggerMock = new();
private readonly Mock<IContentService> _contentServiceMock = new();
private readonly ILogger<ContentController> _loggerMock = Substitute.For<ILogger<ContentController>>();
private readonly IContentService _contentServiceMock = Substitute.For<IContentService>();

private ContentController GetController()
{
return new ContentController(_contentServiceMock.Object, new LayoutService(), _loggerMock.Object);
return new ContentController(_contentServiceMock, new LayoutService(), _loggerMock);
}

[Fact]
Expand All @@ -29,14 +30,13 @@ public async Task Index_NoSlug_Returns_ErrorAction()
result.Should().BeOfType<RedirectToActionResult>();
(result as RedirectToActionResult)!.ActionName.Should().BeEquivalentTo(PagesController.NotFoundPage);

_loggerMock.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() != null && o.ToString()!.StartsWith($"No slug received for C&S {nameof(ContentController)} {nameof(sut.Index)}")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
_loggerMock.Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Is<Object>(o => o.ToString() != null && o.ToString()!.StartsWith($"No slug received for C&S {nameof(ContentController)} {nameof(sut.Index)}")),
Arg.Any<Exception>(),
Arg.Any<Func<Object, Exception?, string>>()
);
}

[Fact]
Expand All @@ -47,15 +47,15 @@ public async Task Index_Calls_Service_GetContent()

await sut.Index(dummySlug, "", null, default);

_contentServiceMock.Verify(o => o.GetContent(dummySlug, default), Times.Once);
await _contentServiceMock.Received(1).GetContent(dummySlug, default);
}

[Fact]
public async Task Index_NullResponse_ReturnsErrorAction()
{
var slug = "slug";
_contentServiceMock.Setup(o => o.GetContent(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((CsPage?)null);

_contentServiceMock.GetContent(Arg.Any<String>(), Arg.Any<CancellationToken>()).Returns((CsPage?)null);

var sut = GetController();

Expand All @@ -64,21 +64,23 @@ public async Task Index_NullResponse_ReturnsErrorAction()
result.Should().BeOfType<RedirectToActionResult>();
(result as RedirectToActionResult)!.ActionName.Should().BeEquivalentTo(PagesController.NotFoundPage);

_loggerMock.Verify(x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() != null && o.ToString()!.Equals($"Failed to load content for C&S page {slug}; no content received.")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);

_loggerMock.Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Is<Object>(o => o.ToString() != null && o.ToString()!.Equals($"Failed to load content for C&S page {slug}; no content received.")),
Arg.Any<Exception>(),
Arg.Any<Func<Object, Exception?, string>>()
);
}

[Fact]
public async Task Index_ExceptionThrown_ReturnsErrorAction()
{
var slug = "slug";
_contentServiceMock.Setup(o => o.GetContent(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new Exception("An exception occurred loading content"));

_contentServiceMock.GetContent(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns(_ => Task.FromException<CsPage?>(new Exception("An exception occurred loading content")));

var sut = GetController();

Expand All @@ -87,20 +89,19 @@ public async Task Index_ExceptionThrown_ReturnsErrorAction()
result.Should().BeOfType<RedirectToActionResult>();
(result as RedirectToActionResult)!.ActionName.Should().BeEquivalentTo("error");

_loggerMock.Verify(x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() != null && o.ToString()!.Equals($"Error loading C&S content page {slug}")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
_loggerMock.Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Is<object>(o => o.ToString() != null && o.ToString()!.Equals($"Error loading C&S content page {slug}")),
Arg.Any<Exception>(),
Arg.Any<Func<object, Exception?, string>>()
);
}

[Fact]
public async Task Index_WithSlug_Returns_View()
{
_contentServiceMock.Setup(o => o.GetContent(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new CsPage());
_contentServiceMock.GetContent(Arg.Any<string>(), Arg.Any<CancellationToken>()).Returns(new CsPage());

var sut = GetController();
var result = await sut.Index("slug1", "", null, default);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\src\Dfe.PlanTech.Infrastructure.Data\Dfe.PlanTech.Infrastructure.Data.csproj"/>
<ProjectReference Include="..\..\src\Dfe.PlanTech.Web\Dfe.PlanTech.Web.csproj"/>
<ProjectReference Include="..\Dfe.PlanTech.UnitTests.Shared\Dfe.PlanTech.UnitTests.Shared.csproj"/>
<ProjectReference Include="..\..\src\Dfe.PlanTech.Infrastructure.Data\Dfe.PlanTech.Infrastructure.Data.csproj" />
<ProjectReference Include="..\..\src\Dfe.PlanTech.Web\Dfe.PlanTech.Web.csproj" />
<ProjectReference Include="..\Dfe.PlanTech.UnitTests.Shared\Dfe.PlanTech.UnitTests.Shared.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
<PackageReference Include="Moq" Version="4.20.70"/>
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.3"/>
<PackageReference Include="NSubstitute" Version="5.3.0"/>
<PackageReference Include="xunit" Version="2.9.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.3" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
Loading