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
13 changes: 13 additions & 0 deletions Testing/.idea/.idea.Testing/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Testing/.idea/.idea.Testing/.idea/indexLayout.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Testing/.idea/.idea.Testing/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 15 additions & 17 deletions Testing/Basic/Homework/1. ObjectComparison/ObjectComparison.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NUnit.Framework;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Legacy;

namespace HomeExercise.Tasks.ObjectComparison;
Expand All @@ -10,31 +11,28 @@ public class ObjectComparison
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.
ClassicAssert.AreEqual(actualTsar.Name, expectedTsar.Name);
ClassicAssert.AreEqual(actualTsar.Age, expectedTsar.Age);
ClassicAssert.AreEqual(actualTsar.Height, expectedTsar.Height);
ClassicAssert.AreEqual(actualTsar.Weight, expectedTsar.Weight);

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);
var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70, 99999999,
new Person("Vasili III of Russia", 28, 170, 60, 99999999, null));

actualTsar.Should()
.BeEquivalentTo(expectedTsar,
config => config
.Excluding(person => person.Id)
.Excluding(person => person.Parent.Id));
// Я специально для теста добавил одно поле, а сам тест остался неизменен. По сути, его нужно изменять только в случае, если нужно добавить какое-то поле в игнор

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

На будущее отмечу: в рамках учебных заданий это ОК, но в реальных проектах комментарии, адресованные ревьюеру, обычно оставляют в github/gitlab и т.д.

@Kpokoko Kpokoko Oct 30, 2025

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.

А как следует оставлять комментарии в github? В pull request их добавлять, или как-то иначе?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

В pull request. При этом оставлять поясняющие комментарии в коде - это нормальная практика, если ты хочешь, чтобы этот комментарий остался в коде "навсегда" и предназначался для всех разработчиков, с которыми ты работаешь над одним кодом. В нашем же случае ты оставлял комментарий для наставника - просто, чтобы я его увидел и имел ввиду. Такое лучше не оставлять в коде, а выносить в pull request (или merge request в случае с gitlab, с которым если ещё не сталкивался, то обязательно столкнешься)

// типа того, как я добавил айдишники людей, т. е. там скорее всего окажутся только поля, которые должны быть уникальными для каждого объекта, а таких вряд ли будет прям много
}

[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("Ivan IV The Terrible", 54, 170, 70, 99999999,
new Person("Vasili III of Russia", 28, 170, 60, 99999999, null));

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Давай ещё раз проведём сравнение и аккуратно, но развернуто распишем (оставь комментарий здесь или в коде):

  1. В чём плюсы/минусы изначального подхода в тесте CheckCurrentTsar?
  2. В чём плюсы/минусы альтернативного решения в CheckCurrentTsar_WithCustomEquality?
  3. В чём плюсы/минусы твоего решения?

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.

  1. Плюсом оригинального подхода можно назвать то, что такой тест можно быстро написать просто для того, чтобы оперативно и один раз убедиться, что что-то работает. Его проблема в том, что такой тест очень сложно поддерживать: если вдруг мы будем менять поля тестируемого класса, то нам придётся вручную добавлять эти поля в тесте. К тому же, такой тест не очень удобно читается из-за большого количества сравнений.
  2. Альтернативное решение оставляет в себе проблему расширяемости теста, поэтому нам также, как и раньше, придётся вручную добавлять или убирать поля при изменении тестируемого класса, но он более читаемый, потому что сравнение вынесено в отдельный метод. К тому же в этом решении сравнение вложенных экземпляров класса происходит через AreEqual, а не по каждому полю как в 1 варианте, что тоже явно плюс.
  3. В моём решении сравнение в тесте происходит при помощи метода из FluentAssertions, в котором объекты автоматически сравниваются по всем полям, поэтому тест будет работать даже если изменить поля тестируемых классов. Единственный минус - необходимость исключать те поля, которые будут уникальными для каждого объекта, чтобы сравнение их пропускало.

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

Expand Down
4 changes: 3 additions & 1 deletion Testing/Basic/Homework/1. ObjectComparison/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ public class Person
{
public static int IdCounter = 0;
public int Age, Height, Weight;
public uint Wealth;
public string Name;
public Person Parent;
public int Id;

public Person(string name, int age, int height, int weight, Person parent)
public Person(string name, int age, int height, int weight, uint wealth, Person parent)
{
Id = IdCounter++;
Name = name;
Age = age;
Height = height;
Weight = weight;
Parent = parent;
Wealth = wealth;
}
}
4 changes: 2 additions & 2 deletions Testing/Basic/Homework/1. ObjectComparison/TsarRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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));
"Ivan IV The Terrible", 54, 170, 70, 99999999,
new Person("Vasili III of Russia", 28, 170, 60, 99999999, null));
}
}
104 changes: 86 additions & 18 deletions Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

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

