diff --git a/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs b/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs index d544c47..60cd548 100644 --- a/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs +++ b/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs @@ -1,31 +1,46 @@ -using NUnit.Framework; +using System.Text.RegularExpressions; +using FluentAssertions; +using FluentAssertions.Equivalency; +using NUnit.Framework; using NUnit.Framework.Legacy; namespace HomeExercise.Tasks.ObjectComparison; -public class ObjectComparison +public partial class ObjectComparison { [Test] [Description("Проверка текущего царя")] [Category("ToRefactor")] - public void CheckCurrentTsar() + public void CheckCurrentTsar_IgnoringIds() { var actualTsar = TsarRegistry.GetCurrentTsar(); var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70, new Person("Vasili III of Russia", 28, 170, 60, null)); - // Перепишите код на использование Fluent Assertions. - ClassicAssert.AreEqual(actualTsar.Name, expectedTsar.Name); - ClassicAssert.AreEqual(actualTsar.Age, expectedTsar.Age); - ClassicAssert.AreEqual(actualTsar.Height, expectedTsar.Height); - ClassicAssert.AreEqual(actualTsar.Weight, expectedTsar.Weight); - - ClassicAssert.AreEqual(expectedTsar.Parent!.Name, actualTsar.Parent!.Name); - ClassicAssert.AreEqual(expectedTsar.Parent.Age, actualTsar.Parent.Age); - ClassicAssert.AreEqual(expectedTsar.Parent.Height, actualTsar.Parent.Height); - ClassicAssert.AreEqual(expectedTsar.Parent.Parent, actualTsar.Parent.Parent); + //можно оставить так, но вдруг появиться свойство, название которого будет начинаться с Id, или иметь Id в середине наименования. + /*actualTsar + .Should() + .BeEquivalentTo(expectedTsar, opt => + opt.Excluding(info => info.Path.EndsWith("Id")));*/ + + actualTsar + .Should() + .BeEquivalentTo(expectedTsar, opt => + opt.Excluding((IMemberInfo memberInfo) => MyRegex().IsMatch(memberInfo.Name))); } - + + + /* + * 1. Если упадет тест мы увидим сообщение из разряда: + * Expected: True + * But was: False + * что не является информативным сообщением, и мы не поймем в чем ошибка конкретно. + * 2. Методе AreEqual реализует то, что уже имеют библиотеки, например .BeEquivalentTo, т.е. мы изобретаем велосипед заново. + * 3. Возвращаем булевый результат, из за чего теряем контекст ошибки(аналогично п.1) + * 4. Метод жестко проверяет свойство parent, и если добавить новое свойство, например Child, тогда тест будет падать, либо нужно будет добавлять новые проверки + * 5. Если добавлять впринципе новые свойства/поля тест упадет, или нужно будет добавлять новые проверки + * 6. Может уйти в бесконечную рекурсию, если ссылка parent будет идти обратно к исходному обьекту. + */ [Test] [Description("Альтернативное решение. Какие у него недостатки?")] public void CheckCurrentTsar_WithCustomEquality() @@ -49,4 +64,7 @@ private bool AreEqual(Person? actual, Person? expected) && actual.Weight == expected.Weight && AreEqual(actual.Parent, expected.Parent); } + + [GeneratedRegex(@"(^Id\b|\bId$)")] + private static partial Regex MyRegex(); } diff --git a/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs b/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs index 950c9bc..03539c7 100644 --- a/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs +++ b/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs @@ -1,31 +1,157 @@ - +using FluentAssertions; using NUnit.Framework; -using NUnit.Framework.Legacy; +using System.Collections; namespace HomeExercise.Tasks.NumberValidator; [TestFixture] public class NumberValidatorTests { - [Test] - public void Test() + #region Конструктор + + [Test, TestCaseSource(nameof(InvalidArguments_NegativeOrScaleTooBig))] + public void Constructor_ShouldThrow_WhenArgumentsAreNegativeOrScaleAtLeastPrecision(int precision, int scale, + string expectedMessage) + { + var act = () => new NumberValidator(precision, scale); + act + .Should() + .Throw($"{expectedMessage}. " + + $"Параметры: precision={precision}, scale={scale}"); + } + + [TestCase(1, 0, true)] + [TestCase(10, 5, false)] + public void Constructor_DoesNotThrow_WhenPrecisionScaleAndOnlyPositiveAreValid(int precision, int scale, + bool onlyPositive) + { + var act = () => new NumberValidator(precision, scale, onlyPositive); + act + .Should() + .NotThrow($"precision={precision}, scale={scale}, onlyPositive={onlyPositive} допустимы"); + } + + #endregion + + #region Валидация чисел + + [TestCaseSource(nameof(ValidNumbers))] + public void IsValidNumber_ShouldReturnTrue_ForValidNumbers(int precision, int scale, bool onlyPositive, + string input, string expectedMessage) + { + var validator = new NumberValidator(precision, scale, onlyPositive); + validator + .IsValidNumber(input) + .Should() + .BeTrue($"{input}. {expectedMessage}. " + $"Параметры: precision={precision}, scale={scale}, " + + $"onlyPositive={onlyPositive}"); + } + + [TestCaseSource(nameof(InvalidNumbers))] + public void IsValidNumber_ShouldReturnFalse_ForInvalidNumbers(int precision, int scale, bool onlyPositive, + string input, string expectedMessage) { - Assert.Throws(() => new NumberValidator(-1, 2, true)); - Assert.DoesNotThrow(() => new NumberValidator(1, 0, true)); - Assert.Throws(() => new NumberValidator(-1, 2, false)); - Assert.DoesNotThrow(() => new NumberValidator(1, 0, true)); - - ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0")); - ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("00.00")); - ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-0.00")); - ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+0.00")); - ClassicAssert.IsTrue(new NumberValidator(4, 2, true).IsValidNumber("+1.23")); - ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+1.23")); - ClassicAssert.IsFalse(new NumberValidator(17, 2, true).IsValidNumber("0.000")); - ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-1.23")); - ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("a.sd")); + var validator = new NumberValidator(precision, scale, onlyPositive); + validator + .IsValidNumber(input) + .Should() + .BeFalse($"{input}. {expectedMessage}. " + $"Параметры: precision={precision}, scale={scale}, " + + $"onlyPositive={onlyPositive}"); + } + + #endregion + + private static IEnumerable InvalidNumbers() + { + yield return new TestCaseData(3, 2, true, "+0.00", "число превышает допустимое количество знаков") + .SetName("InvalidNumber_TooManyDigits_PositiveZero"); + + yield return new TestCaseData(7, 2, true, "-1.231", + "число превышает допустимое количество знаков после запятой") + .SetName("InvalidNumber_TooManyDecimals_Negative"); + + yield return new TestCaseData(3, 2, true, "a.sd", "содержит недопустимые символы") + .SetName("InvalidNumber_InvalidCharacters"); + + yield return new TestCaseData(3, 2, true, "", "пустая строка") + .SetName("InvalidNumber_EmptyString"); + + yield return new TestCaseData(3, 2, true, " ", "строка содержит только пробел") + .SetName("InvalidNumber_WhitespaceOnly"); + + yield return new TestCaseData(3, 2, true, "+", "строка содержит только знак без числа") + .SetName("InvalidNumber_SignOnlyPlus"); + + yield return new TestCaseData(3, 2, true, "-", "строка содержит только знак без числа") + .SetName("InvalidNumber_SignOnlyMinus"); + + yield return new TestCaseData(2, 0, true, "211", "число превышает допустимую длину целой части") + .SetName("InvalidNumber_IntegerTooLong"); + + yield return new TestCaseData(3, 0, true, "0.1", "число превышает допустимую длину дробной части") + .SetName("InvalidNumber_ScaleTooLong"); + + yield return new TestCaseData(17, 2, true, "0.000", "число превышает допустимую длину дробной части") + .SetName("InvalidNumber_ScaleTooLong2"); + + yield return new TestCaseData(3, 0, true, "0.-12", "недопустимый знак внутри числа") + .SetName("InvalidNumber_InternalSignMinus"); + + yield return new TestCaseData(3, 0, true, "0.+12", "недопустимый знак внутри числа") + .SetName("InvalidNumber_InternalSignPlus"); + + yield return new TestCaseData(3, 0, true, "0.12.12", "число содержит несколько разделителей") + .SetName("InvalidNumber_MultipleSeparators"); + + yield return new TestCaseData(3, 0, true, "0.,12.12", "число содержит несколько разделителей") + .SetName("InvalidNumber_MixedSeparators1"); + + yield return new TestCaseData(3, 0, true, "0.2,12", "число содержит несколько разделителей") + .SetName("InvalidNumber_MixedSeparators2"); + + yield return new TestCaseData(3, 0, true, "0,2.12", "число содержит несколько разделителей") + .SetName("InvalidNumber_MixedSeparators3"); + + yield return new TestCaseData(3, 2, true, null, "значение null недопустимо") + .SetName("InvalidNumber_NullValue"); + } + + + private static IEnumerable ValidNumbers() + { + yield return new TestCaseData( + 17, 2, true, "0.0", + "целая часть + дробная часть ≤ precision, дробная часть ≤ scale, число положительное" + ).SetName("ValidNumber_ZeroPointZero"); + + yield return new TestCaseData( + 4, 2, true, "+1.23", + "целая часть + дробная часть ≤ precision, дробная часть ≤ scale, число положительное" + ).SetName("ValidNumber_PositiveWithTwoDecimals"); + + yield return new TestCaseData( + 4, 2, false, "-1.23", + "целая часть + дробная часть ≤ precision, дробная часть ≤ scale, отрицательные числа разрешены" + ).SetName("ValidNumber_NegativeWithTwoDecimals"); + + yield return new TestCaseData( + 5, 0, true, "12345", + "целая часть + дробная часть ≤ precision, дробная часть ≤ scale, число положительное" + ).SetName("ValidNumber_Integer"); + } + + private static IEnumerable InvalidArguments_NegativeOrScaleTooBig() + { + yield return new TestCaseData(-1, 2, "отрицательный precision недопустим") + .SetName("Constructor_Invalid_NegativePrecision"); + + yield return new TestCaseData(1, -1, "отрицательный scale недопустим") + .SetName("Constructor_Invalid_NegativeScale"); + + yield return new TestCaseData(3, 3, "scale должен быть меньше чем precision") + .SetName("Constructor_Invalid_ScaleEqualsPrecision"); + + yield return new TestCaseData(3, 4, "scale должен быть меньше чем precision") + .SetName("Constructor_Invalid_ScaleGreaterThanPrecision"); } } \ No newline at end of file