diff --git a/cs/HomeExercises/NumberValidatorTests.cs b/cs/HomeExercises/NumberValidator.cs similarity index 64% rename from cs/HomeExercises/NumberValidatorTests.cs rename to cs/HomeExercises/NumberValidator.cs index a2878113..d0f5fb80 100644 --- a/cs/HomeExercises/NumberValidatorTests.cs +++ b/cs/HomeExercises/NumberValidator.cs @@ -1,34 +1,9 @@ using System; using System.Text.RegularExpressions; -using FluentAssertions; -using NUnit.Framework; namespace HomeExercises { - public class NumberValidatorTests - { - [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)); - - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0")); - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("00.00")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-0.00")); - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+0.00")); - Assert.IsTrue(new NumberValidator(4, 2, true).IsValidNumber("+1.23")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+1.23")); - Assert.IsFalse(new NumberValidator(17, 2, true).IsValidNumber("0.000")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-1.23")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("a.sd")); - } - } + // тесты в Tests/NumberValues_Should public class NumberValidator { diff --git a/cs/HomeExercises/ObjectComparison.cs b/cs/HomeExercises/ObjectComparison.cs index 44d9aed4..e4657006 100644 --- a/cs/HomeExercises/ObjectComparison.cs +++ b/cs/HomeExercises/ObjectComparison.cs @@ -1,57 +1,7 @@ -using FluentAssertions; -using NUnit.Framework; - -namespace HomeExercises +namespace HomeExercises { - public class ObjectComparison - { - [Test] - [Description("Проверка текущего царя")] - [Category("ToRefactor")] - public void CheckCurrentTsar() - { - 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. - Assert.AreEqual(actualTsar.Name, expectedTsar.Name); - Assert.AreEqual(actualTsar.Age, expectedTsar.Age); - Assert.AreEqual(actualTsar.Height, expectedTsar.Height); - Assert.AreEqual(actualTsar.Weight, expectedTsar.Weight); - - Assert.AreEqual(expectedTsar.Parent!.Name, actualTsar.Parent!.Name); - Assert.AreEqual(expectedTsar.Parent.Age, actualTsar.Parent.Age); - Assert.AreEqual(expectedTsar.Parent.Height, actualTsar.Parent.Height); - Assert.AreEqual(expectedTsar.Parent.Parent, actualTsar.Parent.Parent); - } - - [Test] - [Description("Альтернативное решение. Какие у него недостатки?")] - public void CheckCurrentTsar_WithCustomEquality() - { - 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)); - - // Какие недостатки у такого подхода? - Assert.True(AreEqual(actualTsar, expectedTsar)); - } - - private bool AreEqual(Person? actual, Person? expected) - { - if (actual == expected) return true; - if (actual == null || expected == null) return false; - return - actual.Name == expected.Name - && actual.Age == expected.Age - && actual.Height == expected.Height - && actual.Weight == expected.Weight - && AreEqual(actual.Parent, expected.Parent); - } - } - + // Тесты в Tests/ObjectComparison_Should + public class TsarRegistry { public static Person GetCurrentTsar() diff --git a/cs/HomeExercises/Tests/NumberValidator_Should.cs b/cs/HomeExercises/Tests/NumberValidator_Should.cs new file mode 100644 index 00000000..5d252ba8 --- /dev/null +++ b/cs/HomeExercises/Tests/NumberValidator_Should.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections; +using FluentAssertions; +using NUnit.Framework; + +namespace HomeExercises.Tests +{ + public class NumberValidator_Should + { + #region ConstructorTestsSources + + private static IEnumerable IncorrectConstructorParamsTests() + { + yield return new TestCaseData(-1, 2, true, "precision must be a positive number") + .SetName("Constructor_ThrowsArgumentExceptionOnNegativePrecision"); + yield return new TestCaseData(0, 2, true, "precision must be a positive number") + .SetName("Constructor_ThrowsArgumentExceptionOnZeroPrecision"); + yield return new TestCaseData(1, 2, true, "precision must be a non-negative number less or equal than precision") + .SetName("Constructor_ThrowsArgumentExceptionOnPrecisionLessThanScale"); + yield return new TestCaseData(1, 1, true, "precision must be a non-negative number less or equal than precision") + .SetName("Constructor_ThrowsArgumentExceptionSamePrecisionAndScale"); + yield return new TestCaseData(1, -1, true, "precision must be a non-negative number less or equal than precision") + .SetName("Constructor_ThrowsArgumentExceptionOnNegativeScale"); + } + + private static IEnumerable CorrectConstructorParamsTests() + { + yield return new TestCaseData(2, 1, true) + .SetName("Constructor_WorksWhenPrecisionIsNonNegativeAndGreaterThanScale"); + yield return new TestCaseData(2, 1, false) + .SetName("Constructor_WorksWhenPrecisionIsNonNegativeAndGreaterThanScale"); + } + + #endregion + + #region IsValidNumberTestsSources + + private static IEnumerable IsValidNumberPrecisionTests() + { + yield return new TestCaseData(3, 2, true, "00.00") + .SetName("IsValidNumber_ReturnsFalse_WhenSumOfIntPartAndFracPartIsGreaterThanPrecision") + .Returns(false); + yield return new TestCaseData(3, 2, true, "-0.00") + .SetName("IsValidNumber_ReturnsFalse_WhenSignWithFracAndIntPartsIsGreaterThanPrecision") + .Returns(false); + yield return new TestCaseData(3, 2, true, "+0.00") + .SetName("IsValidNumber_ReturnsFalse_WhenSignWithFracAndIntPartsIsGreaterThanPrecision") + .Returns(false); + + yield return new TestCaseData(17, 2, true, "0.0") + .SetName("IsValidNumber_ReturnsTrue_WhenSumOfIntPartAndFracPartIsNotGreaterThanPrecision") + .Returns(true); + yield return new TestCaseData(17, 2, true, "0") + .SetName("IsValidNumber_ReturnsTrue_WhenIntPartIsNotGreaterThanPrecision") + .Returns(true); + yield return new TestCaseData(17, 2, true, "+0.0") + .SetName("IsValidNumber_ReturnsTrue_WhenSumOfIntPartAndFracPartIsNotGreaterThanPrecision") + .Returns(true); + } + + private static IEnumerable IsValidNumberScaleTests() + { + yield return new TestCaseData(17, 2, true, "0.111") + .SetName("IsValidNumber_ReturnsFalse_WhenFracPartIsGreaterThanScale") + .Returns(false); + + yield return new TestCaseData(17, 2, true, "0.11") + .SetName("IsValidNumber_ReturnsTrue_WhenFracPartIsNotGreaterThanScale") + .Returns(true); + } + + private static IEnumerable IsValidNumberSignTests() + { + yield return new TestCaseData(17, 2, true, "-0.11") + .SetName("IsValidNumber_ReturnsFalse_WhenSignIsNegativeWithOnlyPositive") + .Returns(false); + + yield return new TestCaseData(17, 2, true, "+0.11") + .SetName("IsValidNumber_ReturnsTrue_WhenSignIsPositiveWithOnlyPositive") + .Returns(true); + yield return new TestCaseData(17, 2, false, "+0.11") + .SetName("IsValidNumber_ReturnsTrue_WhenSignIsPositiveWithoutOnlyPositive") + .Returns(true); + yield return new TestCaseData(17, 2, false, "-0.11") + .SetName("IsValidNumber_ReturnsTrue_WhenSignIsNegativeWithoutOnlyPositive") + .Returns(true); + } + + private static IEnumerable IsValidNumberValueTests() + { + yield return new TestCaseData(17, 2, true, null) + .SetName("IsValidNumber_ReturnsFalse_WhenNullIsGiven") + .Returns(false); + yield return new TestCaseData(17, 2, true, "") + .SetName("IsValidNumber_ReturnsFalse_WhenEmptyStringIsGiven") + .Returns(false); + yield return new TestCaseData(17, 2, true, "a.a") + .SetName("IsValidNumber_ReturnsFalse_WhenNonDigitStringIsGiven") + .Returns(false); + } + + #endregion + + [TestCaseSource(nameof(IncorrectConstructorParamsTests))] + public void FailsWithIncorrectConstructorArguments(int precision, int scale, bool onlyPositive, string message) + { + new Func(() => new NumberValidator(precision, scale, onlyPositive)) + .Should() + .ThrowExactly() + .Where(e => e.Message.Equals(message, StringComparison.OrdinalIgnoreCase)); + } + + [TestCaseSource(nameof(CorrectConstructorParamsTests))] + public void InitsWithCorrectConstructorArguments(int precision, int scale, bool onlyPositive) + { + new Func(() => new NumberValidator(precision, scale, onlyPositive)) + .Should() + .NotThrow(); + } + + [ + TestCaseSource(nameof(IsValidNumberPrecisionTests)), + TestCaseSource(nameof(IsValidNumberScaleTests)), + TestCaseSource(nameof(IsValidNumberSignTests)), + TestCaseSource(nameof(IsValidNumberValueTests)), + ] + public bool IsValidNumber(int precision, int scale, bool onlyPositive, string value) + { + return new Func(() => new NumberValidator(precision, scale, onlyPositive))() + .IsValidNumber(value); + } + } +} \ No newline at end of file diff --git a/cs/HomeExercises/Tests/ObjectComparsion_Should.cs b/cs/HomeExercises/Tests/ObjectComparsion_Should.cs new file mode 100644 index 00000000..d5ef0e8f --- /dev/null +++ b/cs/HomeExercises/Tests/ObjectComparsion_Should.cs @@ -0,0 +1,64 @@ +using FluentAssertions; +using FluentAssertions.Equivalency; +using NUnit.Framework; + +namespace HomeExercises.Tests +{ + public class ObjectComparison_Should + { + private Person actualTsar; + private Person expectedTsar; + + [SetUp] + public void SetUp() + { + actualTsar = TsarRegistry.GetCurrentTsar(); + expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70, + new Person("Vasili III of Russia", 28, 170, 60, null)); + } + + [Test] + [Description("Проверка текущего царя")] + [Category("ToRefactor")] + public void CheckCurrentTsar() + { + actualTsar.Should().BeEquivalentTo(expectedTsar, options => + options + .IncludingFields() + .Excluding(memberInfo => + memberInfo.SelectedMemberInfo.Name == nameof(Person.Id) + && memberInfo.SelectedMemberInfo.DeclaringType == typeof(Person))); + } + + [Test] + [Description("Альтернативное решение. Какие у него недостатки?")] + public void CheckCurrentTsar_WithCustomEquality() + { + Assert.True(AreEqual(actualTsar, expectedTsar)); + + // Какие недостатки у такого подхода? + // 1. При большой родословной будут проверяться все предки. И, при наличии расхождения у родителей, нам будет сложно понять в каком объекте произошло падение теста + // 2. Из названия теста непонятно, что именно значит CustomEquality и какое у него поведение + // 3. Не показывается информация, на каком именно объекте тест упал, что усложняет разработку + // 4. При расширении класса Person нужно будет дописывать AreEqual, а в FluentAssertions достаточно указать IncludingFields в конфигурации и все поля автоматически будут сравниваться + // 5. Повышается читабельность и тесты проще писать + // 7. Не инкапсулирована функциональность сравнения внутри класса + + // Улучшайзинги + // 1. Также я вынес инициализацию объектов в метод SetUp, помеченный соответствующим атрибутом + // 2. Для структурированности проекта вынес тесты в папку Tests + } + + private bool AreEqual(Person? actual, Person? expected) + { + if (actual == expected) return true; + if (actual == null || expected == null) return false; + return + actual.Name == expected.Name + && actual.Age == expected.Age + && actual.Height == expected.Height + && actual.Weight == expected.Weight + && AreEqual(actual.Parent, expected.Parent); + } + } +} \ No newline at end of file