Skip to content

Копытов Михаил#49

Open
Mihail3141 wants to merge 8 commits intokontur-courses:masterfrom
Mihail3141:master
Open

Копытов Михаил#49
Mihail3141 wants to merge 8 commits intokontur-courses:masterfrom
Mihail3141:master

Conversation

@Mihail3141
Copy link

@Mihail3141 Mihail3141 changed the title KopitovMihail Копытов Михаил Oct 28, 2025
actualTsar
.Should()
.BeEquivalentTo(expectedTsar, options => options
.ExcludingMembersNamed("Id"));

Choose a reason for hiding this comment

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

Если свойство Id в классе Person переименуют, то в данном случае тест упадёт, а поведение в тесте не сохранится. Для этого можно сослаться напрямую на название свойства в классе

Copy link
Author

Choose a reason for hiding this comment

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

Это решение возникло вследствие попытки исключить свойство Id у всех "родственников". Если в Person появятся дети, браться, сёстры, то каждого придётся вносить в исключение. Изначально решение выглядело так:
actualTsar
.Should()
.BeEquivalentTo(expectedTsar, options => options
.Excluding(person => person.Id)
.Excluding(person => person.Parent.Id));

Comment on lines 18 to 21
actualTsar
.Should()
.BeEquivalentTo(expectedTsar, options => options
.ExcludingMembersNamed("Id"));

Choose a reason for hiding this comment

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

Сейчас этот способ скроет для проверки все свойства и поля с именем Id. Если появится объект для которого мы хотим оставить сохранения по Id, то мы не сможем этого сделать. Нужно как-то сделать так, чтобы скрывали это свойство при сравнении только у конкретного класса.

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

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

Choose a reason for hiding this comment

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

Помимо того что метод AreEqual неудобный, есть ли у него какие-то ещё проблемы, из-за которых что-то может пойти не так при работе с ним?

Copy link
Author

Choose a reason for hiding this comment

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

Теоретически при добавлении "родственников" может получится цикл, а значит и бесконечная рекурсия.

Choose a reason for hiding this comment

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

Ну не только это на самом деле прям самый крайний случай. При достаточно большой структуре, которая будет иметь много Parent-ов, мы просто упадём со StackOverflowException т.к. зайдём слишком глубоко.

Choose a reason for hiding this comment

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

Стоит убрать после окончания рефакторинга

//Преимущества такой реализации:
//-тест занимает меньше строк кода,
//-добавление новых свойств не приводит к изменению теста (кроме самой инициализации expectedTsar),
//-добавление вложенных объектов не приводит к изменению теста (кроме самой инициализации expectedTsar),

Choose a reason for hiding this comment

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

А все ли вложенные объекты мы проверим? Советую почитать доку FluentAssertions https://fluentassertions.com/

Copy link
Author

Choose a reason for hiding this comment

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

Нет, проверяются только до 10 уровня вложения, но можно отключить при помощи AllowingInfiniteRecursion
Рекрусия

[TestCase(-1, 2, true)]
[TestCase(17, -1, true)]
[TestCase(17, 18, true)]
public void Constructor_Should_Throw_For_InvalidParameters(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.

Касательно названий тестов. Есть разные способы составления названия, сейчас ты названия пишешь неструктурированно, такое сложно читать и вникать, т.к. не понятно где условие, а где возвращаемый результат. Обычно используют <название метода>When<условие><Результат>. У нас в команде используется к примеру другая семантика - Should_<ожидаемый результат в snake case>_when<условие в snake case>, тестируемый метод мы обычно не указываем, т.к. тестовые файлы поделены по методам класса.

Так что можешь в целом почитать/посмотреть как в проектах шарповых идёт именование тестов и выбрать себе предпочтительный способ, а текущие названия поменять

[TestCase(-1, 2, true)]
[TestCase(17, -1, true)]
[TestCase(17, 18, true)]
public void Constructor_Should_Throw_For_InvalidParameters(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.

Вообще здесь было бы идеально перебирать все варианты значения onlyPositive, т.к. никакого влияния на конструктор он не имеет, но внутрь передаётся. Можно реализовать это через TestCaseSource https://docs.nunit.org/articles/nunit/writing-tests/attributes/testcasesource.html

Comment on lines 10 to 12
[TestCase(-1, 2, true)]
[TestCase(17, -1, true)]
[TestCase(17, 18, true)]

Choose a reason for hiding this comment

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

Не все случаи проверил

Comment on lines 20 to 22
[TestCase(5, 2, false)]
[TestCase(10, 5, true)]
[TestCase(10, 0, false)]

Choose a reason for hiding this comment

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

А здесь зачем целых три тесткейса? Можно оставить было бы вообще один

Comment on lines 30 to 42
[TestCase(17, 2, true, null, false)]
[TestCase(17, 2, true, "", false)]
[TestCase(17, 2, true, "a.1d", false)]
[TestCase(2, 1, true, "+0.0", false)]
[TestCase(2, 1, true, "00.0", false)]
[TestCase(3, 1, true, "0.00", false)]
[TestCase(17, 2, true, "-0.0", false)]
[TestCase(17, 2, true, "123.45", true)]
[TestCase(17, 2, true, "123,45", true)]
[TestCase(17, 2, true, "+0.0", true)]
[TestCase(17, 2, false, "-0.0", true)]
[TestCase(17, 0, true, "0", true)]
[TestCase(17, 0, false, "-10", true)]

Choose a reason for hiding this comment

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

Не хватает кейсов и названий к ним

}

[Test, TestCaseSource(typeof(TestData), nameof(TestData.InvalidConstructorParameters))]
public void Constructor_Should_ReturnThrow_WhenInvalidParameters(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.

Название нужно переделать. Строка Should_ReturnThrow во-первых зачем-то разделена нижним подчёркиванием(либо делай подчёркивание между каждым словом, либо делай только между смысловыми частями <метод><что возвращает><при каких условиях>, во-вторых метод не возвращает Throw. Если ты хочешь так его назвать, корректнее будет Constructor_shoud_throw_<тип ошибки>_when_invalid_parameters

Copy link
Author

Choose a reason for hiding this comment

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

Constructor_ShouldThrowArgumentException_WhenInvalidParameters Вот такой вариант правильный?

}
}

[Test, TestCaseSource(typeof(TestData), nameof(TestData.InvalidConstructorParameters))]

Choose a reason for hiding this comment

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

Можно оставить только атрибут TestCaseSource, typeof не обязателен

action.Should().NotThrow();
}

