Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 123 additions & 19 deletions cs/HomeExercises/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Linq;
using System.Text.RegularExpressions;
using FluentAssertions;
using NUnit.Framework;
Expand All @@ -7,26 +9,128 @@ namespace HomeExercises
{
public class NumberValidatorTests
{
[Test]
public void Test()
#region TestCases

public static object[][] numberTestCases =
Copy link

@razor2651 razor2651 Nov 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

такой подход неинформативен, из названия не понятно какой кейс проверяется пока не залезешь в код и не посмотришь что поменялось
image

во-первых: в TestCaseData можно указать имя, тем самым четко обозначив кейс
во-вторых: тут у тебя генерация кейсов на лету, это не очень наглядно с точки зрения спецификации, чтобы понять откуда кейс взялся придется дебажиться или изучать код

может быть для мелких задач вроде таких такой контекст и легко восстанавливать почитав код, но на более сложных такое уже потребует постоянных усилий

{
new object[] { 1, 0, "0" },
new object[] { 2, 1, "0.0" },
new object[] { 1, 0, "1" },
new object[] { 2, 0, "12" },
new object[] { 2, 1, "1.2" },
new object[] { 3, 2, "1.23" },
new object[] { 3, 1, "12.3" },
new object[] { 4, 2, "12.34" },
};

public static IEnumerable CorrectSignedNumbersCases()
{
int precision, scale;
string validatingString;
foreach (var testCase in numberTestCases)
{
precision = (int)testCase[0];
scale = (int)testCase[1];
validatingString = (string)testCase[2];
yield return new TestCaseData(precision + 1, scale, $"+{validatingString}").Returns(true);
yield return new TestCaseData(precision + 1, scale, $"-{validatingString}").Returns(true);
}
}

public static IEnumerable CorrectUnsignedNumbersCases()
{
foreach (var testCase in numberTestCases)
{
yield return new TestCaseData(testCase).Returns(true);
}
}

public static IEnumerable IncorrectUnsignedNumbersCases()
{
int precision, scale;
string validatingString;
foreach (var testCase in numberTestCases)
{
precision = (int)testCase[0];
scale = (int)testCase[1];
validatingString = (string)testCase[2];
if (precision > 1 && precision - 1 > scale)
yield return new TestCaseData(precision - 1, scale, validatingString).Returns(false);
if (scale > 0)
yield return new TestCaseData(testCase[0], scale - 1, validatingString).Returns(false);
}
}

public static IEnumerable IncorrectSignedNumbersCases()
{
int precision, scale;
string validatingString;
foreach (var testCase in numberTestCases)
{
precision = (int)testCase[0];
scale = (int)testCase[1];
validatingString = (string)testCase[2];
yield return new TestCaseData(precision, scale, $"+{validatingString}").Returns(false);
yield return new TestCaseData(precision, scale, $"-{validatingString}").Returns(false);
if (scale > 0 && precision > scale)
{
yield return new TestCaseData(precision + 1, scale - 1, $"+{validatingString}")
.Returns(false);
yield return new TestCaseData(precision + 1, scale - 1, $"-{validatingString}")
.Returns(false);
}
}
}

public static IEnumerable NegativeNumbers()
{
int precision;
string validatingString;
foreach (var testCase in numberTestCases)
{
precision = (int)testCase[0];
validatingString = (string)testCase[2];
yield return new TestCaseData(precision + 1, testCase[1], $"-{validatingString}").Returns(false);
}
}

#endregion

[TestCaseSource(nameof(CorrectSignedNumbersCases))]
[TestCaseSource(nameof(CorrectUnsignedNumbersCases))]
[TestCaseSource(nameof(IncorrectUnsignedNumbersCases))]
[TestCaseSource(nameof(IncorrectSignedNumbersCases))]
public bool ValidationTests(int precision, int scale, string validatingString)
{
return new NumberValidator(precision, scale).IsValidNumber(validatingString);
}

[TestCaseSource(nameof(NegativeNumbers))]
public bool ShouldFailWhenNumberNegativeWithOnlyPositive(int precision, int scale, string validatingString)
{
return new NumberValidator(precision, scale, true).IsValidNumber(validatingString);
}

[TestCase(3, 2, "a.sd", TestName = "non digit symbols")]
[TestCase(2, 1, ".0", TestName = "must have digits before point")]
[TestCase(1, 0, "0.", TestName = "must have digits after point (if exist)")]
public void IsNotValid(int precision, int scale, string validatingString, bool onlyPositive = true)

This comment was marked as outdated.

{
new NumberValidator(precision, scale, onlyPositive)
.IsValidNumber(validatingString)
.Should()
.BeFalse();
}

[TestCase(-1, 1, TestName = "negative precision")]
[TestCase(1, -1, TestName = "negative scale")]
[TestCase(-1, -1, TestName = "negative precision and scale")]
[TestCase(1, 1, TestName = "precision equals scale")]
[TestCase(1, 2, TestName = "precision less than scale")]
public void ShouldThrow(int precision, int scale, bool onlyPositive = true)
{
Assert.Throws<ArgumentException>(() => new NumberValidator(-1, 2, true));
Assert.DoesNotThrow(() => new NumberValidator(1, 0, true));
Assert.Throws<ArgumentException>(() => 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"));
Action act = () => new NumberValidator(precision, scale, onlyPositive);
act.Should().Throw<ArgumentException>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это можно проще написать через Assert.Throws<>( () => ...)

}
}

Expand Down
28 changes: 18 additions & 10 deletions cs/HomeExercises/ObjectComparison.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FluentAssertions;
using System;
using FluentAssertions;
using FluentAssertions.Equivalency;
using NUnit.Framework;

namespace HomeExercises
Expand All @@ -16,15 +18,13 @@ public void CheckCurrentTsar()
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);
actualTsar.Should().BeEquivalentTo(expectedTsar, options =>
options
.IgnoringCyclicReferences()
.Excluding((IMemberInfo info) =>
info.SelectedMemberInfo.DeclaringType == typeof(Person)
&& info.SelectedMemberInfo.Name == nameof(Person.Id))
);
}

[Test]
Expand All @@ -35,7 +35,15 @@ public void CheckCurrentTsar_WithCustomEquality()
var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));


// Какие недостатки у такого подхода?

// 1. Вероятность StackOverflow из-за бесконечной рекурсии
// 2. При каждом изменении свойств или добавлении новых функцию придётся переписывать
// 3. Нарушение SRP, ответственность за сравнение объетов должна быть инкапсулирована в класс
// 4. Тест не показывает, по каким критериям различаются два объекта
// 5. Название теста не говорит о конкрутных различиях этого теста с предыдущим

Assert.True(AreEqual(actualTsar, expectedTsar));

This comment was marked as outdated.

}

Expand Down