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

Рыбин Леонид #17

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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 .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
bin/
obj/
.vs/
.vs/
25 changes: 25 additions & 0 deletions Testing/Basic/Basic.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basic", "Basic.csproj", "{3C8EF388-9BF7-47CF-8B51-CA46F08B9253}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3C8EF388-9BF7-47CF-8B51-CA46F08B9253}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C8EF388-9BF7-47CF-8B51-CA46F08B9253}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C8EF388-9BF7-47CF-8B51-CA46F08B9253}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C8EF388-9BF7-47CF-8B51-CA46F08B9253}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54C706B2-21C4-4C49-A162-2E765ACC0F80}
EndGlobalSection
EndGlobal

This comment was marked as off-topic.

This comment was marked as off-topic.

Copy link
Author

Choose a reason for hiding this comment

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

Да, вроде бы. Я помню он мне файл .sln предложил сохранить.

9 changes: 9 additions & 0 deletions Testing/Basic/Basic.sln.DotSettings.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<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">
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=ac147a29_002D1949_002D44a0_002D9289_002Dab17f1aa8756/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests in category 'ToRefactor'" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Category&gt;ToRefactor&lt;/Category&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;NUnit3x::3C8EF388-9BF7-47CF-8B51-CA46F08B9253::net8.0::HomeExercise.Tasks.NumberValidator.NumberValidatorTests&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>

This comment was marked as off-topic.

Copy link
Author

Choose a reason for hiding this comment

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

Я погуглил и вроде как файл DotSettings.user добавляют в .gitignore. Это обязательно ?

Choose a reason for hiding this comment

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

В DotSettings.user обычно какие-то локальные настройки хранятся, скорее всего он создался из-за того что появился солюшен. В данном случае, да - его логично заигнорить.

В следующей домашке попробуй без солюшена редактировать проект, скорее всего такой проблемы просто не возникнет.

27 changes: 16 additions & 11 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 @@ -14,16 +15,8 @@ public void CheckCurrentTsar()
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);
actualTsar.Should().BeEquivalentTo(expectedTsar, options =>
options.Excluding(o => o.Name == nameof(Person.Id) && o.DeclaringType == typeof(Person)));
}

[Test]
Expand All @@ -35,6 +28,18 @@ public void CheckCurrentTsar_WithCustomEquality()
new Person("Vasili III of Russia", 28, 170, 60, null));

// Какие недостатки у такого подхода?
/*
1) Метод AreEqual проверяет только фиксированые параметры в Person, поэтому
если класс Person будет измненён, то тесты предётся переписывать,
добавляя новые или удаляя старые поля.
2) Метод AreEqual возвращает True или False, поэтому даже если только одно поле
не пройдёт проверку, всё что будет понятно, что проверка не прошла, а
что именно было не так, узнать не получится.
3) Даже если переписывание тестов не является проблемой (см. 1)), то при добавлении
большего количества полей, особенно если типом новых полей будут другие классы
со своими полями, метод AreEqual рискует разрастись до монструозных масштабов,
из-за чего его станет очень сложно читать.
*/
ClassicAssert.True(AreEqual(actualTsar, expectedTsar));
}

Expand Down
90 changes: 69 additions & 21 deletions Testing/Basic/Homework/2. NumberValidator/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,79 @@

using System.Collections;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Legacy;

namespace HomeExercise.Tasks.NumberValidator;

[TestFixture]
public class NumberValidatorTests
{
[Test]
public void Test()
[TestCase(-1, 2, true, TestName = "NumberValidator_WithNegativePrecision_ThrowsException")]
[TestCase(0, 2, false, TestName = "NumberValidator_WithPrecisionEqualsZero_ThrowsException")]
[TestCase(1, -2, true, TestName = "NumberValidator_WithNegativeScale_ThrowsException")]
[TestCase(1, 2, false, TestName = "NumberValidator_WithScaleGreaterThanPrecision_ThrowsException")]
[TestCase(1, 1, true, TestName = "NumberValidator_WithScaleEqualsPrecision_ThrowsException")]
public void NumberValidation_ConstructorThrowsArgumentException(int precision, int scale, bool onlyPositive)

Choose a reason for hiding this comment

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

Тут всё хорошо, править не будем, но на будущее:
Здесь наименование метода уже говорит нам о действии и об ожиданиях, поэтому в тест-кейсах необязательно прописывать "NumberValidator_" и "_ThrowsException".

Сейчас теряются из вида условия, когда должна быть вкинута ошибка:
image

Choose a reason for hiding this comment

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

И вот как можно сделать:
У меня информация о том, что проверятся и что ожидается есть - она взята из наименования тест-метода
Все тест-кейсы объединились в группу этого тест-метода, и я точно и чётко вижу все условия, когда конструктор должен упасть с ошибкой
image

{
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"));
var action = () => new NumberValidator(precision, scale, onlyPositive);

action.Should().Throw<ArgumentException>();

This comment was marked as resolved.

}

[Test(Description = "NumberValidator_WithCorrectParameters_NotThrowException")]

Choose a reason for hiding this comment

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

До сих пор не понимаю, для чего тут описание теста)

Description - это не TestName, для атрибута [Test] за имя теста будет браться наименование метода.
Description обычно используется как коммент к тесту, и в нём в свободной форме можно написать, что делает тест, если он неочевиден.

В данном случае я по имени метода всё прекрасно понимаю, что делается и что ожидается.
Но есть замечания:

  1. Не указаны условия, когда конструктор не должен вбросить ошибку (хотя бы просто WhenCorrectParameters).
  2. В наименовании теста ты пишешь, что "конструктор не должен вбросить ArgumentException", а по факту у тебя проверка внутри на любую ошибку.