[Test]

Choose a reason for hiding this comment

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

Можно убрать

[TestCase(17, 2, false, "123,45", TestName = "Valid number with with ',' between integer and scale parts")]
[TestCase(17, 0, true, "123", TestName = "Valid integer without scale part")]
[TestCase(17, 2, true, "0000123.45", TestName = "Valid number with leading zeros")]
[TestCase(int.MaxValue, 2, true, "2147483647.22", TestName = " Max int precision with scale part")]

Choose a reason for hiding this comment

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

А этот тест что проверяет?

Copy link
Author

Choose a reason for hiding this comment

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

Да, ошибся. Смешалось в голове самое число и количество знаков в нём.

}

[TestCase(10, 5, true, TestName = "Valid positive number")]
public void ConstructorShould_ReturnNotThrow_WhenValidParameters(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.

Название очень сложно читать, почитай в комментах я тебе уже написал про наименование тестов

[Test]
public void Test()
[TestCase(17, 2, true, "1.2.3", TestName = "multiple dots")]
[TestCase(17, 2, true, "--123", TestName = "double minus")]

Choose a reason for hiding this comment

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

Есть проверка на двойной минус, но не на плюс. Почему решил добавить её?

Copy link
Author

Choose a reason for hiding this comment

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

Хотел проверить двойной знак перед числом. Подумал, что с "++" будет повторяющийся тест.

[TestCase(17, 2, true, "12a.3", TestName = "Invalid character in number")]
[TestCase(17, 2, true, "\n1.23", TestName = "Special character before")]
[TestCase(17, 2, true, "1234.", TestName = "Trailing dot without fractional part")]
[TestCase(17, 2, true, ".12", TestName = "Leading dot without integer part")]

Choose a reason for hiding this comment

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

onlyPositive во всех тестах равен true, но по факту влияет только на один тест. Лучше сделать его true только в тесте в котором он имеет смысл

[TestCase(17, 2, true, "\n1.23", TestName = "Special character before")]
[TestCase(17, 2, true, "1234.", TestName = "Trailing dot without fractional part")]
[TestCase(17, 2, true, ".12", TestName = "Leading dot without integer part")]
[TestCase(17, 2, true, "-123", TestName = "Negative number with onlyPositive=true")]

Choose a reason for hiding this comment

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

Тут хорошим тестом видится тест на 0 с разными знаками

[TestCase(17, 2, true, "1234.", TestName = "Trailing dot without fractional part")]
[TestCase(17, 2, true, ".12", TestName = "Leading dot without integer part")]
[TestCase(17, 2, true, "-123", TestName = "Negative number with onlyPositive=true")]
public void IsValidNumberShould_ReturnFalse_WhenInvalidNumbers(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.

Всё ещё не достаточно кейсов. Пройдись по проверкам которые указаны в методе который проверяешь и посмотри на какие из них ещё нет теста

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

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

Choose a reason for hiding this comment

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

Ну не только это на самом деле прям самый крайний случай. При достаточно большой структуре, которая будет иметь много Parent-ов, мы просто упадём со StackOverflowException т.к. зайдём слишком глубоко.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants