-
Notifications
You must be signed in to change notification settings - Fork 282
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
Козлов Данил #228
base: master
Are you sure you want to change the base?
Козлов Данил #228
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В целом всё круто)
.SetName("IsValidNumber_ReturnsFalse_WhenSymbolWithNumberLengthGreaterThanPrecision"); | ||
yield return new TestCaseData(3, 2, true, "00.00").Returns(false) | ||
.SetName("IsValidNumber_ReturnsFalse_WhenIntPartWithFracPartGreaterThanPrecision"); | ||
yield return new TestCaseData(17, 2, true, "0").Returns(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Было бы неплохо разделить пустой строкой кейсы с false
и true
yield return new TestCaseData(4, 2, false, "-1.23").Returns(true) | ||
.SetName("IsValidNumber_ReturnsTrue_WhenNegativeSymbolWithNumberLengthNotGreaterThanPrecision"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Было бы неплохо разделить пустой строкой источники данных
throw new ArgumentException("precision must be a non-negative number less or equal than precision"); | ||
numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase); | ||
} | ||
[Test, TestCaseSource(nameof(IsValidNumberPositivityTests)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Атрибут Test
необязателен, если имеются TestCaseSource
. Лучше несколько TestCaseSource
указывать раздельно, не через запятую - оно так смотрится банально симпатичнее.
TestCaseSource(nameof(IsValidNumberSymbolsTests))] | ||
public bool IsValidNumber_Returns(int precision, int scale, bool onlyPositive, string number) | ||
{ | ||
return new NumberValidator(precision, scale, onlyPositive).IsValidNumber(number); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно заменить на expression body
[Test, TestCaseSource(nameof(ConstructorArgumentExceptions))] | ||
public void Constructor_ThrowsArgumentException(int precision, int scale, bool onlyPositive) | ||
{ | ||
Invoking(() => new NumberValidator(precision, scale, onlyPositive)).Should().Throw<ArgumentException>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Применение using static
только ради вызова Invoking
необоснованно.
[TestFixture] | ||
public class NumberValidatorTests | ||
{ | ||
public static IEnumerable IsValidNumberPrecisionTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Почему public
?
{ | ||
get | ||
{ | ||
yield return new TestCaseData(3, 2, true, "+0.00").Returns(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Зачем здесь yield return
? Можно обойтись без него, выйдет лаконичнее.
get | ||
{ | ||
yield return new TestCaseData(3, 2, true, "+0.00").Returns(false) | ||
.SetName("IsValidNumber_ReturnsFalse_WhenSymbolWithNumberLengthGreaterThanPrecision"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Нет нужды и в названии теста, и в названии сценарии теста дублировать информацию.
Тест у тебя называется IsValidNumber_Returns
. В дереве тестов у тебя в итоге будет что-то навроде
IsValidNumber_Returns
IsValidNumber_ReturnsFalse_WhenSymbolWithNumberLengthGreaterThanPrecision
IsValidNumber_ReturnsFalse_WhenIntPartWithFracPartGreaterThanPrecision
Можно и нужно упростить, оставив только нужную для понимания теста информацию.
Assert.AreEqual(actualTsar.Age, expectedTsar.Age); | ||
Assert.AreEqual(actualTsar.Height, expectedTsar.Height); | ||
Assert.AreEqual(actualTsar.Weight, expectedTsar.Weight); | ||
// сравнение родителей такое же как и в коде, который нужно было отрефакторить - сравнивает по ссылке, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Весьма любопытно🧐
{ | ||
private static IEnumerable<TestCaseData> IsValidNumberPrecisionTests | ||
{ | ||
get |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не совсем то, что я имел в виду.
Про разделение false
/ true
- кейсы с false
друг за дружкой без разрывов, {пустая строка}, затем кейсы с true
так же без разрывов.
new TestCaseData(1).Returns(false).SetName("a"),
new TestCaseData(2).Returns(false).SetName("b"),
// пустая строка
new TestCaseData(3).Returns(true).SetName("c"),
new TestCaseData(4).Returns(true).SetName("d"),
Каждый новый кейс начинать отделять пустой строкой имеет смысл только, если кейсы сами по себе большие, но это не тот случай.
Про разделение источников данных пустой строкой - это что после объявления IsValidNumberPrecisionTests
нужно сделать пустую строку. Затем объявлять следующий источник данных.
Так что придется ещё немного поправить)
{ | ||
get | ||
{ | ||
return new List<TestCaseData>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В List
здесь нет смысла, т.к. ты не работаешь с этой структурой. Можно было бы даже не типизировать IEnumerable
. get
здесь тоже сам по себе не нужен.
Избавься от геттера, заюзай массив и не забудь для него использовать инициализатор, тогда будет по красоте. Типизацию IEnumerable
оставь на свой вкус, я бы типизировал, но и без этого всё ок будет.
|
||
[TestCaseSource(nameof(ConstructorArgumentExceptions))] | ||
public void Constructor_ThrowsArgumentException(int precision, int scale, bool onlyPositive) => | ||
FluentActions.Invoking(() => new NumberValidator(precision, scale, onlyPositive)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Почти. Предлагаю вообще избавиться от FluentActions
. С Assert.Throws<>
выйдет лаконичнее.
.SetName("True_WhenNegativeSymbolWithNumberLengthNotGreaterThanPrecision") | ||
}; | ||
|
||
private static IEnumerable<TestCaseData> IsValidNumberScaleTests = new TestCaseData[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Раз IEnumerable<>
типизирован, то после new
нет нужды ещё раз писать тип - можно его убрать. Компилятор сам разберется.
new NumberValidator(precision, scale, onlyPositive).IsValidNumber(number); | ||
|
||
|
||
[TestCaseSource(nameof(ConstructorArgumentExceptions))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Найс. После рефакторинга видно, что источник данных для теста с конструктором слишком далеко от самого теста.
Давай протащим ConstructorArgumentExceptions
ближе к тесту, который использует этот источник данных.
Да и в целом история хорошая, когда данные лежат как можно ближе к месту использования.
private static IEnumerable<TestCaseData> IsValidNumberSymbolsTests = new [] | ||
{ | ||
new TestCaseData(3, 2, true, "a.sd").Returns(false).SetName("False_WhenGivenNotDigits"), | ||
new TestCaseData(17, 2, true, "").Returns(false).SetName("False_WhenEmptyStringGiven") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Давай добавим чуть больше данных для проверок - null, спец.символы - /r , /n, просто символы %$# и т.д. Число с каким-то символом? Символ с каким-то числом? Строка из пробелов?
Что будет, если разделитель не точка, а запятая? А если разделителей несколько? А если в строке только разделитель?
Как валидатор ведет себя с очень большими числами? С очень большой точностью? А все вместе? Умеет ли он понимать числа в экспоненциальном формате?
А может, валидатор считает верным число в другой системе счисления? Иррациональные числа?
Возвращает ли валидатор на один и тот же входной параметр один и тот же результат? Для этого можно использовать атрибут [Repeat()]
.
А может, валидатор как-то меняет свой стейт? Что будет если подать сначала одно число, затем второе, а затем снова первое? Кстати, для того, чтоб обозначить, что метод ничего не изменяет (что-то внутри себя / входные параметры), то его можно пометить атрибутом [Pure]
из неймспейса System.Diagnostics.Contracts
или JetBrains.Annotations
.
Тесты как документация кода должны отвечать на все эти вопросы и даже чуть больше)
.SetName("True_WhenGivenEndlessRationalNumber"), | ||
new TestCaseData(1001, 1000, true, $"{Math.PI}").Returns(true) | ||
.SetName("True_WhenGivenEndlessIrrationalNumber"), | ||
new TestCaseData(2, 1, true, "01").Returns(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Этот же тест в IsValidNumberDiffrentFormsOfNumbersTests
new TestCaseData(17, 2, true, " ").Returns(false).SetName("False_WhenStringOfSpacesGiven") | ||
}; | ||
|
||
[Pure] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Pure]
стоило прицепить на метод IsValidNumber
, с тестами их юзать не стоит.
Атрибуты обычно стараются выравнивать по длине
Чтоб
Было вот так
В случае, когда были только источники данных, я пропускал этот момент, но [Repeat(5)]
прям глаз режет)
|
||
[Pure] | ||
[TestCaseSource(nameof(ConstructorArgumentExceptions))] | ||
public void Constructor_ThrowsArgumentException(int precision, int scale, bool onlyPositive) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Возможно, стоит добавить тест, когда конструктор не кидает исключений.
[Test] | ||
public void ValidatorState_ShouldStayUnchanged() | ||
{ | ||
var validator = new NumberValidator(10, 9, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Нет структуры ААА
- Arrange
, Act
, Assert
. Если тест небольшой, то обычно просто пустыми строками разделяют. Если большой (либо есть желание), то дополнительно прям пишут, где какая секция, используя комментарии.
Тут писать комменты смысла нет, но логически всё равно стоит разделить.
.SetName("True_WhenFracPartNotGreaterThanScale") | ||
}; | ||
|
||
private static IEnumerable<TestCaseData> IsValidNumberBigNumbersTests = new[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Было бы правильнее назвать не BigNumbers
, а LongNumbers
, т.к. 1/3 в виде десятичной дроби - число длинное, но не большое). В то время, как Int64.MaxValue
и большое, и длинное.
|
||
private static IEnumerable<TestCaseData> IsValidNumberDiffrentFormsOfNumbersTests = new[] | ||
{ | ||
new TestCaseData(2, 1, true, "01").Returns(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В программировании относительно часто используется так же и шестнадцатеричная система, её тоже стоит добавить).
Двоичная как-то не очень выглядит. Глядя на тест я вижу, что валидатор считает число 01 верным. А если увеличить разрядность и написать 0001? Должно быть это зависит от передаваемых параметров?
@pakapik