From acbbe50526429bf7ff9d1b26076ef3e77bea17f5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 24 May 2024 18:53:00 -0400 Subject: [PATCH] use FluentAssertions consistently in unit tests --- src/SMAPI.Tests/Core/AssetNameTests.cs | 9 +- src/SMAPI.Tests/Core/ModResolverTests.cs | 128 +++++++++--------- src/SMAPI.Tests/Core/TranslationTests.cs | 60 ++++---- src/SMAPI.Tests/Utilities/KeybindListTests.cs | 33 +++-- .../Utilities/PathUtilitiesTests.cs | 17 ++- src/SMAPI.Tests/Utilities/SDateTests.cs | 23 ++-- .../Utilities/SemanticVersionTests.cs | 47 +++++-- .../WikiClient/ChangeDescriptorTests.cs | 21 +-- 8 files changed, 182 insertions(+), 156 deletions(-) diff --git a/src/SMAPI.Tests/Core/AssetNameTests.cs b/src/SMAPI.Tests/Core/AssetNameTests.cs index 2d546ec7..74d74b2c 100644 --- a/src/SMAPI.Tests/Core/AssetNameTests.cs +++ b/src/SMAPI.Tests/Core/AssetNameTests.cs @@ -56,11 +56,10 @@ namespace SMAPI.Tests.Core public void Constructor_NullOrWhitespace(string? name) { // act - ArgumentException exception = Assert.Throws(() => _ = AssetName.Parse(name!, _ => null))!; - - // assert - exception.ParamName.Should().Be("rawName"); - exception.Message.Should().Be("The asset name can't be null or empty. (Parameter 'rawName')"); + FluentActions.Invoking(() => _ = AssetName.Parse(name!, _ => null)).Should() + .Throw() + .WithParameterName("rawName") + .WithMessage("The asset name can't be null or empty. (Parameter 'rawName')"); } diff --git a/src/SMAPI.Tests/Core/ModResolverTests.cs b/src/SMAPI.Tests/Core/ModResolverTests.cs index 55df823a..51b3a87c 100644 --- a/src/SMAPI.Tests/Core/ModResolverTests.cs +++ b/src/SMAPI.Tests/Core/ModResolverTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using FluentAssertions; using Moq; using Newtonsoft.Json; using NUnit.Framework; @@ -38,7 +39,7 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ReadManifests(new ModToolkit(), rootFolder, new ModDatabase(), useCaseInsensitiveFilePaths: true).ToArray(); // assert - Assert.AreEqual(0, mods.Length, 0, $"Expected to find zero manifests, found {mods.Length} instead."); + mods.Should().BeEmpty("it should match number of mods input"); // cleanup Directory.Delete(rootFolder, recursive: true); @@ -57,9 +58,9 @@ namespace SMAPI.Tests.Core IModMetadata? mod = mods.FirstOrDefault(); // assert - Assert.AreEqual(1, mods.Length, 0, $"Expected to find one manifest, found {mods.Length} instead."); - Assert.AreEqual(ModMetadataStatus.Failed, mod!.Status, "The mod metadata was not marked failed."); - Assert.IsNotNull(mod.Error, "The mod metadata did not have an error message set."); + mods.Should().HaveCount(1, "it should match number of mods input"); + mod!.Status.Should().Be(ModMetadataStatus.Failed); + mod.Error.Should().NotBeNull(); // cleanup Directory.Delete(rootFolder, recursive: true); @@ -100,30 +101,27 @@ namespace SMAPI.Tests.Core IModMetadata? mod = mods.FirstOrDefault(); // assert - Assert.AreEqual(1, mods.Length, 0, "Expected to find one manifest."); - Assert.IsNotNull(mod, "The loaded manifest shouldn't be null."); - Assert.AreEqual(null, mod!.DataRecord, "The data record should be null since we didn't provide one."); - Assert.AreEqual(modFolder, mod.DirectoryPath, "The directory path doesn't match."); - Assert.AreEqual(null, mod.Error, "The error should be null since parsing should have succeeded."); - Assert.AreEqual(ModMetadataStatus.Found, mod.Status, "The status doesn't match."); + mods.Should().HaveCount(1, "it should match number of mods input"); + mod.Should().NotBeNull(); + mod!.DataRecord.Should().BeNull("we didn't provide one"); + mod.DirectoryPath.Should().Be(modFolder); + mod.Error.Should().BeNull(); + mod.Status.Should().Be(ModMetadataStatus.Found); - Assert.AreEqual(original[nameof(IManifest.Name)], mod.DisplayName, "The display name should use the manifest name."); - Assert.AreEqual(original[nameof(IManifest.Name)], mod.Manifest.Name, "The manifest's name doesn't match."); - Assert.AreEqual(original[nameof(IManifest.Author)], mod.Manifest.Author, "The manifest's author doesn't match."); - Assert.AreEqual(original[nameof(IManifest.Description)], mod.Manifest.Description, "The manifest's description doesn't match."); - Assert.AreEqual(original[nameof(IManifest.EntryDll)], mod.Manifest.EntryDll, "The manifest's entry DLL doesn't match."); - Assert.AreEqual(original[nameof(IManifest.MinimumApiVersion)], mod.Manifest.MinimumApiVersion?.ToString(), "The manifest's minimum API version doesn't match."); - Assert.AreEqual(original[nameof(IManifest.MinimumGameVersion)], mod.Manifest.MinimumGameVersion?.ToString(), "The manifest's minimum game version doesn't match."); - Assert.AreEqual(original[nameof(IManifest.Version)].ToString(), mod.Manifest.Version.ToString(), "The manifest's version doesn't match."); + mod.DisplayName.Should().Be((string)original[nameof(IManifest.Name)], mod.DisplayName); + mod.Manifest.Name.Should().Be((string)original[nameof(IManifest.Name)], mod.Manifest.Name); + mod.Manifest.ExtraFields.Should() + .NotBeNull() + .And.HaveCount(2) + .And.ContainKeys("ExtraString", "ExtraInt"); + mod.Manifest.ExtraFields["ExtraString"].Should().Be(original["ExtraString"]); + mod.Manifest.ExtraFields["ExtraInt"].Should().Be(original["ExtraInt"]); - Assert.IsNotNull(mod.Manifest.ExtraFields, "The extra fields should not be null."); - Assert.AreEqual(2, mod.Manifest.ExtraFields.Count, "The extra fields should contain two values."); - Assert.AreEqual(original["ExtraString"], mod.Manifest.ExtraFields["ExtraString"], "The manifest's extra fields should contain an 'ExtraString' value."); - Assert.AreEqual(original["ExtraInt"], mod.Manifest.ExtraFields["ExtraInt"], "The manifest's extra fields should contain an 'ExtraInt' value."); - - Assert.IsNotNull(mod.Manifest.Dependencies, "The dependencies field should not be null."); - Assert.AreEqual(1, mod.Manifest.Dependencies.Length, "The dependencies field should contain one value."); - Assert.AreEqual(originalDependency[nameof(IManifestDependency.UniqueID)], mod.Manifest.Dependencies[0].UniqueID, "The first dependency's unique ID doesn't match."); + mod.Manifest.Dependencies.Should() + .NotBeNull() + .And.HaveCount(1); + mod.Manifest.Dependencies[0].Should().NotBeNull(); + mod.Manifest.Dependencies[0].UniqueID.Should().Be((string)originalDependency[nameof(IManifestDependency.UniqueID)]); // cleanup Directory.Delete(rootFolder, recursive: true); @@ -268,7 +266,7 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(Array.Empty(), new ModDatabase()).ToArray(); // assert - Assert.AreEqual(0, mods.Length, 0, "Expected to get an empty list of mods."); + mods.Should().BeEmpty("it should match number of mods input"); } [Test(Description = "Assert that processing dependencies doesn't change the order if there are no mod dependencies.")] @@ -284,10 +282,10 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modA.Object, modB.Object, modC.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(3, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order unexpectedly changed with no dependencies."); - Assert.AreSame(modB.Object, mods[1], "The load order unexpectedly changed with no dependencies."); - Assert.AreSame(modC.Object, mods[2], "The load order unexpectedly changed with no dependencies."); + mods.Should().HaveCount(3, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "the load order shouldn't change with no dependencies"); + mods[1].Should().BeSameAs(modB.Object, "the load order shouldn't change with no dependencies"); + mods[2].Should().BeSameAs(modC.Object, "the load order shouldn't change with no dependencies"); } [Test(Description = "Assert that processing dependencies skips mods that have already failed without calling any other properties.")] @@ -320,10 +318,10 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modC.Object, modA.Object, modB.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(3, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since the other mods depend on it."); - Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A, and is needed by mod C."); - Assert.AreSame(modC.Object, mods[2], "The load order is incorrect: mod C should be third since it needs both mod A and mod B."); + mods.Should().HaveCount(3, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "mod A should be first since the other mods depend on it"); + mods[1].Should().BeSameAs(modB.Object, "mod B should be second since it needs mod A, and is needed by mod C"); + mods[2].Should().BeSameAs(modC.Object, "mod C should be third since it needs both mod A and mod B"); } [Test(Description = "Assert that simple dependency chains are reordered correctly.")] @@ -340,11 +338,11 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modC.Object, modA.Object, modB.Object, modD.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(4, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since it's needed by mod B."); - Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A, and is needed by mod C."); - Assert.AreSame(modC.Object, mods[2], "The load order is incorrect: mod C should be third since it needs mod B, and is needed by mod D."); - Assert.AreSame(modD.Object, mods[3], "The load order is incorrect: mod D should be fourth since it needs mod C."); + mods.Should().HaveCount(4, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "mod A should be first since it's needed by mod B"); + mods[1].Should().BeSameAs(modB.Object, "mod B should be second since it needs mod A, and is needed by mod C"); + mods[2].Should().BeSameAs(modC.Object, "mod C should be third since it needs mod B, and is needed by mod D"); + mods[3].Should().BeSameAs(modD.Object, "mod D should be fourth since it needs mod C"); } [Test(Description = "Assert that overlapping dependency chains are reordered correctly.")] @@ -366,13 +364,13 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modC.Object, modA.Object, modB.Object, modD.Object, modF.Object, modE.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(6, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since it's needed by mod B."); - Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A, and is needed by mod C."); - Assert.AreSame(modC.Object, mods[2], "The load order is incorrect: mod C should be third since it needs mod B, and is needed by mod D."); - Assert.AreSame(modD.Object, mods[3], "The load order is incorrect: mod D should be fourth since it needs mod C."); - Assert.AreSame(modE.Object, mods[4], "The load order is incorrect: mod E should be fifth since it needs mod B, but is specified after C which also needs mod B."); - Assert.AreSame(modF.Object, mods[5], "The load order is incorrect: mod F should be last since it needs mods E and C."); + mods.Should().HaveCount(6, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "mod A should be first since it's needed by mod B"); + mods[1].Should().BeSameAs(modB.Object, "mod B should be second since it needs mod A, and is needed by mod C"); + mods[2].Should().BeSameAs(modC.Object, "mod C should be third since it needs mod B, and is needed by mod D"); + mods[3].Should().BeSameAs(modD.Object, "mod D should be fourth since it needs mod C"); + mods[4].Should().BeSameAs(modE.Object, "mod E should be fifth since it needs mod B, but is specified after C which also needs mod B"); + mods[5].Should().BeSameAs(modF.Object, "mod F should be last since it needs mods E and C"); } [Test(Description = "Assert that mods with circular dependency chains are skipped, but any other mods are loaded in the correct order.")] @@ -393,9 +391,9 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modC.Object, modA.Object, modB.Object, modD.Object, modE.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(5, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since it's needed by mod B."); - Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A."); + mods.Should().HaveCount(5, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "mod A should be first since it's needed by mod B"); + mods[1].Should().BeSameAs(modB.Object, "mod B should be second since it needs mod A"); modC.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny(), It.IsAny(), It.IsAny()), Times.Once, "Mod C was expected to fail since it's part of a dependency loop."); modD.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny(), It.IsAny(), It.IsAny()), Times.Once, "Mod D was expected to fail since it's part of a dependency loop."); modE.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny(), It.IsAny(), It.IsAny()), Times.Once, "Mod E was expected to fail since it's part of a dependency loop."); @@ -410,18 +408,18 @@ namespace SMAPI.Tests.Core Mock modB = this.GetMetadata("Mod B", dependencies: new[] { "Mod A" }); Mock modC = this.GetMetadata("Mod C", dependencies: new[] { "Mod B" }, allowStatusChange: true); Mock modD = new(MockBehavior.Strict); - modD.Setup(p => p.Manifest).Returns(null); + modD.Setup(p => p.Manifest).Returns(null!); // deliberately testing null handling modD.Setup(p => p.Status).Returns(ModMetadataStatus.Failed); // act IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modC.Object, modA.Object, modB.Object, modD.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(4, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modD.Object, mods[0], "The load order is incorrect: mod D should be first since it was already failed."); - Assert.AreSame(modA.Object, mods[1], "The load order is incorrect: mod A should be second since it's needed by mod B."); - Assert.AreSame(modB.Object, mods[2], "The load order is incorrect: mod B should be third since it needs mod A, and is needed by mod C."); - Assert.AreSame(modC.Object, mods[3], "The load order is incorrect: mod C should be fourth since it needs mod B, and is needed by mod D."); + mods.Should().HaveCount(4, "it should match number of mods input"); + mods[0].Should().BeSameAs(modD.Object, "mod D should be first since it was already failed"); + mods[1].Should().BeSameAs(modA.Object, "mod A should be second since it's needed by mod B"); + mods[2].Should().BeSameAs(modB.Object, "mod B should be third since it needs mod A, and is needed by mod C"); + mods[3].Should().BeSameAs(modC.Object, "mod C should be fourth since it needs mod B, and is needed by mod D"); } [Test(Description = "Assert that dependencies are failed if they don't meet the minimum version.")] @@ -436,7 +434,7 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modA.Object, modB.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(2, mods.Length, 0, "Expected to get the same number of mods input."); + mods.Should().HaveCount(2, "it should match number of mods input"); modB.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny(), It.IsAny(), It.IsAny()), Times.Once, "Mod B unexpectedly didn't fail even though it needs a newer version of Mod A."); } @@ -452,9 +450,9 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modA.Object, modB.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(2, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since it's needed by mod B."); - Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A."); + mods.Should().HaveCount(2, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "mod A should be first since it's needed by mod B"); + mods[1].Should().BeSameAs(modB.Object, "mod B should be second since it needs mod A"); } [Test(Description = "Assert that optional dependencies are sorted correctly if present.")] @@ -469,9 +467,9 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modB.Object, modA.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(2, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since it's needed by mod B."); - Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A."); + mods.Should().HaveCount(2, "it should match number of mods input"); + mods[0].Should().BeSameAs(modA.Object, "mod A should be first since it's needed by mod B"); + mods[1].Should().BeSameAs(modB.Object, "mod B should be second since it needs mod A"); } [Test(Description = "Assert that optional dependencies are accepted if they're missing.")] @@ -485,8 +483,8 @@ namespace SMAPI.Tests.Core IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modB.Object }, new ModDatabase()).ToArray(); // assert - Assert.AreEqual(1, mods.Length, 0, "Expected to get the same number of mods input."); - Assert.AreSame(modB.Object, mods[0], "The load order is incorrect: mod B should be first since it's the only mod."); + mods.Should().HaveCount(1, "should match number of mods input"); + mods[0].Should().BeSameAs(modB.Object, "mod B should be first since it's the only mod"); } diff --git a/src/SMAPI.Tests/Core/TranslationTests.cs b/src/SMAPI.Tests/Core/TranslationTests.cs index a52df607..8b9563a5 100644 --- a/src/SMAPI.Tests/Core/TranslationTests.cs +++ b/src/SMAPI.Tests/Core/TranslationTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using FluentAssertions; using NUnit.Framework; using StardewModdingAPI; using StardewModdingAPI.Framework; @@ -42,13 +43,12 @@ namespace SMAPI.Tests.Core Translation[]? translationList = helper.GetTranslations()?.ToArray(); // assert - Assert.AreEqual("en", helper.Locale, "The locale doesn't match the input value."); - Assert.AreEqual(LocalizedContentManager.LanguageCode.en, helper.LocaleEnum, "The locale enum doesn't match the input value."); - Assert.IsNotNull(translationList, "The full list of translations is unexpectedly null."); - Assert.AreEqual(0, translationList!.Length, "The full list of translations is unexpectedly not empty."); + helper.Locale.Should().Be("en"); + helper.LocaleEnum.Should().Be(LocalizedContentManager.LanguageCode.en); + translationList.Should().NotBeNull().And.BeEmpty(); - Assert.IsNotNull(translation, "The translation helper unexpectedly returned a null translation."); - Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value."); + translation.Should().NotBeNull(); + translation.ToString().Should().Be(this.GetPlaceholderText("key")); } [Test(Description = "Assert that the translation helper returns the expected translations correctly.")] @@ -70,8 +70,9 @@ namespace SMAPI.Tests.Core // assert foreach (string locale in expected.Keys) { - Assert.IsNotNull(actual[locale], $"The translations for {locale} is unexpectedly null."); - Assert.That(actual[locale], Is.EquivalentTo(expected[locale]).Using(this.CompareEquality), $"The translations for {locale} don't match the expected values."); + actual[locale].Should() + .NotBeNull($"the translations for {locale} should be set") + .And.BeEquivalentTo(expected[locale], $"the translations for {locale} should match the input values"); } } @@ -98,8 +99,9 @@ namespace SMAPI.Tests.Core // assert foreach (string locale in expected.Keys) { - Assert.IsNotNull(actual[locale], $"The translations for {locale} is unexpectedly null."); - Assert.That(actual[locale], Is.EquivalentTo(expected[locale]).Using(this.CompareEquality), $"The translations for {locale} don't match the expected values."); + actual[locale].Should() + .NotBeNull($"the translations for {locale} should be set") + .And.BeEquivalentTo(expected[locale], $"the translations for {locale} should match the input values"); } } @@ -125,9 +127,9 @@ namespace SMAPI.Tests.Core // assert if (translation.HasValue()) - Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a valid input."); + translation.ToString().Should().Be(text, "the translation should match the valid input"); else - Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value given a null or empty input."); + translation.ToString().Should().Be(this.GetPlaceholderText("key"), "the translation should match the placeholder given a null or empty input"); } [Test(Description = "Assert that the translation's implicit string conversion returns the expected text for various inputs.")] @@ -138,9 +140,9 @@ namespace SMAPI.Tests.Core // assert if (translation.HasValue()) - Assert.AreEqual(text, (string?)translation, "The translation returned an unexpected value given a valid input."); + ((string?)translation).Should().Be(text, "the translation should match the valid input"); else - Assert.AreEqual(this.GetPlaceholderText("key"), (string?)translation, "The translation returned an unexpected value given a null or empty input."); + ((string?)translation).Should().Be(this.GetPlaceholderText("key"), "the translation should match the placeholder given a null or empty input"); } [Test(Description = "Assert that the translation returns the expected text given a use-placeholder setting.")] @@ -151,11 +153,11 @@ namespace SMAPI.Tests.Core // assert if (translation.HasValue()) - Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a valid input."); + translation.ToString().Should().Be(text, "the translation should match the valid input"); else if (!value) - Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a null or empty input with the placeholder disabled."); + translation.ToString().Should().Be(text, "the translation should return the text as-is given a null or empty input with the placeholder disabled"); else - Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value given a null or empty input with the placeholder enabled."); + translation.ToString().Should().Be(this.GetPlaceholderText("key"), "the translation should match the placeholder given a null or empty input with the placeholder enabled"); } [Test(Description = "Assert that the translation returns the expected text after setting the default.")] @@ -166,11 +168,11 @@ namespace SMAPI.Tests.Core // assert if (!string.IsNullOrEmpty(text)) - Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a valid base text."); + translation.ToString().Should().Be(text, "the translation should match the valid base text"); else if (!string.IsNullOrEmpty(@default)) - Assert.AreEqual(@default, translation.ToString(), "The translation returned an unexpected value given a null or empty base text, but valid default."); + translation.ToString().Should().Be(@default, "the translation should match the default text, given a null or empty base text and valid default."); else - Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value given a null or empty base and default text."); + translation.ToString().Should().Be(this.GetPlaceholderText("key"), translation.ToString(), "the translation should match the placeholder, given a null or empty base text and no default text"); } /**** @@ -211,7 +213,7 @@ namespace SMAPI.Tests.Core } // assert - Assert.AreEqual(expected, translation.ToString(), "The translation returned an unexpected text."); + translation.ToString().Should().Be(expected); } [Test(Description = "Assert that the translation can replace tokens in all valid formats.")] @@ -231,7 +233,7 @@ namespace SMAPI.Tests.Core Translation translation = new Translation("pt-BR", "key", text).Tokens(new Dictionary { [key] = value }); // assert - Assert.AreEqual(value, translation.ToString(), "The translation returned an unexpected value given a valid base text."); + translation.ToString().Should().Be(value); } [Test(Description = "Assert that translation tokens are case-insensitive and surrounding-whitespace-insensitive.")] @@ -247,7 +249,7 @@ namespace SMAPI.Tests.Core Translation translation = new Translation("pt-BR", "key", text).Tokens(new Dictionary { [key] = value }); // assert - Assert.AreEqual(value, translation.ToString(), "The translation returned an unexpected value given a valid base text."); + translation.ToString().Should().Be(value); } @@ -261,8 +263,8 @@ namespace SMAPI.Tests.Core private void AssertSetLocale(TranslationHelper helper, string locale, LocalizedContentManager.LanguageCode localeEnum) { helper.SetLocale(locale, localeEnum); - Assert.AreEqual(locale, helper.Locale, "The locale doesn't match the input value."); - Assert.AreEqual(localeEnum, helper.LocaleEnum, "The locale enum doesn't match the input value."); + helper.Locale.Should().Be(locale); + helper.LocaleEnum.Should().Be(localeEnum); } /// Get sample raw translations to input. @@ -314,14 +316,6 @@ namespace SMAPI.Tests.Core return expected; } - /// Get whether two translations have the same public values. - /// The first translation to compare. - /// The second translation to compare. - private bool CompareEquality(Translation a, Translation b) - { - return a.Key == b.Key && a.ToString() == b.ToString(); - } - /// Get the default placeholder text when a translation is missing. /// The translation key. private string GetPlaceholderText(string key) diff --git a/src/SMAPI.Tests/Utilities/KeybindListTests.cs b/src/SMAPI.Tests/Utilities/KeybindListTests.cs index c5fd5daf..489df35f 100644 --- a/src/SMAPI.Tests/Utilities/KeybindListTests.cs +++ b/src/SMAPI.Tests/Utilities/KeybindListTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using FluentAssertions; using NUnit.Framework; using StardewModdingAPI; using StardewModdingAPI.Utilities; @@ -24,11 +25,10 @@ namespace SMAPI.Tests.Utilities bool success = KeybindList.TryParse($"{button}", out KeybindList? parsed, out string[] errors); // assert - Assert.IsTrue(success, "Parsing unexpectedly failed."); - Assert.IsNotNull(parsed, "The parsed result should not be null."); - Assert.AreEqual(parsed!.ToString(), $"{button}"); - Assert.IsNotNull(errors, message: "The errors should never be null."); - Assert.IsEmpty(errors, message: "The input bindings incorrectly reported errors."); + success.Should().BeTrue(); + parsed.Should().NotBeNull(); + parsed!.ToString().Should().Be(button.ToString()); + errors.Should().NotBeNull().And.BeEmpty(); } /// Assert the parsed fields when constructed from multi-key values. @@ -50,10 +50,10 @@ namespace SMAPI.Tests.Utilities bool success = KeybindList.TryParse(input, out KeybindList? parsed, out string[] errors); // assert - Assert.IsTrue(success, "Parsing unexpectedly failed."); - Assert.IsNotNull(parsed, "The parsed result should not be null."); - Assert.IsNotNull(errors, message: "The errors should never be null."); - Assert.IsEmpty(errors, message: "The input bindings incorrectly reported errors."); + success.Should().BeTrue(); + parsed.Should().NotBeNull(); + errors.Should().NotBeNull().And.BeEmpty(); + return parsed!.ToString(); } @@ -70,10 +70,9 @@ namespace SMAPI.Tests.Utilities bool success = KeybindList.TryParse(input, out KeybindList? parsed, out string[] errors); // assert - Assert.IsFalse(success, "Parsing unexpectedly succeeded."); - Assert.IsNull(parsed, "The parsed result should be null."); - Assert.IsNotNull(errors, message: "The errors should never be null."); - Assert.AreEqual(expectedError, string.Join("; ", errors), "The errors don't match the expected ones."); + success.Should().BeFalse(); + parsed.Should().BeNull(); + errors.Should().BeEquivalentTo(errors); } @@ -110,10 +109,10 @@ namespace SMAPI.Tests.Utilities } // assert - Assert.IsTrue(success, "Parsing unexpected failed"); - Assert.IsNotNull(parsed, "The parsed result should not be null."); - Assert.IsNotNull(errors, message: "The errors should never be null."); - Assert.IsEmpty(errors, message: "The input bindings incorrectly reported errors."); + success.Should().BeTrue(); + parsed.Should().NotBeNull(); + errors.Should().NotBeNull().And.BeEmpty(); + return parsed!.GetState(); } diff --git a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs index 3219d89d..6687ce42 100644 --- a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs +++ b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; +using FluentAssertions; using NUnit.Framework; using StardewModdingAPI.Toolkit.Utilities; @@ -152,7 +153,9 @@ namespace SMAPI.Tests.Utilities string[] segments = PathUtilities.GetSegments(path.OriginalPath); // assert - Assert.AreEqual(path.Segments, segments); + path.Segments.Should() + .HaveCount(segments.Length) + .And.ContainInOrder(segments); } [Test(Description = "Assert that PathUtilities.GetSegments splits paths correctly when given a limit.")] @@ -163,7 +166,9 @@ namespace SMAPI.Tests.Utilities string[] segments = PathUtilities.GetSegments(path.OriginalPath, 3); // assert - Assert.AreEqual(path.SegmentsLimit3, segments); + path.SegmentsLimit3.Should() + .HaveCount(segments.Length) + .And.ContainInOrder(segments); } /**** @@ -180,7 +185,7 @@ namespace SMAPI.Tests.Utilities string normalized = PathUtilities.NormalizeAssetName(path.OriginalPath); // assert - Assert.AreEqual(path.NormalizedOnUnix, normalized); // MonoGame uses the Linux format + normalized.Should().Be(path.NormalizedOnUnix); // MonoGame uses the Linux format } /**** @@ -194,11 +199,13 @@ namespace SMAPI.Tests.Utilities string normalized = PathUtilities.NormalizePath(path.OriginalPath); // assert + normalized.Should().Be( #if SMAPI_FOR_WINDOWS - Assert.AreEqual(path.NormalizedOnWindows, normalized); + path.NormalizedOnWindows #else - Assert.AreEqual(path.NormalizedOnUnix, normalized); + path.NormalizedOnUnix #endif + ); } /**** diff --git a/src/SMAPI.Tests/Utilities/SDateTests.cs b/src/SMAPI.Tests/Utilities/SDateTests.cs index e2ee6238..7f39b011 100644 --- a/src/SMAPI.Tests/Utilities/SDateTests.cs +++ b/src/SMAPI.Tests/Utilities/SDateTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; +using FluentAssertions; using NUnit.Framework; using StardewModdingAPI.Utilities; using StardewValley; @@ -67,11 +68,11 @@ namespace SMAPI.Tests.Utilities SDate date = new(day, season, year); // assert - Assert.AreEqual(day, date.Day); - Assert.AreEqual(expectedSeason, date.Season); - Assert.AreEqual((int)expectedSeason, date.SeasonIndex); - Assert.AreEqual(Utility.getSeasonKey(expectedSeason), date.SeasonKey); - Assert.AreEqual(year, date.Year); + date.Day.Should().Be(day); + date.Season.Should().Be(expectedSeason); + date.SeasonIndex.Should().Be((int)expectedSeason); + date.SeasonKey.Should().Be(Utility.getSeasonKey(expectedSeason)); + date.Year.Should().Be(year); } [Test(Description = "Assert that the constructor throws an exception if the values are invalid.")] @@ -86,7 +87,9 @@ namespace SMAPI.Tests.Utilities public void Constructor_RejectsInvalidValues(int day, string season, int year) { // act & assert - Assert.Throws(() => _ = new SDate(day, season, year), "Constructing the invalid date didn't throw the expected exception."); + FluentActions + .Invoking(() => _ = new SDate(day, season, year)) + .Should().Throw(); } /**** @@ -111,7 +114,9 @@ namespace SMAPI.Tests.Utilities public void FromDaysSinceStart_RejectsInvalidValues(int daysSinceStart) { // act & assert - Assert.Throws(() => _ = SDate.FromDaysSinceStart(daysSinceStart), "Passing the invalid number of days didn't throw the expected exception."); + FluentActions + .Invoking(() => _ = SDate.FromDaysSinceStart(daysSinceStart)) + .Should().Throw(); } /**** @@ -241,7 +246,9 @@ namespace SMAPI.Tests.Utilities public void AddDays_RejectsInvalidValues(string dateStr, int addDays) { // act & assert - Assert.Throws(() => _ = this.GetDate(dateStr).AddDays(addDays), "Passing the invalid number of days didn't throw the expected exception."); + FluentActions + .Invoking(() => _ = this.GetDate(dateStr).AddDays(addDays)) + .Should().Throw(); } diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs index 77c0da5f..b8764ac3 100644 --- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using FluentAssertions; using Newtonsoft.Json; using NUnit.Framework; using StardewModdingAPI; @@ -91,7 +92,9 @@ namespace SMAPI.Tests.Utilities [TestCase("1.2.3.4-some-tag.4 ")] public void Constructor_FromString_Standard_DisallowsNonStandardVersion(string input) { - Assert.Throws(() => _ = new SemanticVersion(input)); + FluentActions + .Invoking(() => _ = new SemanticVersion(input)) + .Should().Throw(); } /// Assert the parsed version when constructed from standard parts. @@ -277,7 +280,10 @@ namespace SMAPI.Tests.Utilities : null; // assert - Assert.AreEqual(versionA.IsOlderThan(versionB), versionA.IsOlderThan(versionB?.ToString()), "The two signatures returned different results."); + bool olderThanVersion = versionA.IsOlderThan(versionB); + bool olderThanString = versionA.IsOlderThan(versionB?.ToString()); + olderThanVersion.Should().Be(olderThanString, "comparing to a version or string should return the same result"); + return versionA.IsOlderThan(versionB); } @@ -326,7 +332,10 @@ namespace SMAPI.Tests.Utilities : null; // assert - Assert.AreEqual(versionA.IsNewerThan(versionB), versionA.IsNewerThan(versionB?.ToString()), "The two signatures returned different results."); + bool newerThanVersion = versionA.IsNewerThan(versionB); + bool newerThanString = versionA.IsNewerThan(versionB?.ToString()); + newerThanVersion.Should().Be(newerThanString, "comparing to a version or string should return the same result"); + return versionA.IsNewerThan(versionB); } @@ -366,7 +375,10 @@ namespace SMAPI.Tests.Utilities ISemanticVersion version = new SemanticVersion(versionStr); // assert - Assert.AreEqual(version.IsBetween(lower, upper), version.IsBetween(lower?.ToString(), upper?.ToString()), "The two signatures returned different results."); + bool betweenVersions = version.IsBetween(lower, upper); + bool betweenStrings = version.IsBetween(lower?.ToString(), upper?.ToString()); + betweenVersions.Should().Be(betweenStrings, "comparing to a version or string should return the same result"); + return version.IsBetween(lower, upper); } @@ -385,8 +397,8 @@ namespace SMAPI.Tests.Utilities SemanticVersion? after = JsonConvert.DeserializeObject(json); // assert - Assert.IsNotNull(after, "The semantic version after deserialization is unexpectedly null."); - Assert.AreEqual(versionStr, after!.ToString(), "The semantic version after deserialization doesn't match the input version."); + after.Should().NotBeNull(); + after!.ToString().Should().Be(versionStr); } @@ -419,7 +431,7 @@ namespace SMAPI.Tests.Utilities GameVersion version = new(versionStr); // assert - Assert.AreEqual(versionStr, version.ToString(), "The game version did not round-trip to the same value."); + version.ToString().Should().Be(versionStr, "the game version should round-trip to the same value"); } @@ -436,12 +448,21 @@ namespace SMAPI.Tests.Utilities /// Whether the version should be marked as non-standard. private void AssertParts(ISemanticVersion version, int major, int minor, int patch, string? prerelease, string? build, bool nonStandard) { - Assert.AreEqual(major, version.MajorVersion, "The major version doesn't match."); - Assert.AreEqual(minor, version.MinorVersion, "The minor version doesn't match."); - Assert.AreEqual(patch, version.PatchVersion, "The patch version doesn't match."); - Assert.AreEqual(string.IsNullOrWhiteSpace(prerelease) ? null : prerelease.Trim(), version.PrereleaseTag, "The prerelease tag doesn't match."); - Assert.AreEqual(string.IsNullOrWhiteSpace(build) ? null : build.Trim(), version.BuildMetadata, "The build metadata doesn't match."); - Assert.AreEqual(nonStandard, version.IsNonStandard(), $"The version is incorrectly marked {(nonStandard ? "standard" : "non-standard")}."); + version.MajorVersion.Should().Be(major); + version.MinorVersion.Should().Be(minor); + version.PatchVersion.Should().Be(patch); + + if (string.IsNullOrWhiteSpace(prerelease)) + version.PrereleaseTag.Should().BeNull(); + else + version.PrereleaseTag.Should().Be(prerelease.Trim()); + + if (string.IsNullOrWhiteSpace(build)) + version.BuildMetadata.Should().BeNull(); + else + version.BuildMetadata.Should().Be(build.Trim()); + + version.IsNonStandard().Should().Be(nonStandard); } /// Assert that the expected exception type is thrown, and log the action output and thrown exception. diff --git a/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs b/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs index 8e7e1fb8..0bf685a7 100644 --- a/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs +++ b/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using FluentAssertions; using NUnit.Framework; using StardewModdingAPI; using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; @@ -36,10 +37,10 @@ namespace SMAPI.Tests.WikiClient ChangeDescriptor parsed = ChangeDescriptor.Parse(rawDescriptor, out string[] errors); // assert - Assert.That(parsed.Add, Is.EquivalentTo(expectedAdd), $"{nameof(parsed.Add)} doesn't match the expected value."); - Assert.That(parsed.Remove, Is.EquivalentTo(expectedRemove), $"{nameof(parsed.Replace)} doesn't match the expected value."); - Assert.That(parsed.Replace, Is.EquivalentTo(expectedReplace), $"{nameof(parsed.Replace)} doesn't match the expected value."); - Assert.That(errors, Is.EquivalentTo(expectedErrors), $"{nameof(errors)} doesn't match the expected value."); + parsed.Add.Should().BeEquivalentTo(expectedAdd); + parsed.Remove.Should().BeEquivalentTo(expectedRemove); + parsed.Replace.Should().BeEquivalentTo(expectedReplace); + errors.Should().BeEquivalentTo(expectedErrors); } [Test(Description = "Assert that Parse sets the expected values for descriptors when a format callback is specified.")] @@ -69,10 +70,10 @@ namespace SMAPI.Tests.WikiClient ); // assert - Assert.That(parsed.Add, Is.EquivalentTo(expectedAdd), $"{nameof(parsed.Add)} doesn't match the expected value."); - Assert.That(parsed.Remove, Is.EquivalentTo(expectedRemove), $"{nameof(parsed.Replace)} doesn't match the expected value."); - Assert.That(parsed.Replace, Is.EquivalentTo(expectedReplace), $"{nameof(parsed.Replace)} doesn't match the expected value."); - Assert.That(errors, Is.EquivalentTo(expectedErrors), $"{nameof(errors)} doesn't match the expected value."); + parsed.Add.Should().BeEquivalentTo(expectedAdd); + parsed.Remove.Should().BeEquivalentTo(expectedRemove); + parsed.Replace.Should().BeEquivalentTo(expectedReplace); + errors.Should().BeEquivalentTo(expectedErrors); } [Test(Description = "Assert that Apply returns the expected value for the given descriptor.")] @@ -111,7 +112,7 @@ namespace SMAPI.Tests.WikiClient { ChangeDescriptor parsed = ChangeDescriptor.Parse(descriptor, out string[] errors); - Assert.IsEmpty(errors, "Parsing the descriptor failed."); + errors.Should().BeEmpty(); return parsed.ApplyToCopy(input); } @@ -128,7 +129,7 @@ namespace SMAPI.Tests.WikiClient { var parsed = ChangeDescriptor.Parse(descriptor, out string[] errors); - Assert.IsEmpty(errors, "Parsing the descriptor failed."); + errors.Should().BeEmpty(); return parsed.ToString(); }