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

Горбатов Александр #227

Open
wants to merge 5 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
2 changes: 1 addition & 1 deletion cs/HomeExercises/HomeExercises.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>HomeExercises</RootNamespace>
<AssemblyName>ObjectComparison</AssemblyName>
<LangVersion>8</LangVersion>
<Nullable>enable</Nullable>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
using System;
using System.Text.RegularExpressions;
using FluentAssertions;
using NUnit.Framework;

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 static readonly Regex NumberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private readonly bool onlyPositive;
private readonly int precision;
private readonly int scale;
Expand All @@ -45,8 +18,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");
numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase);
throw new ArgumentException("scale must be a non-negative number less than precision");
}

public bool IsValidNumber(string value)
Expand All @@ -60,7 +32,7 @@ public bool IsValidNumber(string value)
if (string.IsNullOrEmpty(value))
return false;

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

Expand Down
67 changes: 9 additions & 58 deletions cs/HomeExercises/ObjectComparison.cs
Original file line number Diff line number Diff line change
@@ -1,58 +1,6 @@
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);
}
}

public class TsarRegistry
public static class TsarRegistry
{
public static Person GetCurrentTsar()
{
Expand All @@ -64,15 +12,18 @@ public static Person GetCurrentTsar()

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


public Person(string name, int age, int height, int weight, Person? parent)
{
Id = IdCounter++;
Id = _idCounter++;
Name = name;
Age = age;
Height = height;
Expand Down
1 change: 1 addition & 0 deletions cs/HomeExercisesTests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using NUnit.Framework;
24 changes: 24 additions & 0 deletions cs/HomeExercisesTests/HomeExercisesTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1"/>
<PackageReference Include="NUnit" Version="3.13.3"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2"/>
<PackageReference Include="NUnit.Analyzers" Version="3.6.1"/>
<PackageReference Include="coverlet.collector" Version="3.2.0"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\HomeExercises\HomeExercises.csproj" />
</ItemGroup>

</Project>
92 changes: 92 additions & 0 deletions cs/HomeExercisesTests/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using FluentAssertions;
using HomeExercises;

namespace HomeExercisesTests
{
public class NumberValidatorTests
{
[TestCase(-1, 0, Description = "Precision should be a positive number")]
[TestCase(1, -1, Description = "Scale must be a non-negative number")]
[TestCase(1, 2, Description = "Scale must be less than precision")]
public void Constructor_ThrowsArgumentException_OnIncorrectInput(int precision, int scale)
{
Assert.Throws<ArgumentException>(() => new NumberValidator(precision, scale, true));
}

[Test]
public void Constructor_SuccessfullyCreatesObject_OnCorrectInput()
{
Assert.DoesNotThrow(() => new NumberValidator(1, 0, true));
}

[TestCase(null)]
[TestCase(" ")]
[TestCase(" ")]
[TestCase("")]
[TestCase("\n")]
public void IsValidNumber_ReturnsFalse_WhenValueIsNullOrEmpty(string value)
{
var numberValidator = new NumberValidator(1, 0, true);

numberValidator.IsValidNumber(value).Should().BeFalse();
}

[TestCase(".")]
[TestCase("a.sd")]
[TestCase("+-5")]
[TestCase("1..3")]
[TestCase("1.")]
[TestCase(".3")]
public void IsValidNumber_ReturnsFalse_WhenValueIsNaN(string value)
{
var numberValidator = new NumberValidator(2, 1, true);

numberValidator.IsValidNumber(value).Should().BeFalse();
}

[TestCase("00.00", 3, 2)]
[TestCase("+1.00", 3, 2)]
[TestCase("-1.00", 3, 2)]
[TestCase("-1", 1, 0)]
public void IsValidNumber_ReturnsFalse_WhenIntAndFracPartsAreGreaterThanPrecision(string value, int precision,
int scale)
{
var numberValidator = new NumberValidator(precision, scale);

numberValidator.IsValidNumber(value).Should().BeFalse();
}

[TestCase("0.00", 3, 1)]
[TestCase("-123.12", 6, 0)]
public void IsValidNumber_ReturnsFalse_WhenFracPartIsGreaterThanScale(string value, int precision, int scale)
{
var numberValidator = new NumberValidator(precision, scale);

numberValidator.IsValidNumber(value).Should().BeFalse();
}

[TestCase("-1.2", 3, 1)]
[TestCase("-0.00", 4, 2)]
public void IsValidNumber_ReturnsFalse_WhenValueIsNegativeButOnlyPositiveIsTrue(string value, int precision,
int scale)
{
var numberValidator = new NumberValidator(precision, scale, true);

numberValidator.IsValidNumber(value).Should().BeFalse();
}

[TestCase("0.0", 17, 2, true)]
[TestCase("0.0", 2, 1, false)]
[TestCase("+1.23", 4, 2, true)]
[TestCase("-1.23", 4, 2, false)]
[TestCase("0,1", 2, 1, true)]
[TestCase("0", 1, 0, true)]
public void IsValidNumber_ReturnsTrue_OnCorrectInputData(string value, int precision, int scale,
bool onlyPositive)
{
var numberValidator = new NumberValidator(precision, scale, onlyPositive);

numberValidator.IsValidNumber(value).Should().BeTrue();
}
}
}
66 changes: 66 additions & 0 deletions cs/HomeExercisesTests/ObjectComparisonTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using FluentAssertions;
using HomeExercises;

namespace HomeExercisesTests
{
public class ObjectComparisonTests
{
[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));

/*
* Чем это решение лучше решения в CheckCurrentTsar_WithCustomEquality:
* 1. Это решение не нужно будет переписывать при добавлении/удалении в класс/из класса Person полей.
* 2. В этом решении наглядно видно, какое именно поле не учитывается при сравнении объектов.
* 3. При падении теста мы увидим, какие именно поля объектов не прошли проверку на равенство.
*/
actualTsar
.Should()
.BeEquivalentTo(expectedTsar, options => options
.Excluding(info =>
info.SelectedMemberPath.Equals(nameof(Person.Id)) ||
info.SelectedMemberPath.EndsWith($"{nameof(Person.Parent)}.{nameof(Person.Id)}"))
LevShisterov marked this conversation as resolved.
Show resolved Hide resolved
.IgnoringCyclicReferences()
.AllowingInfiniteRecursion());
}

[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));

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

/*
* 1. Если мы решим изменить класс Person (например, добавить или удалить какие-то поля),
* то тогда придется переписывать метод AreEqual
* 2. Если тест не пройдет, то мы не увидим, какие именно поля у двух объектов не совпали.
* Нам просто выдаст сообщение 'Expected: True, But was: False'
LevShisterov marked this conversation as resolved.
Show resolved Hide resolved
*/

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

private static 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);
}
}
}
6 changes: 6 additions & 0 deletions cs/testing.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HomeExercises", "HomeExerci
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Challenge", "Challenge\Challenge.csproj", "{BB8C2EFB-6AE9-45BD-8468-829D5AB79DCB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HomeExercisesTests", "HomeExercisesTests\HomeExercisesTests.csproj", "{224B11F4-2395-4280-8F7A-6E193AD3504E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +29,10 @@ Global
{BB8C2EFB-6AE9-45BD-8468-829D5AB79DCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB8C2EFB-6AE9-45BD-8468-829D5AB79DCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB8C2EFB-6AE9-45BD-8468-829D5AB79DCB}.Release|Any CPU.Build.0 = Release|Any CPU
{224B11F4-2395-4280-8F7A-6E193AD3504E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{224B11F4-2395-4280-8F7A-6E193AD3504E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{224B11F4-2395-4280-8F7A-6E193AD3504E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{224B11F4-2395-4280-8F7A-6E193AD3504E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down