Добавьте файлы проекта.
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
using FluentAssertions;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
using Xunit;
|
||||
|
||||
namespace Telegrator.Tests.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Тесты для коллекций.
|
||||
///
|
||||
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
||||
/// 1. Collection Testing - тестирование коллекций и их операций
|
||||
/// 2. List Testing - тестирование списков
|
||||
/// 3. Indexing Testing - тестирование индексации
|
||||
/// 4. Enumeration Testing - тестирование перечисления
|
||||
/// 5. Capacity Testing - тестирование емкости коллекций
|
||||
/// </summary>
|
||||
public class CollectionTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - создание списка.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем создание коллекций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_ShouldBeCreated()
|
||||
{
|
||||
// Arrange & Act
|
||||
var list = new HandlerDescriptorList();
|
||||
|
||||
// Assert
|
||||
list.Should().NotBeNull();
|
||||
list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - добавление дескриптора.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем добавление элементов в коллекцию
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_Add_ShouldAddDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||
|
||||
// Act
|
||||
list.Add(descriptor);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(1);
|
||||
list.Should().Contain(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - добавление нескольких дескрипторов.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем множественные операции
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_AddMultiple_ShouldAddAllDescriptors()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var descriptor1 = CreateTestDescriptor(UpdateType.Message);
|
||||
var descriptor2 = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||
var descriptor3 = CreateTestDescriptor(UpdateType.InlineQuery);
|
||||
|
||||
// Act
|
||||
list.Add(descriptor1);
|
||||
list.Add(descriptor2);
|
||||
list.Add(descriptor3);
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(3);
|
||||
list.Should().Contain(descriptor1);
|
||||
list.Should().Contain(descriptor2);
|
||||
list.Should().Contain(descriptor3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - получение по индексу.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем индексацию коллекций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_Indexer_ShouldReturnDescriptorAtIndex()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||
var list = new HandlerDescriptorList
|
||||
{
|
||||
descriptor
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = list[0];
|
||||
|
||||
// Assert
|
||||
result.Should().Be(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - получение по неверному индексу.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем исключения при некорректном доступе
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(1)]
|
||||
[InlineData(100)]
|
||||
public void HandlerDescriptorList_IndexerWithInvalidIndex_ShouldThrowArgumentOutOfRangeException(int invalidIndex)
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList
|
||||
{
|
||||
CreateTestDescriptor(UpdateType.Message)
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
list.Invoking(l => _ = l[invalidIndex])
|
||||
.Should().Throw<ArgumentOutOfRangeException>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - перечисление элементов.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем перечисление коллекций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_ShouldBeEnumerable()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = CreateTestDescriptor(UpdateType.Message);
|
||||
var descriptor2 = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||
var list = new HandlerDescriptorList
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2
|
||||
};
|
||||
|
||||
// Act
|
||||
var enumeratedItems = list.ToList();
|
||||
|
||||
// Assert
|
||||
enumeratedItems.Should().HaveCount(2);
|
||||
enumeratedItems.Should().Contain(descriptor1);
|
||||
enumeratedItems.Should().Contain(descriptor2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - очистка списка.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем очистку коллекций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_Clear_ShouldRemoveAllDescriptors()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList
|
||||
{
|
||||
CreateTestDescriptor(UpdateType.Message),
|
||||
CreateTestDescriptor(UpdateType.CallbackQuery)
|
||||
};
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
|
||||
// Assert
|
||||
list.Should().BeEmpty();
|
||||
list.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - проверка содержания элемента.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем поиск в коллекциях
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_Contains_ShouldReturnCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||
var nonExistentDescriptor = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||
|
||||
// Act
|
||||
list.Add(descriptor);
|
||||
var containsExisting = list.Contains(descriptor);
|
||||
var containsNonExistent = list.Contains(nonExistentDescriptor);
|
||||
|
||||
// Assert
|
||||
containsExisting.Should().BeTrue();
|
||||
containsNonExistent.Should().BeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - удаление элемента.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем удаление элементов из коллекций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_Remove_ShouldRemoveDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||
list.Add(descriptor);
|
||||
|
||||
// Act
|
||||
var removed = list.Remove(descriptor);
|
||||
|
||||
// Assert
|
||||
removed.Should().BeTrue();
|
||||
list.Should().BeEmpty();
|
||||
list.Should().NotContain(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для HandlerDescriptorList - удаление несуществующего элемента.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем удаление несуществующих элементов
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_RemoveNonExistent_ShouldReturnFalse()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var nonExistentDescriptor = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||
|
||||
// Act
|
||||
var removed = list.Remove(nonExistentDescriptor);
|
||||
|
||||
// Assert
|
||||
removed.Should().BeFalse();
|
||||
list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для CompletedFiltersList - создание списка.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем создание специализированных коллекций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CompletedFiltersList_ShouldBeCreated()
|
||||
{
|
||||
// Arrange & Act
|
||||
var list = new CompletedFiltersList();
|
||||
|
||||
// Assert
|
||||
list.Should().NotBeNull();
|
||||
list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для проверки производительности коллекций.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем производительность при большом количестве элементов
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerDescriptorList_ShouldHandleLargeNumberOfItems()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var itemsCount = 1000;
|
||||
|
||||
// Act
|
||||
for (int i = 0; i < itemsCount; i++)
|
||||
{
|
||||
list.Add(CreateTestDescriptor(UpdateType.Message));
|
||||
}
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(itemsCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для проверки потокобезопасности (базовый тест).
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем базовую потокобезопасность
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async void HandlerDescriptorList_ShouldHandleConcurrentAccess()
|
||||
{
|
||||
// Arrange
|
||||
var list = new HandlerDescriptorList();
|
||||
var tasks = new List<Task>();
|
||||
|
||||
// Act
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
for (int j = 0; j < 10; j++)
|
||||
{
|
||||
list.Add(CreateTestDescriptor(UpdateType.Message));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks.ToArray());
|
||||
|
||||
// Assert
|
||||
list.Should().HaveCount(100);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вспомогательный метод для создания тестового дескриптора.
|
||||
/// </summary>
|
||||
private static HandlerDescriptor CreateTestDescriptor(UpdateType updateType)
|
||||
{
|
||||
return new HandlerDescriptor(DescriptorType.General, typeof(TestUpdateHandler));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using FluentAssertions;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters;
|
||||
using Telegrator.Filters.Components;
|
||||
using Xunit;
|
||||
|
||||
#pragma warning disable CS8625
|
||||
namespace Telegrator.Tests.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Тесты для базовых фильтров.
|
||||
///
|
||||
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
||||
/// 1. AAA (Arrange-Act-Assert) - структура теста: подготовка, действие, проверка
|
||||
/// 2. Given-When-Then - альтернативная формулировка AAA для лучшей читаемости
|
||||
/// 3. Тестирование граничных случаев и исключений
|
||||
/// 4. Использование моков для изоляции тестируемого кода
|
||||
/// 5. Тестирование как позитивных, так и негативных сценариев
|
||||
/// </summary>
|
||||
public class FilterTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Тест для AnyFilter - фильтр, который всегда проходит.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем базовое поведение - фильтр должен всегда возвращать true
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AnyFilter_ShouldAlwaysPass()
|
||||
{
|
||||
// Arrange (Given) - подготовка тестовых данных
|
||||
var anyFilter = Filter<Update>.Any();
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act (When) - выполнение тестируемого действия
|
||||
var result = anyFilter.CanPass(context);
|
||||
|
||||
// Assert (Then) - проверка результата
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для ReverseFilter - инвертирование результата фильтра.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем композицию фильтров и логику инверсии
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReverseFilter_ShouldInvertResult()
|
||||
{
|
||||
// Arrange
|
||||
var alwaysTrueFilter = Filter<Update>.Any();
|
||||
var reverseFilter = alwaysTrueFilter.Not();
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act
|
||||
var result = reverseFilter.CanPass(context);
|
||||
|
||||
// Assert
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для AndFilter - логическое И между фильтрами.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем комбинирование фильтров и логику И
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(true, true, true)] // Оба фильтра проходят
|
||||
[InlineData(true, false, false)] // Первый проходит, второй нет
|
||||
[InlineData(false, true, false)] // Первый не проходит, второй проходит
|
||||
[InlineData(false, false, false)] // Оба фильтра не проходят
|
||||
public void AndFilter_ShouldCombineFiltersWithAndLogic(bool firstResult, bool secondResult, bool expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var firstFilter = Filter<Update>.If(_ => firstResult);
|
||||
var secondFilter = Filter<Update>.If(_ => secondResult);
|
||||
var andFilter = firstFilter.And(secondFilter);
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act
|
||||
var result = andFilter.CanPass(context);
|
||||
|
||||
// Assert
|
||||
result.Should().Be(expectedResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для OrFilter - логическое ИЛИ между фильтрами.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем комбинирование фильтров и логику ИЛИ
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(true, true, true)] // Оба фильтра проходят
|
||||
[InlineData(true, false, true)] // Первый проходит, второй нет
|
||||
[InlineData(false, true, true)] // Первый не проходит, второй проходит
|
||||
[InlineData(false, false, false)] // Оба фильтра не проходят
|
||||
public void OrFilter_ShouldCombineFiltersWithOrLogic(bool firstResult, bool secondResult, bool expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var firstFilter = Filter<Update>.If(_ => firstResult);
|
||||
var secondFilter = Filter<Update>.If(_ => secondResult);
|
||||
var orFilter = firstFilter.Or(secondFilter);
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act
|
||||
var result = orFilter.CanPass(context);
|
||||
|
||||
// Assert
|
||||
result.Should().Be(expectedResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для CompiledFilter - компиляция нескольких фильтров.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем сложную композицию фильтров
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CompiledFilter_ShouldPassOnlyWhenAllFiltersPass()
|
||||
{
|
||||
// Arrange
|
||||
var filter1 = Filter<Update>.If(_ => true);
|
||||
var filter2 = Filter<Update>.If(_ => true);
|
||||
var filter3 = Filter<Update>.If(_ => false);
|
||||
|
||||
var compiledFilter = CompiledFilter<Update>.Compile(filter1, filter2, filter3);
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act
|
||||
var result = compiledFilter.CanPass(context);
|
||||
|
||||
// Assert
|
||||
result.Should().BeFalse(); // Должен вернуть false, так как filter3 возвращает false
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для проверки IsCollectible свойства.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем свойства объектов
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Filter_IsCollectible_ShouldBeTrueForAnyFilter()
|
||||
{
|
||||
// Arrange
|
||||
var anyFilter = Filter<Update>.Any();
|
||||
|
||||
// Act
|
||||
var isCollectible = anyFilter.IsCollectible;
|
||||
|
||||
// Assert
|
||||
isCollectible.Should().BeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для FunctionFilter - фильтр на основе функции.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем создание фильтров из функций
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FunctionFilter_ShouldUseProvidedFunction()
|
||||
{
|
||||
// Arrange
|
||||
var wasCalled = false;
|
||||
var functionFilter = Filter<Update>.If(_ =>
|
||||
{
|
||||
wasCalled = true;
|
||||
return true;
|
||||
});
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act
|
||||
var result = functionFilter.CanPass(context);
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
wasCalled.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Handlers;
|
||||
using Xunit;
|
||||
|
||||
namespace Telegrator.Tests.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Тесты для обработчиков обновлений.
|
||||
///
|
||||
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
||||
/// 1. Mocking - создание моков для изоляции зависимостей
|
||||
/// 2. Dependency Injection - тестирование через интерфейсы
|
||||
/// 3. Test Doubles - использование заглушек вместо реальных объектов
|
||||
/// 4. Behavior Verification - проверка поведения, а не только результата
|
||||
/// 5. Exception Testing - тестирование исключений
|
||||
/// </summary>
|
||||
public class HandlerTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Тест для базового обработчика обновлений.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем абстрактный класс через конкретную реализацию
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UpdateHandlerBase_ShouldExecuteAndMarkLifetimeAsEnded()
|
||||
{
|
||||
// Arrange
|
||||
var mockContainer = new Mock<IAbstractHandlerContainer<Message>>();
|
||||
var testHandler = new TestUpdateHandler();
|
||||
|
||||
// Act
|
||||
await testHandler.Execute(mockContainer.Object);
|
||||
|
||||
// Assert
|
||||
testHandler.WasExecuted.Should().BeTrue();
|
||||
testHandler.LifetimeToken.IsEnded.Should().BeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для проверки токена жизненного цикла.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем состояние объектов
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HandlerLifetimeToken_ShouldTrackLifetimeCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var handler = new TestUpdateHandler();
|
||||
|
||||
// Act & Assert
|
||||
handler.LifetimeToken.IsEnded.Should().BeFalse();
|
||||
|
||||
// Act
|
||||
handler.LifetimeToken.LifetimeEnded();
|
||||
|
||||
// Assert
|
||||
handler.LifetimeToken.IsEnded.Should().BeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Тест для проверки отмены операции.
|
||||
///
|
||||
/// ПРИНЦИП: Тестируем асинхронные операции и отмену
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UpdateHandlerBase_ShouldHandleCancellation()
|
||||
{
|
||||
// Arrange
|
||||
var mockContainer = new Mock<IAbstractHandlerContainer<Message>>();
|
||||
var testHandler = new TestUpdateHandler();
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel(); // Отменяем сразу
|
||||
|
||||
// Act & Assert
|
||||
await testHandler.Invoking(h => h.Execute(mockContainer.Object, cancellationTokenSource.Token))
|
||||
.Should().ThrowAsync<OperationCanceledException>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
# Тесты для Telegrator
|
||||
|
||||
Этот проект содержит комплексные тесты для библиотеки Telegrator, демонстрирующие различные парадигмы и подходы к тестированию.
|
||||
|
||||
## Структура тестов
|
||||
|
||||
### 1. Filters (Фильтры)
|
||||
**Файл:** `Filters/FilterTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **AAA (Arrange-Act-Assert)** - структура теста: подготовка, действие, проверка
|
||||
- **Given-When-Then** - альтернативная формулировка AAA для лучшей читаемости
|
||||
- **Тестирование граничных случаев** и исключений
|
||||
- **Использование моков** для изоляции тестируемого кода
|
||||
- **Тестирование как позитивных, так и негативных сценариев**
|
||||
|
||||
**Что тестируется:**
|
||||
- Базовые фильтры (AnyFilter, ReverseFilter, AndFilter, OrFilter)
|
||||
- Компиляция фильтров
|
||||
- Логические операции между фильтрами
|
||||
- Свойства фильтров (IsCollectible)
|
||||
|
||||
### 2. Handlers (Обработчики)
|
||||
**Файл:** `Handlers/HandlerTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Mocking** - создание моков для изоляции зависимостей
|
||||
- **Dependency Injection** - тестирование через интерфейсы
|
||||
- **Test Doubles** - использование заглушек вместо реальных объектов
|
||||
- **Behavior Verification** - проверка поведения, а не только результата
|
||||
- **Exception Testing** - тестирование исключений
|
||||
|
||||
**Что тестируется:**
|
||||
- Базовые обработчики обновлений
|
||||
- Жизненный цикл обработчиков
|
||||
- Обработка исключений
|
||||
- Отмена операций
|
||||
- Токены жизненного цикла
|
||||
|
||||
### 3. Descriptors (Дескрипторы)
|
||||
**Файл:** `Descriptors/HandlerDescriptorTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Builder Pattern Testing** - тестирование паттерна строителя
|
||||
- **Factory Pattern Testing** - тестирование фабричных методов
|
||||
- **Immutable Object Testing** - тестирование неизменяемых объектов
|
||||
- **Configuration Testing** - тестирование конфигурации объектов
|
||||
- **Validation Testing** - тестирование валидации данных
|
||||
|
||||
**Что тестируется:**
|
||||
- Создание дескрипторов обработчиков
|
||||
- Различные типы дескрипторов (General, Singleton, Keyed, Implicit)
|
||||
- Наборы фильтров
|
||||
- Индексаторы
|
||||
- Валидация параметров
|
||||
|
||||
### 4. Providers (Провайдеры)
|
||||
**Файл:** `Providers/ProviderTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Service Layer Testing** - тестирование сервисного слоя
|
||||
- **Integration Testing** - тестирование интеграции компонентов
|
||||
- **Collection Testing** - тестирование коллекций и их операций
|
||||
- **Provider Pattern Testing** - тестирование паттерна провайдера
|
||||
- **Dependency Resolution Testing** - тестирование разрешения зависимостей
|
||||
|
||||
**Что тестируется:**
|
||||
- Провайдеры обработчиков
|
||||
- Коллекции обработчиков
|
||||
- Операции с коллекциями
|
||||
- Интеграция между провайдерами
|
||||
|
||||
### 5. Hosting (Хостинг)
|
||||
**Файл:** `Hosting/HostingTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Host Testing** - тестирование хостинга приложений
|
||||
- **Configuration Testing** - тестирование конфигурации
|
||||
- **Dependency Injection Testing** - тестирование DI контейнера
|
||||
- **Builder Pattern Testing** - тестирование паттерна строителя
|
||||
- **Lifecycle Testing** - тестирование жизненного цикла приложения
|
||||
|
||||
**Что тестируется:**
|
||||
- Строители хостов
|
||||
- Конфигурация ботов
|
||||
- Жизненный цикл хостов
|
||||
- Валидация параметров
|
||||
|
||||
### 6. Collections (Коллекции)
|
||||
**Файл:** `Collections/CollectionTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Collection Testing** - тестирование коллекций и их операций
|
||||
- **List Testing** - тестирование списков
|
||||
- **Indexing Testing** - тестирование индексации
|
||||
- **Enumeration Testing** - тестирование перечисления
|
||||
- **Capacity Testing** - тестирование емкости коллекций
|
||||
|
||||
**Что тестируется:**
|
||||
- Списки дескрипторов обработчиков
|
||||
- Списки завершенных фильтров
|
||||
- Операции с коллекциями
|
||||
- Производительность
|
||||
- Потокобезопасность
|
||||
|
||||
### 7. Integration (Интеграционные тесты)
|
||||
**Файл:** `Integration/IntegrationTests.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Integration Testing** - тестирование взаимодействия компонентов
|
||||
- **End-to-End Testing** - тестирование полного потока
|
||||
- **System Testing** - тестирование системы в целом
|
||||
- **Workflow Testing** - тестирование рабочих процессов
|
||||
- **Scenario Testing** - тестирование сценариев использования
|
||||
|
||||
**Что тестируется:**
|
||||
- Полный цикл обработки обновлений
|
||||
- Взаимодействие фильтров и обработчиков
|
||||
- Композиция фильтров
|
||||
- Жизненный цикл обработчиков
|
||||
- Интеграция компонентов
|
||||
|
||||
### 8. TestHelpers (Вспомогательные утилиты)
|
||||
**Файл:** `TestHelpers/TestUtilities.cs`
|
||||
|
||||
**Парадигмы тестирования:**
|
||||
- **Test Utilities** - создание вспомогательных методов для тестов
|
||||
- **Test Data Builders** - построители тестовых данных
|
||||
- **Mock Factories** - фабрики моков
|
||||
- **Test Fixtures** - фикстуры для тестов
|
||||
- **Test Helpers** - вспомогательные классы для тестирования
|
||||
|
||||
**Что предоставляется:**
|
||||
- Утилиты для создания тестовых данных
|
||||
- Фабрики моков
|
||||
- Вспомогательные классы
|
||||
- Тестовые обработчики
|
||||
|
||||
## Основные принципы тестирования
|
||||
|
||||
### 1. AAA (Arrange-Act-Assert)
|
||||
```csharp
|
||||
[Fact]
|
||||
public void TestExample()
|
||||
{
|
||||
// Arrange - подготовка тестовых данных
|
||||
var filter = Filter<Update>.Any();
|
||||
var context = TestUtilities.CreateFilterContext();
|
||||
|
||||
// Act - выполнение тестируемого действия
|
||||
var result = filter.CanPass(context);
|
||||
|
||||
// Assert - проверка результата
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Тестирование граничных случаев
|
||||
```csharp
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(1)]
|
||||
[InlineData(100)]
|
||||
public void TestBoundaryConditions(int invalidIndex)
|
||||
{
|
||||
// Тестируем граничные случаи
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Использование моков
|
||||
```csharp
|
||||
[Fact]
|
||||
public void TestWithMocks()
|
||||
{
|
||||
// Arrange
|
||||
var mockClient = new Mock<ITelegramBotClient>();
|
||||
var mockContainer = TestUtilities.CreateMockHandlerContainer();
|
||||
|
||||
// Act & Assert
|
||||
// Тестирование с моками
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Тестирование исключений
|
||||
```csharp
|
||||
[Fact]
|
||||
public void TestExceptions()
|
||||
{
|
||||
// Act & Assert
|
||||
Action action = () => { /* код, который должен выбросить исключение */ };
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
```
|
||||
|
||||
## Запуск тестов
|
||||
|
||||
### Через командную строку
|
||||
```bash
|
||||
dotnet test
|
||||
```
|
||||
|
||||
### Через Visual Studio
|
||||
1. Откройте Test Explorer
|
||||
2. Запустите все тесты или выберите конкретные
|
||||
|
||||
### Через Rider
|
||||
1. Откройте Unit Tests window
|
||||
2. Запустите тесты
|
||||
|
||||
## Покрытие кода
|
||||
|
||||
Для анализа покрытия кода используйте:
|
||||
```bash
|
||||
dotnet test --collect:"XPlat Code Coverage"
|
||||
```
|
||||
|
||||
## Рекомендации по написанию тестов
|
||||
|
||||
1. **Именование тестов** должно быть описательным и следовать паттерну `MethodName_Scenario_ExpectedResult`
|
||||
2. **Каждый тест** должен тестировать только одну вещь
|
||||
3. **Используйте моки** для изоляции зависимостей
|
||||
4. **Тестируйте как позитивные, так и негативные сценарии**
|
||||
5. **Группируйте связанные тесты** в отдельные классы
|
||||
6. **Используйте вспомогательные методы** для создания тестовых данных
|
||||
7. **Документируйте сложные тесты** с помощью комментариев
|
||||
|
||||
## Полезные ссылки
|
||||
|
||||
- [xUnit Documentation](https://xunit.net/)
|
||||
- [Moq Documentation](https://github.com/moq/moq4)
|
||||
- [FluentAssertions Documentation](https://fluentassertions.com/)
|
||||
- [.NET Testing Best Practices](https://docs.microsoft.com/en-us/dotnet/core/testing/)
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Telegrator\Telegrator.csproj" />
|
||||
<ProjectReference Include="..\Telegrator.Hosting\Telegrator.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Handlers;
|
||||
|
||||
namespace Telegrator.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Вспомогательный класс для тестирования абстрактного UpdateHandlerBase.
|
||||
///
|
||||
/// ПРИНЦИП: Создание тестовых двойников для абстрактных классов
|
||||
/// </summary>
|
||||
[MessageHandler]
|
||||
internal class TestUpdateHandler : MessageHandler
|
||||
{
|
||||
public bool WasExecuted { get; private set; }
|
||||
|
||||
public override Task Execute(IAbstractHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
WasExecuted = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user