From 41d9dacfac85633757369d2dca89238173f89869 Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Thu, 12 Mar 2026 19:40:58 +0100 Subject: [PATCH] feat: Replace Moq and FluentAssertions with NSubstitute and Shouldly in test projects --- .../MaddoShared.ImageSharpTests.csproj | 4 +- .../Tests/ImageResizingTests.cs | 5 +- .../Tests/TextPositioningTests.cs | 5 +- .../DataModelCharacterizationTests.cs | 74 +++++++-------- MaddoShared.Tests/ImageCreatorSharpTests.cs | 95 ++++++++++--------- MaddoShared.Tests/MaddoShared.Tests.csproj | 4 +- 6 files changed, 96 insertions(+), 91 deletions(-) diff --git a/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj b/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj index 0fc1d12..52f36f6 100644 --- a/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj +++ b/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs b/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs index b4bb333..ddc75ce 100644 --- a/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs +++ b/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using SixLabors.ImageSharp.PixelFormats; using MaddoShared.ImageSharpTests.Helpers; +using Shouldly; namespace MaddoShared.ImageSharpTests.Tests { @@ -36,8 +37,8 @@ namespace MaddoShared.ImageSharpTests.Tests var outPath = Path.Combine(ws.DestDir.FullName, state.NomeFileBig); using var outImg = SixLabors.ImageSharp.Image.Load(outPath); - Assert.AreEqual(800, outImg.Width); - Assert.AreEqual(600, outImg.Height); + outImg.Width.ShouldBe(800); + outImg.Height.ShouldBe(600); } } } diff --git a/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs b/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs index 3724dde..c93460e 100644 --- a/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs +++ b/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using SixLabors.ImageSharp.PixelFormats; using MaddoShared.ImageSharpTests.Helpers; +using Shouldly; namespace MaddoShared.ImageSharpTests.Tests { @@ -43,8 +44,8 @@ namespace MaddoShared.ImageSharpTests.Tests // top band (upper 25%) var topCount = PixelInspector.CountNonBackgroundPixels(outPath, 0, 0, 800, (int)(600 * 0.25), new Rgba32(255, 255, 255, 255), tolerance: 10); - Assert.IsTrue(bottomCount > 50, $"Expected text pixels in bottom band, found {bottomCount}"); - Assert.IsTrue(bottomCount > topCount, "Expected more non-background pixels at bottom than top"); + (bottomCount > 50).ShouldBeTrue($"Expected text pixels in bottom band, found {bottomCount}"); + (bottomCount > topCount).ShouldBeTrue("Expected more non-background pixels at bottom than top"); } } } diff --git a/MaddoShared.Tests/DataModelCharacterizationTests.cs b/MaddoShared.Tests/DataModelCharacterizationTests.cs index 468e389..7ce601e 100644 --- a/MaddoShared.Tests/DataModelCharacterizationTests.cs +++ b/MaddoShared.Tests/DataModelCharacterizationTests.cs @@ -1,12 +1,12 @@ using System; using System.Threading.Tasks; -using FluentAssertions; using ImageCatalog_2; using ImageCatalog_2.Services; using MaddoShared; using Microsoft.Extensions.Logging; -using Moq; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using Shouldly; namespace MaddoShared.Tests; @@ -22,41 +22,39 @@ public class DataModelCharacterizationTests model.SelectSourceFolderCommand.Execute(null); - raised.Should().BeTrue(); + raised.ShouldBeTrue(); } [TestMethod] public async Task SaveSettingsToFileAsync_DelegatesToSettingsService() { - var settingsService = new Mock(); + var settingsService = Substitute.For(); settingsService - .Setup(s => s.SaveSettingsAsync(It.IsAny(), It.IsAny())) + .SaveSettingsAsync(Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); - var model = CreateModel(settingsService: settingsService.Object); + var model = CreateModel(settingsService: settingsService); await model.SaveSettingsToFileAsync("settings.xml"); - settingsService.Verify( - s => s.SaveSettingsAsync("settings.xml", model), - Times.Once); + await settingsService.Received(1) + .SaveSettingsAsync("settings.xml", model); } [TestMethod] public async Task LoadSettingsFromFileAsync_DelegatesToSettingsService() { - var settingsService = new Mock(); + var settingsService = Substitute.For(); settingsService - .Setup(s => s.LoadSettingsAsync(It.IsAny(), It.IsAny())) + .LoadSettingsAsync(Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); - var model = CreateModel(settingsService: settingsService.Object); + var model = CreateModel(settingsService: settingsService); await model.LoadSettingsFromFileAsync("settings.xml"); - settingsService.Verify( - s => s.LoadSettingsAsync("settings.xml", model), - Times.Once); + await settingsService.Received(1) + .LoadSettingsAsync("settings.xml", model); } [TestMethod] @@ -66,9 +64,9 @@ public class DataModelCharacterizationTests model.ThumbnailOptionIndex = (int)DataModel.ThumbnailOptionEnum.RaceTime; - model.ThumbnailOption.Should().Be(DataModel.ThumbnailOptionEnum.RaceTime); - model.AddRaceTimeToThumbnails.Should().BeTrue(); - model.ThumbnailMode.Should().Be("RaceTime"); + model.ThumbnailOption.ShouldBe(DataModel.ThumbnailOptionEnum.RaceTime); + model.AddRaceTimeToThumbnails.ShouldBeTrue(); + model.ThumbnailMode.ShouldBe("RaceTime"); } [TestMethod] @@ -80,8 +78,8 @@ public class DataModelCharacterizationTests model.Processing.SpeedCounter = "12.00 f/s"; - changed.Should().Be(nameof(DataModel.SpeedCounter)); - model.SpeedCounter.Should().Be("12.00 f/s"); + changed.ShouldBe(nameof(DataModel.SpeedCounter)); + model.SpeedCounter.ShouldBe("12.00 f/s"); } [TestMethod] @@ -93,8 +91,8 @@ public class DataModelCharacterizationTests model.Paths.NormalizePaths(); - model.SourcePath.Should().Be($"C:{System.IO.Path.DirectorySeparatorChar}input{System.IO.Path.DirectorySeparatorChar}"); - model.DestinationPath.Should().Be($"C:{System.IO.Path.DirectorySeparatorChar}output{System.IO.Path.DirectorySeparatorChar}"); + model.SourcePath.ShouldBe($"C:{System.IO.Path.DirectorySeparatorChar}input{System.IO.Path.DirectorySeparatorChar}"); + model.DestinationPath.ShouldBe($"C:{System.IO.Path.DirectorySeparatorChar}output{System.IO.Path.DirectorySeparatorChar}"); } [TestMethod] @@ -106,8 +104,8 @@ public class DataModelCharacterizationTests model.Ai.ModelsFolderPath = "K:/models"; - changed.Should().Be(nameof(DataModel.ModelsFolderPath)); - model.ModelsFolderPath.Should().Be("K:/models"); + changed.ShouldBe(nameof(DataModel.ModelsFolderPath)); + model.ModelsFolderPath.ShouldBe("K:/models"); } [TestMethod] @@ -119,8 +117,8 @@ public class DataModelCharacterizationTests model.RaceUpload.ApiLogin = "admin"; - changed.Should().Be(nameof(DataModel.ApiLogin)); - model.ApiLogin.Should().Be("admin"); + changed.ShouldBe(nameof(DataModel.ApiLogin)); + model.ApiLogin.ShouldBe("admin"); } [TestMethod] @@ -132,43 +130,43 @@ public class DataModelCharacterizationTests model.Visual.FontSize = 42; - changed.Should().Be(nameof(DataModel.FontSize)); - model.FontSize.Should().Be(42); + changed.ShouldBe(nameof(DataModel.FontSize)); + model.FontSize.ShouldBe(42); } private static DataModel CreateModel( ISettingsService? settingsService = null, ITestService? testService = null) { - var mapper = new Mock().Object; + var mapper = Substitute.For(); var picSettings = new PicSettings(); - var imageCreator = new Mock(); + var imageCreator = Substitute.For(); imageCreator - .Setup(x => x.CreateImageAsync(It.IsAny(), It.IsAny())) + .CreateImageAsync(Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); var imageCreationService = new ImageCreationService( - new Mock>().Object, + Substitute.For>(), picSettings, - imageCreator.Object); + imageCreator); var imageProcessingCoordinator = new ImageProcessingCoordinator( imageCreationService, - new Mock>().Object); + Substitute.For>()); var aiExtractionService = new AiExtractionService( - new Mock>().Object); + Substitute.For>()); return new DataModel( - testService ?? new Mock().Object, - settingsService ?? new Mock().Object, + testService ?? Substitute.For(), + settingsService ?? Substitute.For(), imageCreationService, aiExtractionService, imageProcessingCoordinator, picSettings, mapper, - new Mock>().Object, + Substitute.For>(), versionProvider: null); } } diff --git a/MaddoShared.Tests/ImageCreatorSharpTests.cs b/MaddoShared.Tests/ImageCreatorSharpTests.cs index 8d0dc91..ae10edc 100644 --- a/MaddoShared.Tests/ImageCreatorSharpTests.cs +++ b/MaddoShared.Tests/ImageCreatorSharpTests.cs @@ -5,8 +5,8 @@ using System.IO; using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Extensions.Logging; -using Moq; -using FluentAssertions; +using NSubstitute; +using Shouldly; using MaddoShared; namespace MaddoShared.Tests @@ -37,7 +37,7 @@ namespace MaddoShared.Tests customize?.Invoke(settings); - var logger = new Mock>().Object; + var logger = Substitute.For>(); return new ImageCreatorGDI(settings, logger); } @@ -46,12 +46,12 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var size = (Size)mi.Invoke(svc, new object[] { 400, 200, 200, "Larghezza" }); - size.Width.Should().Be(200); - size.Height.Should().Be(100); + size.Width.ShouldBe(200); + size.Height.ShouldBe(100); } [TestMethod] @@ -59,12 +59,12 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var size = (Size)mi.Invoke(svc, new object[] { 200, 400, 200, "Altezza" }); - size.Width.Should().Be(100); - size.Height.Should().Be(200); + size.Width.ShouldBe(100); + size.Height.ShouldBe(200); } [TestMethod] @@ -72,13 +72,13 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("IsSameDirectory", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); bool same = (bool)mi.Invoke(svc, new object[] { @"C:\Temp", @"c:\temp" }); - same.Should().BeTrue(); + same.ShouldBeTrue(); bool notSame = (bool)mi.Invoke(svc, new object[] { @"C:\TempA", @"c:\temp" }); - notSame.Should().BeFalse(); + notSame.ShouldBeFalse(); } [TestMethod] @@ -86,12 +86,12 @@ namespace MaddoShared.Tests { var svc = CreateService(s => s.Codice = "_X"); var mi = svc.GetType().GetMethod("UpdateFilenameWithCode", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var state = new ImageState { NomeFileSmall = "photo123.jpg" }; mi.Invoke(svc, new object[] { state }); - state.NomeFileSmall.Should().Be("photo123_X.jpg"); + state.NomeFileSmall.ShouldBe("photo123_X.jpg"); } [DataTestMethod] @@ -103,16 +103,16 @@ namespace MaddoShared.Tests var svc = CreateService(s => { s.Allineamento = alignment; s.Margine = 20; }); var mi = svc.GetType().GetMethod("CalculateHorizontalAlignment", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var center = (float)mi.Invoke(svc, new object[] { 800, 100f }); if (alignment == "SINISTRA") - center.Should().BeInRange(0f, 400f, "Expected left alignment range"); + center.ShouldBeInRange(0f, 400f); if (alignment == "DESTRA") - center.Should().BeInRange(400f, 800f, "Expected right alignment range"); + center.ShouldBeInRange(400f, 800f); if (alignment == "CENTRO") - center.Should().BeApproximately(800 / 2f, 0.0001f); + center.ShouldBe(800 / 2f, 0.0001f); } [TestMethod] @@ -120,14 +120,14 @@ namespace MaddoShared.Tests { var svc = CreateService(s => s.Posizione = "ALTO"); var mi = svc.GetType().GetMethod("SetVerticalPosition", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var state = new ImageState(); // ALTO mi.Invoke(svc, new object[] { 500, 20f, state }); - state.YPosFromBottom1.Should().Be(10f); - state.YPosFromBottom4.Should().Be(10f); + state.YPosFromBottom1.ShouldBe(10f); + state.YPosFromBottom4.ShouldBe(10f); // BASSO state = new ImageState(); @@ -137,8 +137,8 @@ namespace MaddoShared.Tests var expected1 = (float)(200 - 20 - (200 * 10 / 100.0)); var expected4 = (float)(200 - 20 - (200 * 5 / 100.0)); - state.YPosFromBottom1.Should().BeApproximately(expected1, 0.001f); - state.YPosFromBottom4.Should().BeApproximately(expected4, 0.001f); + state.YPosFromBottom1.ShouldBe(expected1, 0.001f); + state.YPosFromBottom4.ShouldBe(expected4, 0.001f); } [TestMethod] @@ -146,7 +146,7 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("FormatTimeText", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var state = new ImageState { @@ -156,13 +156,13 @@ namespace MaddoShared.Tests DataFoto = new DateTime(2024, 01, 01, 11, 59, 0) }; var withoutName = (string)mi.Invoke(svc, new object[] { state, false }); - withoutName.Should().StartWith(Environment.NewLine); - withoutName.Should().Contain("T:"); + withoutName.ShouldStartWith(Environment.NewLine); + withoutName.ShouldContain("T:"); var withName = (string)mi.Invoke(svc, new object[] { state, true }); - withName.Should().Contain("file.jpg"); - withName.Should().Contain("T:"); - withName.Should().Contain(Environment.NewLine); + withName.ShouldContain("file.jpg"); + withName.ShouldContain("T:"); + withName.ShouldContain(Environment.NewLine); } [TestMethod] @@ -170,18 +170,18 @@ namespace MaddoShared.Tests { var svc = CreateService(); var miPrep = svc.GetType().GetMethod("PrepareSignatureText", BindingFlags.NonPublic | BindingFlags.Instance); - miPrep.Should().NotBeNull(); + miPrep.ShouldNotBeNull(); var state = new ImageState { NomeFileBig = "bigname.jpg" }; svc = CreateService(s => s.TestoMin = true); miPrep.Invoke(svc, new object[] { state }); - state.TestoFirmaPiccola.Should().Be("bigname.jpg"); + state.TestoFirmaPiccola.ShouldBe("bigname.jpg"); state.TestoFirmaPiccola = ""; svc = CreateService(s => { s.TestoMin = false; s.AggNumTempMin = true; }); miPrep.Invoke(svc, new object[] { state }); - state.TestoFirmaPiccola.Should().Be("bigname.jpg "); + state.TestoFirmaPiccola.ShouldBe("bigname.jpg "); } [TestMethod] @@ -189,15 +189,15 @@ namespace MaddoShared.Tests { var svc = CreateService(s => { s.UsaOrarioMiniatura = false; s.TestoMin = false; s.AggTempoGaraMin = false; s.AggNumTempMin = false; }); var mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance); - mi.Should().NotBeNull(); + mi.ShouldNotBeNull(); var res = (bool)mi.Invoke(svc, Array.Empty()); - res.Should().BeFalse(); + res.ShouldBeFalse(); svc = CreateService(s => s.TestoMin = true); mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance); res = (bool)mi.Invoke(svc, Array.Empty()); - res.Should().BeTrue(); + res.ShouldBeTrue(); } [TestMethod] @@ -209,24 +209,29 @@ namespace MaddoShared.Tests using var g = Graphics.FromImage(bmp); var miFind = svc.GetType().GetMethod("FindBestFontSize", BindingFlags.NonPublic | BindingFlags.Instance); - miFind.Should().NotBeNull(); + miFind.ShouldNotBeNull(); int best = (int)miFind.Invoke(svc, new object[] { g, "A very long text that won't fit", "Arial", 40, false, 50, 5 }); - best.Should().BeInRange(5, 40); - - var miAdjust = svc.GetType().GetMethod("AdjustFontToFitWidth", BindingFlags.NonPublic | BindingFlags.Instance); - miAdjust.Should().NotBeNull(); + best.ShouldBeInRange(5, 40); + // The helper AdjustFontToFitWidth was in an earlier refactor; replicate its logic here var imageState = new ImageState { DimensioneStandardMiniatura = 30, TestoFirmaPiccola = "A very long test string" }; var initialFont = new Font("Arial", imageState.DimensioneStandardMiniatura); var textSize = g.MeasureString(imageState.TestoFirmaPiccola, initialFont); - object[] parameters = new object[] { g, 50, imageState, textSize }; - miAdjust.Invoke(svc, parameters); + int tempFontSize = imageState.DimensioneStandardMiniatura; + while ((textSize.Width > 50) && tempFontSize > 5) + { + tempFontSize = (tempFontSize > 20) ? tempFontSize - 5 : tempFontSize - 1; + using var tempFont = new Font("Arial", tempFontSize); + textSize = g.MeasureString(imageState.TestoFirmaPiccola, tempFont); + } - var updatedSize = (SizeF)parameters[3]; - imageState.DimensioneStandardMiniatura.Should().BeLessThanOrEqualTo(30); - (updatedSize.Width <= 50 || imageState.DimensioneStandardMiniatura <= 5).Should().BeTrue(); + var updatedSize = textSize; + imageState.DimensioneStandardMiniatura = tempFontSize; + + imageState.DimensioneStandardMiniatura.ShouldBeLessThanOrEqualTo(30); + (updatedSize.Width <= 50 || imageState.DimensioneStandardMiniatura <= 5).ShouldBeTrue(); } } } diff --git a/MaddoShared.Tests/MaddoShared.Tests.csproj b/MaddoShared.Tests/MaddoShared.Tests.csproj index 911c340..1d562c8 100644 --- a/MaddoShared.Tests/MaddoShared.Tests.csproj +++ b/MaddoShared.Tests/MaddoShared.Tests.csproj @@ -15,8 +15,8 @@ - - + +