Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
70 changes: 52 additions & 18 deletions Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using NUnit.Framework;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Legacy;

namespace HomeExercise.Tasks.ObjectComparison;
public class ObjectComparison

public class TsarComparison
{
[Test]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Неинформативное название и описание. Не ясно что проверяется. Давай поправим

[Description("Проверка текущего царя")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

категорию теста после рефакторинга можно убрать

Expand All @@ -11,31 +13,63 @@ 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(
name: "Ivan IV The Terrible",
age: 54,
height: 170,
weight: 70,
parent: new Person(
name: "Vasili III of Russia",
age: 28,
height: 170,
weight: 60,
parent: null!
)
);

// Перепишите код на использование Fluent Assertions.
ClassicAssert.AreEqual(actualTsar.Name, expectedTsar.Name);
ClassicAssert.AreEqual(actualTsar.Age, expectedTsar.Age);
ClassicAssert.AreEqual(actualTsar.Height, expectedTsar.Height);
ClassicAssert.AreEqual(actualTsar.Weight, expectedTsar.Weight);
/// <summary>
/// Такой подход автоматически проверяет все свойства объекта, включая вложенные
/// Тест не будет требовать изменений при добавлении новых свойств в класс Person
/// При падении теста, FluentAssertions предоставляет подробную информацию о том, какие именно свойства не совпали
/// </summary>

ClassicAssert.AreEqual(expectedTsar.Parent!.Name, actualTsar.Parent!.Name);
ClassicAssert.AreEqual(expectedTsar.Parent.Age, actualTsar.Parent.Age);
ClassicAssert.AreEqual(expectedTsar.Parent.Height, actualTsar.Parent.Height);
ClassicAssert.AreEqual(expectedTsar.Parent.Parent, actualTsar.Parent.Parent);
actualTsar.Should().BeEquivalentTo(expectedTsar, options => options.Excluding(t => t.Id).Excluding(t => t.Parent.Id));
}

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

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

var expectedTsar = new Person(
name: "Ivan IV The Terrible",
age: 54,
height: 170,
weight: 70,
new Person(
name: "Vasili III of Russia",
age: 28,
height: 170,
weight: 60,
parent: null!
)
);

// Какие недостатки у такого подхода?
// Недостатки подхода:
// 1) Требуется ручное обновление метода сравнения при добавлении новых свойств в класс Person,
// 2) Тест менее информативен при падении. Он просто укажет, что объекты не равны, не показывая какие именно свойства не совпали.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Могут ли быть проблемы с рекурсией?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Могут ли быть проблемы с рекурсией?

Да, попробовал сделать так, чтобы у обоих сравниваемых объектов родитель указывал на самого себя. В итоге метод сравнения убежал в бесконечную рекурсию:

image image

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

// Вызываем Stack Overflow exception
// var p1 = new Person("A", 1, 1, 1, null!);
// p1.Parent = p1;
//
// var p2 = new Person("A", 1, 1, 1, null!);
// p2.Parent = p2;
//
// ClassicAssert.True(AreEqual(p1, p2));
}

private bool AreEqual(Person? actual, Person? expected)
Expand All @@ -49,4 +83,4 @@ private bool AreEqual(Person? actual, Person? expected)
&& actual.Weight == expected.Weight
&& AreEqual(actual.Parent, expected.Parent);
}
}
}
220 changes: 199 additions & 21 deletions Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,209 @@

using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Legacy;

namespace HomeExercise.Tasks.NumberValidator;