Expand All @@ -7,25 +8,92 @@ namespace HomeExercise.Tasks.NumberValidator;
[TestFixture]
public class NumberValidatorTests
{
[Test]
public void Test()
[Test, TestCaseSource(nameof(GenerateValidatorTestsWithExceptions))]
public void Tests_NumberValidator_WithException(int precision, int scale, bool onlyPositive)
{
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));
var validatorAction = () => new NumberValidator(precision, scale, onlyPositive);
validatorAction.Should().Throw<ArgumentException>();
}

public static IEnumerable<TestCaseData> GenerateValidatorTestsWithExceptions
{
get
{
var tests = new[]
{
new { precision = -1, scale = 2, onlyPositive = true},
new { precision = -1, scale = 2, onlyPositive = false},
new { precision = 1, scale = -1, onlyPositive = false},
new { precision = 1, scale = 10, onlyPositive = false},
};
foreach (var test in tests)
{
yield return new TestCaseData(test.precision, test.scale, test.onlyPositive)
.SetName($"Exception should be thrown with {test.precision} precision, {test.scale} scale, {test.onlyPositive} onlyPositive");
}
}
}

[Test, TestCaseSource(nameof(GenerateValidatorTestsWithoutExceptions))]
public void Tests_NumberValidator_WithoutException(int precision, int scale, bool onlyPositive)
{
var validatorAction = () => new NumberValidator(precision, scale, onlyPositive);
validatorAction.Should().NotThrow<ArgumentException>();
}

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 static IEnumerable<TestCaseData> GenerateValidatorTestsWithoutExceptions
{
get
{
var tests = new[]
{
new { precision = 1, scale = 0, onlyPositive = true},
};
foreach (var test in tests)
{
yield return new TestCaseData(test.precision, test.scale, test.onlyPositive)
.SetName($"Exception should be thrown with {test.precision} precision, {test.scale} scale, {test.onlyPositive} onlyPositive");
}
}
}

[Test, TestCaseSource(nameof(GenerateValidatorTestIsValidNumberMethod))]
public bool ValidatorTest_IsValidNumber_Method(int precision, int scale, bool onlyPositive, string validatingNumber)
{
var numberValidator = new NumberValidator(precision, scale, onlyPositive);
return numberValidator.IsValidNumber(validatingNumber);
}

public static IEnumerable<TestCaseData> GenerateValidatorTestIsValidNumberMethod
{
get
{
var tests = new[]
{
new { precision = 17, scale = 2, onlyPositive = true, validatingNumber = "0.0", expectingResult = true},
new { precision = 17, scale = 2, onlyPositive = true, validatingNumber = "0", expectingResult = true},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "00.00", expectingResult = false},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "-0.00", expectingResult = false},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "+0.00", expectingResult = false},
new { precision = 4, scale = 2, onlyPositive = true, validatingNumber = "1.23", expectingResult = true},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "+1.23", expectingResult = false},
new { precision = 17, scale = 2, onlyPositive = true, validatingNumber = "0.000", expectingResult = false},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "-1.23", expectingResult = false},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "a.sd", expectingResult = false},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "", expectingResult = false},
new { precision = 3, scale = 2, onlyPositive = true, validatingNumber = "0,0", expectingResult = true},
new { precision = 17, scale = 2, onlyPositive = false, validatingNumber = "+-1.23", expectingResult = false},
new { precision = 17, scale = 2, onlyPositive = false, validatingNumber = "-1.23", expectingResult = true},
new { precision = 17, scale = 2, onlyPositive = true, validatingNumber = ".23", expectingResult = false},
new { precision = 17, scale = 2, onlyPositive = true, validatingNumber = "23.", expectingResult = false},
};
foreach (var test in tests)
{
yield return new TestCaseData(test.precision, test.scale, test.onlyPositive, test.validatingNumber)
.Returns(test.expectingResult)
.SetName($"Test with precision: {test.precision}, scale: {test.scale}, onlyPositive: {test.onlyPositive}," +
$"validatingNumber: {test.validatingNumber} should return {test.expectingResult}, but returned {!test.expectingResult}");
}
}
}
}
8 changes: 8 additions & 0 deletions Testing/Testing.sln.DotSettings.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Расскажи, что это за правки и зачем они нужны в рамках данной задачи?)

<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultTestCaseBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F40cbefd211328d0387117e65923cdf13595b5e0e21e2068a421a51011b5a_003FDefaultTestCaseBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATestCaseSourceAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fcd60bd81539a9932fef279e34bb8da2acbbedd179a22dd9859da7cf7279f15b4_003FTestCaseSourceAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=03d02b35_002D3e85_002D48cc_002D9209_002D8b64406a4fae/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="NumberValidatorTests" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;NUnit3x::6ED454CB-A772-43E5-B72D-3FE9DA27337F::net8.0::HomeExercise.Tasks.NumberValidator.NumberValidatorTests&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>