public void NumberValidation_ConstructorDoesNotHaveArgumentException()
{
var action = () => new NumberValidator(1, 0, true);

action.Should().NotThrow();
}

[TestCaseSource(nameof(ValidNumbersCases))]
[TestCaseSource(nameof(InvalidNumbersCases))]
[TestCaseSource(nameof(InvalidInputCases))]
public bool IsValidNumber_WorksCorrectly(int precision, int scale, bool onlyPositive, string value)
{
return new NumberValidator(precision, scale, onlyPositive).IsValidNumber(value);
}

public static IEnumerable ValidNumbersCases
{
get
{
yield return new TestCaseData(1, 0, true, "0").SetName("ForZero_WithCorrectParams_ReturnTrue").Returns(true);
yield return new TestCaseData(2, 1, true, "0.0").SetName("ForZero_WithZeroFractionalPartAndDot_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(2, 1, true, "0.1").SetName("ForZero_WithNonZeroFractionalPartAndDot_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(2, 1, true, "0,1").SetName("ForZero_WithNonZeroDecimalPartAndComma_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(2, 0, true, "+1").SetName("ForPositiveInteger_WithSign_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(3, 1, true, "+1.1").SetName("ForPositiveDecimal_WithDotAndSign_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(3, 1, true, "+1,1").SetName("ForPositiveDecimal_WithCommaAndSign_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(2, 0, false, "-1").SetName("ForNegativeInteger_WithSign_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(3, 1, false, "-1.1").SetName("ForNegativeDecimal_WithDotAndSign_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(3, 1, false, "-1,1").SetName("ForNegativeDecimal_WithCommaAndSign_ReturnTrueOnCorrectParams").Returns(true);
yield return new TestCaseData(7, 2, true, "671,23").SetName("ForRealDecimal_WithCorrectParameters_ReturnTrue").Returns(true);
}
}

public static IEnumerable InvalidNumbersCases
{
get
{
yield return new TestCaseData(2, 0, true, "-1").SetName("ForNegativeInteger_WithMinusSign_OnlyPositiveIsFalse").Returns(false);

Choose a reason for hiding this comment

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

Вот тут что-то не то "ForNegativeInteger_WithMinusSign" - у нас уже есть информация, что на входе отрицательное целое, зачем уточнять, что оно со знаком?) Может отрицательное без знака как-то быть?)
Я бы тут просто написал "ForNegativeNumber_WhenOnlyPositiveConfigured_ReturnFalse"

Choose a reason for hiding this comment

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

Можно 2 теста добавить: один ForDecimal, другой ForInteger

yield return new TestCaseData(3, 2, true, "4321.5").SetName("ForDecimal_WithIncorrectPrecision_ReturnFalse").Returns(false);

Choose a reason for hiding this comment

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

Вот я вижу тест для decimal, логичный вопрос возникает: а где для integer такой же?

yield return new TestCaseData(3, 0, true, "+145").SetName("ForPositiveDecimal_WithPlusSign_PrecisionCalculationAccountPlusSign").Returns(false);

Choose a reason for hiding this comment

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

"ForPositiveDecimal" - а в тесте integer

yield return new TestCaseData(3, 0, false, "-124").SetName("ForNegativeDecimal_WithMinusSign_PrecisionCalculationAccountMinusSign").Returns(false);

Choose a reason for hiding this comment

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

Также - в тесте передаем integer, а в наименовании decimal

}
Copy link

@SlavikGh0st SlavikGh0st Nov 6, 2024

Choose a reason for hiding this comment

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

Вот сейчас, когда все тесты однообразно проименованы, я невооруженным глазом вижу:

  1. Что работа Precision проверяется только для integer, хотя по хорошему бы и для decimal аналогичных кейсов накидать.
  2. Нету тестов проверки на scale: когда у нас число не будет соответствовать этому параметру, должно вернуться false.

}

public static IEnumerable InvalidInputCases
{
get
{
yield return new TestCaseData(10, 9, true, "127.0.0.1").SetName("ForNumber_WithNonRealNumberFormat_ReturnFalse").Returns(false);
yield return new TestCaseData(2, 0, true, "").SetName("ForNumber_IsEmptyString_ReturnFalse").Returns(false);

Choose a reason for hiding this comment

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

Вот тут хорошо просто зайдет "ForEmptyString_ReturnFalse" - префикс "ForNumber" только путает

yield return new TestCaseData(2, 1, true, ".0").SetName("ForNumber_WithMissingIntegerPart_ReturnFalse").Returns(false);
yield return new TestCaseData(2, 0, true, "0.").SetName("ForNumber_WithMissingFractionalPart_ReturnFalse").Returns(false);
yield return new TestCaseData(10, 2, true, "abcde.f").SetName("ForNumber_WithNonDigitCharacters_ReturnFalse").Returns(false);

Choose a reason for hiding this comment

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

Как будто бы этот тест дублирует первый - тут нужен один с понятным названием "ForNotNumberInput_ReturnFalse"


}

This comment was marked as resolved.

Copy link
Author

Choose a reason for hiding this comment

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

Я переписал название тестов в развернутом виде, но PascalCase требует каждое слово с большой буквы писать, но в названии теста там предложение обычное, и как-то не очень в предложении каждое слово с большой буквы писать.

This comment was marked as resolved.

This comment was marked as resolved.

}
}