[TestFixture]
public class NumberValidatorTests
{
#region Допустимая разрядность

[TestCase(-1, 2, true)]
[TestCase(0, 2, true)]
public void Ctor_InvalidPrecision_ThrowsArgumentException(int precision, int scale, bool onlyPositive)
{
var act = () => new NumberValidator(precision, scale, onlyPositive);
act.Should()
.Throw<ArgumentException>()
.WithMessage("precision must be a positive number");
}

#endregion

#region Некорректная дробность

[TestCase(5, -1, true)]
public void Ctor_InvalidScale_Negative_ThrowsArgumentException(int precision, int scale, bool onlyPositive)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Если TestCase один, то лучше не писать через TestCase, а написать просто тест

{
var act = () => new NumberValidator(precision, scale, onlyPositive);
act.Should()
.Throw<ArgumentException>()
.WithMessage("*non-negative*"); // узнал, что можно передавать часть строки
}

[TestCase(1, 2, true)]
[TestCase(1, 1, true)]
public void Ctor_WithScaleGreaterOrEqualToPrecision_ThrowsArgumentException(int precision, int scale, bool onlyPositive)
{
var act = () => new NumberValidator(precision, scale, onlyPositive);
act.Should()
.Throw<ArgumentException>()
.WithMessage("*less or equal*"); // узнал, что можно передавать часть строки
}

#endregion

#region Корректные аргументы для конструктора

[Test]
public void Ctor_ValidArguments_DoesNotThrow()
{
var act = () => new NumberValidator(1, 0, true);
act.Should().NotThrow();
}

#endregion

#region Передаваемое число в формате пустой строки или null

[TestCase("", 17, 2, true)]
[TestCase(null, 17, 2, true)]
public void IsValidNumber_NullOrEmpty_ReturnsFalse(string value, int precision, int scale, bool onlyPositive)
{
var validator = new NumberValidator(precision, scale, onlyPositive);
var result = validator.IsValidNumber(value);
result.Should().BeFalse();
}

#endregion

#region Некорректный формат передаваемого числа

[Test]
public void IsValidNumber_MultipleFractionSeparators_ReturnsFalse()
{
var validator = new NumberValidator(10, 4, true);
var result = validator.IsValidNumber("1..2");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_OnlySigns_ReturnsFalse()
{
var validator = new NumberValidator(15, 1, true);
var result = validator.IsValidNumber("+-.");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_OnlyDot_ReturnsFalse()
{
var validator = new NumberValidator(22, 8, true);
var result = validator.IsValidNumber(".");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_DotLastSign_ReturnsFalse()
{
var validator = new NumberValidator(9, 4, true);
var result = validator.IsValidNumber("1.");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_DotFirstSign_ReturnsFalse()
{
var validator = new NumberValidator(7, 3, true);
var result = validator.IsValidNumber(".1");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_ContainsSpace_ReturnsFalse()
{
var validator = new NumberValidator(17, 6, true);
var result = validator.IsValidNumber("5 2");
result.Should().BeFalse();
}

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

ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0"));
ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0"));
ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0"));
ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("00.00"));
ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-0.00"));
ClassicAssert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0"));
ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+0.00"));
ClassicAssert.IsTrue(new NumberValidator(4, 2, true).IsValidNumber("+1.23"));
ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+1.23"));
ClassicAssert.IsFalse(new NumberValidator(17, 2, true).IsValidNumber("0.000"));
ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-1.23"));
ClassicAssert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("a.sd"));
public void IsValidNumber_ContainsUnderscore_ReturnsFalse()
{
var validator = new NumberValidator(11, 3, true);
var result = validator.IsValidNumber("4_4");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_ContainsLettersAndUnderscore_ReturnsFalse()
{
var validator = new NumberValidator(10, 5, true);
var result = validator.IsValidNumber("abcd_4");
result.Should().BeFalse();
}

[Test]
public void IsValidNumber_DoubleMinus_ReturnsFalse()
{
var validator = new NumberValidator(12, 2, true);
var result = validator.IsValidNumber("--1");
result.Should().BeFalse();
}

#endregion

#region Корректное передаваемое число

[TestCase("+1", 4, 2, true)]
[TestCase("+0.1", 9, 1, true)]
[TestCase("000.10", 5, 2, true)]
[TestCase("000,10", 8, 4, true)]
[TestCase("10", 2, 0, true)]
public void IsValidNumber_ValidFormat_ReturnsTrue(string value, int precision, int scale, bool onlyPositive)
{
var validator = new NumberValidator(precision, scale, onlyPositive);
var result = validator.IsValidNumber(value);
result.Should().BeTrue();
}

#endregion

#region Количество допустимых цифр в числе, включая знак, целую и дробную часть, без точки

[TestCase("17.9", 3, 1, true, true)]
[TestCase("171.9", 3, 1, true, false)]
[TestCase("+17", 3, 2, true, true)]
[TestCase("+17", 2, 0, true, false)]
public void IsValidNumber_RespectsPrecisionLimit_ReturnsExpected(string value, int precision, int scale,
bool onlyPositive, bool expected)
{
var validator = new NumberValidator(precision, scale, onlyPositive);
var result = validator.IsValidNumber(value);
result.Should().Be(expected);
}

#endregion

#region Количество знаков после запятой

[TestCase("1.0", 10, 0, true, false)]
[TestCase("1.44", 10, 2, true, true)]
[TestCase("1.4", 10, 2, true, true)]
[TestCase("1,040", 10, 3, true, true)]
[TestCase("1.41414141", 10, 2, true, false)]
public void IsValidNumber_RespectsScale_ReturnsExpected(string value, int precision, int scale, bool onlyPositive,
bool expected)
{
var validator = new NumberValidator(precision, scale, onlyPositive);
var result = validator.IsValidNumber(value);
result.Should().Be(expected);
}

#endregion

#region Проверка параметра onlyPositive (только положительные)

[TestCase("-4.59", 4, 2, false, true)]
[TestCase("-6", 2, 0, true, false)]
[TestCase("+5", 2, 0, false, true)]
public void IsValidNumber_RespectsOnlyPositive_ReturnsExpected(string value, int precision, int scale, bool onlyPositive,
bool expected)
{
var validator = new NumberValidator(precision, scale, onlyPositive);
var result = validator.IsValidNumber(value);
result.Should().Be(expected);
}

#endregion
}