Skip to content
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)}"))
.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'
*/

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