diff --git a/Testing/Advanced/Advanced.csproj b/Testing/Advanced/Advanced.csproj index 1a7435e..355ffea 100644 --- a/Testing/Advanced/Advanced.csproj +++ b/Testing/Advanced/Advanced.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -7,16 +7,16 @@ - - + + - - - - - - - + + + + + + + diff --git a/Testing/Basic/Basic.csproj b/Testing/Basic/Basic.csproj index d23dca2..c4f36ef 100644 --- a/Testing/Basic/Basic.csproj +++ b/Testing/Basic/Basic.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -7,16 +7,16 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs b/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs index d544c47..7df95b5 100644 --- a/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs +++ b/Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs @@ -1,31 +1,119 @@ -using NUnit.Framework; +using System.Runtime.CompilerServices; +using FluentAssertions; +using NUnit.Framework; using NUnit.Framework.Legacy; namespace HomeExercise.Tasks.ObjectComparison; public class ObjectComparison { - [Test] - [Description("Проверка текущего царя")] - [Category("ToRefactor")] - public void CheckCurrentTsar() + #region test data + private static Person currentTsar = TsarRegistry.GetCurrentTsar(); + private static Person tsarCopy = new Person("Ivan IV The Terrible", 54, 170, 70, new Person("Vasili III of Russia", 28, 170, 60, null)); + private static Person tsarCopyDifferent = new Person("Ivan IV The Kind", 54, 170, 70, new Person("Vasili III of Russia", 28, 170, 60, null)); + + private static Person person0Parents = new Person("1", 1, 1, 1, null); + private static Person person0ParentsCopy = person0Parents.Copy()!; + private static Person person0ParentsLimits = new Person(string.Empty, int.MaxValue, int.MinValue, 0, null); + private static Person person0ParentsLimitsCopy = person0ParentsLimits.Copy()!; + private static Person person1Parents = new Person("1", 1, 1, 1, new Person("2", 2, 2, 2, null)); + private static Person person1ParentsСopy = person1Parents.Copy()!; + private static Person person1ParentsDifferent = new Person("1", 1, 1, 1, new Person("3", 3, 3, 3, null)); + private static Person person2Parents = new Person("1", 1, 1, 1, new Person("2", 2, 2, 2, new Person("3", 3, 3, 3, null))); + private static Person person2ParentsCopy = person2Parents.Copy()!; + private static Person person2ParentsDifferent = new Person("1", 1, 1, 1, new Person("2", 2, 2, 2, new Person("5", 5, 5, 5, null))); + #endregion + + private static IEnumerable FieldCompare_SuccessCases() { - var actualTsar = TsarRegistry.GetCurrentTsar(); + yield return new TestCaseData(null, null).SetName("Пустые аргументы"); + yield return new TestCaseData(currentTsar, tsarCopy).SetName("Проверка царя"); + yield return new TestCaseData(person0Parents, person0ParentsCopy).SetName("Нет родителя"); + yield return new TestCaseData(person1Parents, person1ParentsСopy).SetName("Есть родитель"); + yield return new TestCaseData(person0Parents, person1ParentsСopy).SetName("Разные родители"); + yield return new TestCaseData(person2Parents, person2ParentsDifferent).SetName("Разные прародители"); + yield return new TestCaseData(person0ParentsLimits, person0ParentsLimitsCopy).SetName("Проверка с граничными значениями полей"); + } - var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70, - new Person("Vasili III of Russia", 28, 170, 60, null)); + [Test, TestCaseSource(nameof(FieldCompare_SuccessCases))] + [Description("Проверка Person по полям - поля равны")] + public void AreEqual_NotThrows_OnEqualFields(Person? actual, Person? expected) + { + actual.Should().BeEquivalentTo(expected, options => + options + .Excluding(t => t.Id) + .Excluding(t => t.Parent) + ); + } + + private static IEnumerable FieldCompare_FailCases() + { + yield return new TestCaseData(currentTsar, null).SetName("Сравнение с null"); + yield return new TestCaseData(currentTsar, tsarCopyDifferent).SetName("Проверка неправильного царя"); + yield return new TestCaseData(currentTsar, person0Parents).SetName("Полностью разные"); + yield return new TestCaseData(person0ParentsLimits, person0Parents).SetName("Просто разные"); + } - // Перепишите код на использование 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); + [Test, TestCaseSource(nameof(FieldCompare_FailCases))] + [Description("Проверка Person по полям - поля разные")] + public void AreEqual_Throws_OnDifferentFields(Person? actual, Person? expected) + { + actual.Should().NotBeEquivalentTo(expected, options => + options + .Excluding(t => t.Id) + .Excluding(t => t.Parent) + ); + } - 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); + private static IEnumerable FullCompare_SuccessCases() + { + yield return new TestCaseData(null, null).SetName("Пустые аргументы при полном сравнении"); + yield return new TestCaseData(currentTsar, tsarCopy).SetName("Проверка царя при полном сравнении"); + yield return new TestCaseData(person0Parents, person0ParentsCopy).SetName("Нет родителя при полном сравнении"); + yield return new TestCaseData(person1Parents, person1ParentsСopy).SetName("Есть родитель при полном сравнении"); + yield return new TestCaseData(person2Parents, person2ParentsCopy).SetName("Родитель у родителя при полном сравнении"); } + + [Test, TestCaseSource(nameof(FullCompare_SuccessCases))] + [Description("Проверка Person по Parent - Parent равны")] + public void AreEqual_NotThrows_OnEqualPersons(Person? actual, Person? expected) + { + actual.Should().BeEquivalentTo(expected, options => + options + .Excluding(t => t.Id) + .Excluding(t => t.Path.EndsWith("Id")) + ); + } + + private static IEnumerable FullCompare_FailCases() + { + yield return new TestCaseData(currentTsar, null).SetName("Сравнение с null при полном сравнении"); + yield return new TestCaseData(currentTsar, tsarCopyDifferent).SetName("Проверка неправильного царя при полном сравнении"); + yield return new TestCaseData(currentTsar, person1Parents).SetName("Полностью разные при полном сравнении"); + yield return new TestCaseData(person1Parents, person0Parents).SetName("Родитель null при полном сравнении"); + yield return new TestCaseData(person1Parents, person1ParentsDifferent).SetName("Разные родители при полном сравнении"); + yield return new TestCaseData(person2Parents, person2ParentsDifferent).SetName("Разные прародители при полном сравнении"); + } + + [Test, TestCaseSource(nameof(FullCompare_FailCases))] + [Description("Проверка Person по Parent - Parent различны")] + public void AreEqual_Throw_OnDifferentPersons(Person? actual, Person? expected) + { + actual.Should().NotBeEquivalentTo(expected, options => + options + .Excluding(t => t.Id) + .Excluding(t => t.Path.EndsWith("Id")) + ); + } + + // А что так можно было что ли? + + // При добавлении новых полей в Person придется расширять условие в return еще больше; + // Тест должен пройти по всем Parent прежде чем дать результат, + // в моем тесте при различии полей, он сразу прервется выкинув исключение; + // Я добавил TestCaseSource для добавления новых кейсов, в данном тесте данные собираются в нем; + // Использование FluentAssertions упрощает прочтение кода в моем тесте; + [Test] [Description("Альтернативное решение. Какие у него недостатки?")] public void CheckCurrentTsar_WithCustomEquality() @@ -42,6 +130,7 @@ private bool AreEqual(Person? actual, Person? expected) { if (actual == expected) return true; if (actual == null || expected == null) return false; + // Большой return return actual.Name == expected.Name && actual.Age == expected.Age diff --git a/Testing/Basic/Homework/1. ObjectComparison/Person.cs b/Testing/Basic/Homework/1. ObjectComparison/Person.cs index 4846867..458dd04 100644 --- a/Testing/Basic/Homework/1. ObjectComparison/Person.cs +++ b/Testing/Basic/Homework/1. ObjectComparison/Person.cs @@ -6,10 +6,10 @@ public class Person public static int IdCounter = 0; public int Age, Height, Weight; public string Name; - public Person Parent; + public Person? Parent; public int Id; - public Person(string name, int age, int height, int weight, Person parent) + public Person(string name, int age, int height, int weight, Person? parent) { Id = IdCounter++; Name = name; @@ -18,4 +18,16 @@ public Person(string name, int age, int height, int weight, Person parent) Weight = weight; Parent = parent; } + + public Person? Copy() => Copy(this)!; + + public static Person? Copy(Person? source) + { + if (source is null) + { + return null; + } + return new Person(source.Name, source.Age, source.Height, source.Weight, Person.Copy(source.Parent!)!); + } + } \ No newline at end of file diff --git a/Testing/Basic/Homework/2. NumberValidator/NumberValidator.cs b/Testing/Basic/Homework/2. NumberValidator/NumberValidator.cs index 327ce9c..55c3c61 100644 --- a/Testing/Basic/Homework/2. NumberValidator/NumberValidator.cs +++ b/Testing/Basic/Homework/2. NumberValidator/NumberValidator.cs @@ -17,7 +17,7 @@ public NumberValidator(int precision, int scale = 0, bool onlyPositive = false) if (precision <= 0) throw new ArgumentException("precision must be a positive number"); if (scale < 0 || scale >= precision) - throw new ArgumentException("precision must be a non-negative number less or equal than precision"); + throw new ArgumentException("scale must be a non-negative number less or equal than precision"); numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase); } diff --git a/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs b/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs index 950c9bc..99d9a61 100644 --- a/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs +++ b/Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs @@ -1,4 +1,6 @@  +using System.Runtime.CompilerServices; +using FluentAssertions; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -7,25 +9,113 @@ namespace HomeExercise.Tasks.NumberValidator; [TestFixture] public class NumberValidatorTests { + #region Constructor tests + + private static IEnumerable Constructor_MustThrow_OnPrecisionExceed() + { + yield return new TestCaseData(0, 0, false).SetName("Некорректная длина"); + yield return new TestCaseData(-1, -1, false).SetName("Некорректная длина"); + yield return new TestCaseData(-2, 2, true).SetName("Некорректная длина"); + yield return new TestCaseData(0, -3, false).SetName("Некорректная длина"); + yield return new TestCaseData(-5, -5, false).SetName("Некорректная длина"); + } + + private static IEnumerable Constructor_MustThrow_OnScaleExceed() + { + yield return new TestCaseData(1, 2, true).SetName("Некорректная точность"); + yield return new TestCaseData(1, 1, false).SetName("Некорректная точность"); + yield return new TestCaseData(1, -1, false).SetName("Некорректная точность"); + } + + private static IEnumerable Constructor_NotThrow_OnValidArguments() + { + yield return new TestCaseData(1, 0, true).SetName("Корректные аргументы"); + yield return new TestCaseData(int.MaxValue, int.MaxValue-1, true).SetName("Корректные аргументы"); + } + [Test] - public void Test() - { - 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")); + [ + TestCaseSource(nameof(Constructor_MustThrow_OnPrecisionExceed)), + TestCaseSource(nameof(Constructor_MustThrow_OnScaleExceed)) + ] + public void Constructor_Throws_OnInvalidArgument(int precision, int scale, bool onlyPositive) + { + Action action = () => new NumberValidator(precision, scale, onlyPositive); + action.Should().Throw(); + } + + [Test, TestCaseSource(nameof(Constructor_NotThrow_OnValidArguments))] + public void Constructor_NotThrows_OnValidArgument(int precision, int scale, bool onlyPositive) + { + Action action = () => new NumberValidator(precision, scale, onlyPositive); + action.Should().NotThrow(); + } + + #endregion + + #region IsValidNumber tests + + private static IEnumerable IsValidNumbers_ValidNumbersCases() + { + yield return new TestCaseData(17, 2, true, new string[] { "0.0", "0", "012322121.23" }).SetName("Правильные числа"); + + yield return new TestCaseData(4, 2, false, new string[] { "-1,23", "12,32" }).SetName("Правильные числа"); + + yield return new TestCaseData(4, 2, true, new string[] { "+1,23", "32" }).SetName("Правильные числа"); + + yield return new TestCaseData(20, 19, false, new string[] { "1,1234567890123456789", "1123456789012345678.9" }).SetName("Правильные числа"); } + + private static IEnumerable IsValidNumbers_TooBigNumbersCases() + { + yield return new TestCaseData(3, 2, false, new string[] { "00.00", "-0.00", "+0.00", "01,24" }).SetName("Слишком большие числа"); + + yield return new TestCaseData(19, 18, false, new string[] { "1.1234567890123456789", "12345678912345678900.01" }).SetName("Слишком большие числа"); + + } + private static IEnumerable IsValidNumbers_WithWrongSignCases() + { + yield return new TestCaseData(4, 2, true, new string[] { "-0.00", "-1.23" }).SetName("Числа с неправильным знаком");; + + yield return new TestCaseData(4, 3, true, new string[] { ".000", "+.124" }).SetName("Числа без целой части");; + } + + private static IEnumerable IsValidNumbers_WithLiteralsCases() + { + yield return new TestCaseData(4, 2, true, new string[] { "O.00", "_0.00"}).SetName("Некорректные символы"); + + yield return new TestCaseData(17, 2, false, new string[] {"0.sd00", "OIOIIOIO"}).SetName("Некорректные символы");; + + yield return new TestCaseData(19, 18, false, new string[] {"1,12 0123456789", "1678912345678900 . 01", "#!@$:|><1,2?%^&*()"}).SetName("Некорректные символы");; + + } + + [Test, TestCaseSource(nameof(IsValidNumbers_ValidNumbersCases))] + public void IsValidNumber_IsTrue_OnCorrectNumber(int precision, int scale, bool onlyPositive, string[] numbers) + { + var validator = new NumberValidator(precision, scale, onlyPositive); + + foreach (var number in numbers) + { + validator.IsValidNumber(number).Should().BeTrue(); + } + } + + [Test] + [ + TestCaseSource(nameof(IsValidNumbers_TooBigNumbersCases)), + TestCaseSource(nameof(IsValidNumbers_WithLiteralsCases)), + TestCaseSource(nameof(IsValidNumbers_WithWrongSignCases)) + ] + public void IsValidNumber_IsFalse_OnIncorrectNumber(int precision, int scale, bool onlyPositive, string[] numbers) + { + var validator = new NumberValidator(precision, scale, onlyPositive); + + foreach (var number in numbers) + { + validator.IsValidNumber(number).Should().BeFalse(); + } + } + + #endregion } \ No newline at end of file