Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Юдин Павел #225

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
12 changes: 6 additions & 6 deletions cs/HomeExercises/HomeExercises.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>HomeExercises</RootNamespace>
<AssemblyName>ObjectComparison</AssemblyName>
Expand All @@ -10,11 +10,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

</Project>
186 changes: 114 additions & 72 deletions cs/HomeExercises/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,118 @@

namespace HomeExercises
{
public class NumberValidatorTests
{
[Test]
public void Test()
{
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"));
}
}

public class NumberValidator
{
private readonly Regex numberRegex;
private readonly bool onlyPositive;
private readonly int precision;
private readonly int scale;

public NumberValidator(int precision, int scale = 0, bool onlyPositive = false)
{
this.precision = precision;
this.scale = scale;
this.onlyPositive = onlyPositive;
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");
numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase);
}

public bool IsValidNumber(string value)
{
// Проверяем соответствие входного значения формату N(m,k), в соответствии с правилом,
// описанным в Формате описи документов, направляемых в налоговый орган в электронном виде по телекоммуникационным каналам связи:
// Формат числового значения указывается в виде N(m.к), где m – максимальное количество знаков в числе, включая знак (для отрицательного числа),
// целую и дробную часть числа без разделяющей десятичной точки, k – максимальное число знаков дробной части числа.
// Если число знаков дробной части числа равно 0 (т.е. число целое), то формат числового значения имеет вид N(m).

if (string.IsNullOrEmpty(value))
return false;

var match = numberRegex.Match(value);
if (!match.Success)
return false;

// Знак и целая часть
var intPart = match.Groups[1].Value.Length + match.Groups[2].Value.Length;
// Дробная часть
var fracPart = match.Groups[4].Value.Length;

if (intPart + fracPart > precision || fracPart > scale)
return false;

if (onlyPositive && match.Groups[1].Value == "-")
return false;
return true;
}
}
[TestFixture]
public class NumberValidatorTests
{

dmnovikova marked this conversation as resolved.
Show resolved Hide resolved

[TestCase(-1, 2, true, TestName = "Negative Precision")]
[TestCase(2, -1, true, TestName = "Negative Scale")]
[TestCase(2, 3, true, TestName = "Precision < Scale")]
[TestCase(3, 3, true, TestName = "Precision = Scale")]
[TestCase(2, 3, true, TestName = "Precision = Zero")]
[TestCase(null, 3, true, TestName = "Precision is null")]
[TestCase(null, 3, true, TestName = "Scale is null")]
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved

public void NumberValidator_CreateInvalidNumber_ShouldBeTrowException(int precision, int scale, bool onlyPositive)
{
Action createNumberValidator = () => new NumberValidator(precision, scale, onlyPositive);
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
createNumberValidator.Should().Throw<ArgumentException>();
}



[TestCase(2, 0, " ", TestName = "number is someWhiteSpaces")]
[TestCase(2, 0, "string.string", TestName = "number is word")]
[TestCase(2, 0, "", TestName = "Number is Empty")]
[TestCase(2, 0, null, TestName = "Number Is Null")]
[TestCase(4, 0, "-+20", TestName = "more than one sign")]
[TestCase(4, 2, "20?00", TestName = "invalid separator. It isn't '.' or ','")]
[TestCase(4, 2, "20......00", TestName = "more than one separators in a row")]
[TestCase(2, 0, "20.", TestName = "Number with separator, but Empty fractional part")]
[TestCase(1, 0, "+", TestName = "number with sign but without fractional and integer parts")]
[TestCase(4, 1, "2.0.0.0", TestName = "more than one separator")]
[TestCase(4, 1, "D75", TestName = "number in is not the correct number system")]
[TestCase(4, 1, " 200 ", TestName = "number surrounded by spaces")]

dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
public void IsValidNumber_IfInvalidTypeValue_ShouldBeFalse(int precision, int scale, string number, bool onlyPositive = true)
{
var numberValidator = new NumberValidator(precision, scale, onlyPositive);
numberValidator.IsValidNumber(number).Should().BeFalse();
}



[TestCase(5, 2, true, "-20.00", TestName = "incorrect sign")]
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
[TestCase(3, 1, true, "20.00", TestName = "precision < length number")]
[TestCase(4, 0, true, "20.00", TestName = "scale < length fractional part")]

public void IsValidNumber_IfIncorrectArgs_ShouldBeFalse(int precision, int scale, bool onlyPositive, string number)
{
var numberValidator = new NumberValidator(precision, scale, onlyPositive);
numberValidator.IsValidNumber(number).Should().BeFalse();
}


[TestCase(21, 1, true, "20.0", TestName = "precision > number Length")]
[TestCase(3, 2, true, "20.0", TestName = "scale > fractional part")]
[TestCase(3, 1, true, "20,0", TestName = "used ','")]
[TestCase(4, 1, true, "+20.0", TestName = "use '+' before number")]
[TestCase(4, 1, false, "+20.0", TestName = "use '+' without onlyPositive flag")]
[TestCase(2, 0, false, "20", TestName = "number without fractional part")]
[Test]
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
public void IsValidNumber_IfCorrectValue_ShouldBeTrue(int precision, int scale, bool onlyPositive, string number)
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
{
var numberValidator = new NumberValidator(precision, scale, onlyPositive);
numberValidator.IsValidNumber(number).Should().BeTrue();
}
}

public class NumberValidator
{
private readonly Regex numberRegex;
private readonly bool onlyPositive;
private readonly int precision;
private readonly int scale;

public NumberValidator(int precision, int scale = 0, bool onlyPositive = false)
{
this.precision = precision;
this.scale = scale;
this.onlyPositive = onlyPositive;
if (precision <= 0)
throw new ArgumentException("precision must be a positive number");
if (scale < 0 || scale >= precision)
throw new ArgumentException("scale must be a non-negative number less or equal than precision");
numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase);
}

public bool IsValidNumber(string value)
{
// Проверяем соответствие входного значения формату N(m,k), в соответствии с правилом,
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
// описанным в Формате описи документов, направляемых в налоговый орган в электронном виде по телекоммуникационным каналам связи:
// Формат числового значения указывается в виде N(m.к), где m – максимальное количество знаков в числе, включая знак (для отрицательного числа),
// целую и дробную часть числа без разделяющей десятичной точки, k – максимальное число знаков дробной части числа.
// Если число знаков дробной части числа равно 0 (т.е. число целое), то формат числового значения имеет вид N(m).

if (string.IsNullOrEmpty(value))
return false;

var match = numberRegex.Match(value);
if (!match.Success)
return false;

// Знак и целая часть
var intPart = match.Groups[1].Value.Length + match.Groups[2].Value.Length;
// Дробная часть
var fracPart = match.Groups[4].Value.Length;

if (intPart + fracPart > precision || fracPart > scale)
return false;

if (onlyPositive && match.Groups[1].Value == "-")
return false;
return true;
}
}
}
143 changes: 73 additions & 70 deletions cs/HomeExercises/ObjectComparison.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,81 +3,84 @@

namespace HomeExercises
{
public class ObjectComparison
{
[Test]
[Description("Проверка текущего царя")]
[Category("ToRefactor")]
public void CheckCurrentTsar()
{
var actualTsar = TsarRegistry.GetCurrentTsar();
public class ObjectComparison
{
private const int MAX_TREE_HEIGHT = 75;
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
[SetUp]
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
public void SetUp()
{
AssertionOptions.FormattingOptions.MaxDepth = MAX_TREE_HEIGHT;
}
[Test]
[Description("Проверка текущего царя")]
[Category("ToRefactor")]
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
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));
var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));
actualTsar.Should().
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
BeEquivalentTo(expectedTsar, options =>
options.Excluding(tsar => tsar.DeclaringType.Equals(typeof(Person)) && tsar.Name.Equals(nameof(Person.Id))));
}

// Перепишите код на использование 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);
[Test]
[Description("Альтернативное решение. Какие у него недостатки?")]
// размыта зона ответственности этого теста(если тест упадет из-за неверного поля,то не понятно из-за какого конкретно)
//Очень негибкий тест:
// Любое изменение существующих полей класса Person приводит к ошибке
// При любом добавлении новых полей нужно снова изменять этот тест
//
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.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);
}
// Какие недостатки у такого подхода?
Assert.True(AreEqual(actualTsar, expectedTsar));
}

[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));
private bool AreEqual(Person? actual, Person? expected)
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
{
if (actual == expected) return true;
if (actual == null || expected == null) return false;
return

Choose a reason for hiding this comment

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

хорошая практика перед return оставлять пустую строку
это повышает читаемость методов

actual.Name == expected.Name
&& actual.Age == expected.Age
&& actual.Height == expected.Height
&& actual.Weight == expected.Weight
&& AreEqual(actual.Parent, expected.Parent);
}
}

// Какие недостатки у такого подхода?
Assert.True(AreEqual(actualTsar, expectedTsar));
}
public class TsarRegistry
{
public static Person GetCurrentTsar()
{
return new Person(
"Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));
}
}

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);
}
}
public class Person
{
public static int IdCounter = 0;
public int Age, Height, Weight;
dmnovikova marked this conversation as resolved.
Show resolved Hide resolved
public string Name;
public Person? Parent;
public int Id;

public class TsarRegistry
{
public static Person GetCurrentTsar()
{
return new Person(
"Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));
}
}

public class Person
{
public static int IdCounter = 0;
public int Age, Height, Weight;
public string Name;
public Person? Parent;
public int Id;

public Person(string name, int age, int height, int weight, Person? parent)
{
Id = IdCounter++;
Name = name;
Age = age;
Height = height;
Weight = weight;
Parent = parent;
}
}
public Person(string name, int age, int height, int weight, Person? parent)
{
Id = IdCounter++;
Name = name;
Age = age;
Height = height;
Weight = weight;
Parent = parent;
}
}
}