Flexible Coding
148 subscribers
163 photos
2 files
96 links
Download Telegram
Ради эксперимента поставил заметочник affine.pro. Однако у него довольно плохая мобильная версия, поэтому для быстрых записей я захотел написать телеграм-бота. А уже об этом - моя новая статья :)
Behaviour Driven Development && Dotnet

Так получилось, что в рамках своего опыта работы я не встречал проектов с хорошими юнит-тестами и большим покрытием ими проектов.

Зато на всех проектах использовался подход Behaviour Driven Development, в рамках которого писались интеграционные тесты.

Behaviour Driven Development - это подход к разработке, при котором вы пишете интеграционные тесты на основе User Story, используя язык Gherkin как со стороны бизнеса, так и со стороны разработки. Чем-то похоже на ATDD

Каждый тест состоит из трех этапов:

- Given (дано) - подготовка окружения (может быть несколько)
- When (когда) - выполнение действия, которое как-то это окружение меняет (желательно, чтобы этот шаг был только один)
- Then (тогда) - проверка того, что окружение изменилось так как надо (тоже может быть несколько)

Например
- Дано: созданный аккаунт пользователя с ролью администратор
- Когда пользователь авторизуется в системе
- Тогда пользователь видит страницу администратора

Такие тесты более сложны в сопровождении, дольше пишутся и дольше прогоняются, однако имеют очень важное преимущество - они не просто тестируют изолированный кусок логики, как юнит-тесты, а фактически подтверждают выполнение бизнес-требований.

В дотнете есть много популярных фреймворков для написания таких тестов - LightBDD, SpecFlow, MSpec. Однако наиболее популярны первые два - о них и поговорим в следующих постах

Flexible Coding
SLNX-формат или как Microsoft меня расстроили

Всем привет! Сегодня поговорим про "решения" проектов на .NET.

Как мы знаем, приложение на дотнете - это некоторое решение (Solution) с набором проектов. В одном из проектов может быть слой бизнес-логики, в другом - инфраструктурный, ну и так далее. Всё это может быть и в одном проекте, но это редкость.

Так вот, проекты объединяются в решение через sln-файлы - файлы решений. Внутри этого файла находится страшный сплошной текст с информацией о проектах, их расположением в Solution Folders и прочими метаданными. И его довольно трудно читать - там довольно странные GUID-ы, куча каких-то маппингов, повторов...

А проблемы начинаются, когда разработчики параллельно в разных ветках добавляют много проектов, а потом надо решать merge-конфликты. И после очередного разбора конфликтов на работе, итогом которого стала отвязка части проектов и ручная их перепривязка к решению, стало понятно, что так дальше продолжаться не может.

В .NET8 был представлен новый формат файлов решений - slnx. Данные в нём уже структурированы и довольно удобно читаются - xml-формат, там нет GUID-ов и прочего мусора, который мешает пониманию содержимого файла - то, что надо!

И вот мы уже перешли на новый формат решений, счастливы - конфликты в этом файле решаются просто, коллизий нет... и тут выясняется, что dotnet cli этот формат полностью не поддерживает. То есть dotnet restore, dotnet build в нашем dockerfile тупо не отрабатывает и мы не можем задеплоиться!

Собственно, благодаря тому, что xml удобно парсить, не составило труда написать скрипт, который парсит список проектов в решении и выполняет dotnet restore для них по отдельности, но это не так удобно. Вместо dotnet restore мы выполнеяем команду

dotnet fsi list-projects.fsx | sed 's|\\|/|g' | xargs -I {} sh -c 'if [ -f "{}" ]; then dotnet restore "{}"; fi'

> Да, я написал F#-скрипт для парсинга slnx-файла

А ещё забавно, что даже последняя версия Visual Studio 2022 отказалась открывать этот файл :)

Какой же статус работы по slnx?
- Для dotnet cli - выйдет в .net9.0.2
- Для vs code - есть открытый issue
- Для Visual Studio - да вроде уже работает, но у меня не работает :(
- И только JetBrains Rider стабильно открывает и понимает новый формат солюшнов

Морали не будет, просто будьте внимательнее при переходе на что-то новое и крутое :)

Flexible Coding
Итак, 2025 год (с наступившим!), автор вышел из цепочки событий разной степени безумия и готов с вами познакомиться!

Меня зовут Дмитрий Бахтенков, уже несколько лет я занимаюсь коммерческой разработкой на .NET, а также увлекаюсь различными аспектами саморазвития - очень много времени уделяю тому, как быть продуктивным, организованным, эффективным и т.д.

Этот канал я создал, чтобы делиться с вами своими мыслями, идеями и личным опытом, непосредственно связанным с программированием и IT в целом.

Что здесь есть?
- Статьи. Последнее время я публикую их на портале вАЙТИ
- Обзоры книг. Я много читаю, в том числе техническую литературу. Об интересных книгах я пишу отзывы, и думаю о том, чтобы делать более развёрнутые конспекты
- Интересные технические кейсы. Что-то непонятное, занятное и странное
- Обучающие посты
- Личный опыт, публичные выступления, советы по организации и многое другое

А ещё я завёл бусти. Он пока пустой, но в ближайшее время я буду его наполнять по чуть чуть. Подписывайтесь, если хотите меня поддержать, я буду рад!
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет!
Начал писать цикл статей про многопоточность на разных уровнях абстракции. И представляю вам первую статью из этой серии - про процессор!
Интеграционное тестирование MongoDB. Точки отказа

Всем привет! Сегодня рассмотрим тему настроек MongoDB для интгерационного тестирования и узнаем, что такое FailPoint и как он может помочь проверить нашу логику в нестандартных ситуациях.

Часто нам приходится писать интеграционные тесты. Интеграционные тесты — это тесты, которые проверяют взаимодействие нашего кода с внешними системами (БД, внешние API и т. д.) в как можно более приближённых к реальности условиях.

Зачем нужны такие тесты? Ну, например, чтобы убедиться, что наша логика корректно обрабатывает ситуации, когда база данных возвращает ошибку создания индекса или отказывает в записи. Юнит-тесты здесь уже не справятся, ведь они не покрывают фактическое взаимодействие с “живой” MongoDB.

Test Commands в MongoDB
MongoDB поддерживает набор специальных команд для тестирования (testCommands), которые обычно отключены в продакшене. Среди них есть конфигурация FailPoint — «точка отказа», позволяющая искусственно вызывать сбои и ошибки в различных операциях.

Чтобы включить testCommands, достаточно при запуске MongoDB задать параметр enableTestCommands=1. Вот пример docker-compose для этой задачи:


services:
mongo:
image: mongo:6.0
command: mongod --setParameter enableTestCommands=1
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password


Для запуска команд нужно выбрать базу данных (для ConfigureFailPoint это база данных admin) и вызвать метод db.runCommand({}).

Определение ConfigureFailPoint состоит из следующих полей:
1. configureFailPoint - название точки отказа, которую мы настраиваем. Чаще всего встречается значение failCommand для настройки отказа конкретной команды
2. mode: определяет режим срабатывания точки отказа: alwaysOn, off, {times: n} и другие
3. data - является вложенным объектом, в котором указываются детали и условия сбоя

Пример использования для команды createIndexes:


use admin;
db.runCommand({
configureFailPoint: "failCommand",
mode: { times: 1 },
data: {
failCommands: ["createIndexes"],
errorCode: 100,
closeConnection: false
}
});


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


use admin;
db.runCommand({
configureFailPoint: "failCommand",
mode: "off"
});


В следующем посте мы рассмотрим пример интеграционного теста, который использует данную функциональность, а пока что можете погрузиться в вики-страничку про данный механизм.

Всем спасибо, с вами был Flexible Coding!
Интеграционное тестирование MongoDB. Точки отказа. .NET

Всем привет! Продолжаем тему точек отказа у MongoDb. С основами разобрались, а теперь приступим к написанию тестов на .NET.
Тесты разделяются на три этапа: Given (Arrange), When (Act) и Then (Assert). Так же в рамках теста может происходит подготовка окружения - Setup и очистка - TearDown.

В рамках конкретного теста точку отказа удобнее всего настраивать на этапе Setup. В NUnit это будет выглядеть следующим образом:

[SetUp] 
public void Setup() 

    // Подключаемся к локальной монге, где включены testCommands 
    var client = new MongoClient("mongodb://root:password@localhost:27017"); 
    _database = client.GetDatabase("testDb"); 
    _collection = _database.GetCollection<BsonDocument>("testCollection"); 
    _adminDatabase = client.GetDatabase("admin"); 
 
    // Включаем FailPoint для команды createIndexes 
    var enableFailPoint = new BsonDocument 
    { 
        { "configureFailPoint", "failCommand" }, 
        { "mode", new BsonDocument("times", 1) }, 
        { 
            "data", new BsonDocument 
            { 
                { "failCommands", new BsonArray { "createIndexes" } }, 
                { "errorCode", 100 }, // код ошибки (для проверки) 
                { "closeConnection", false } // соединение не рвём 
            } 
        } 
    }; 
 
    _adminDatabase.RunCommand<BsonDocument>(enableFailPoint); 
}


Далее - сама логика теста:

[Test] 
public void CreateIndexes_ShouldThrowException_WhenFailPointEnabled() 

    var ex = Assert.Throws<MongoCommandException>(() => 
    { 
        _collection.Indexes.CreateOne( 
            new CreateIndexModel<BsonDocument>( 
                Builders<BsonDocument>.IndexKeys.Ascending("someField"))); 
    }); 
 
    Assert.That(ex.Code, Is.EqualTo(100)); 
}


А затем возвращаем MongoDB в исходное состояние:

[TearDown] 
public void TearDown() 

    // Выключаем FailPoint, чтобы вернуть MongoDb в исходное состояние 
    var disableFailPoint = new BsonDocument 
    { 
        { "configureFailPoint", "failCommand" }, 
        { "mode", "off" } 
    }; 
 
    _adminDatabase.RunCommand<BsonDocument>(disableFailPoint); 
}


Таким образом можно настраивать необходимое поведение БД в рамках теста.

Flexible Coding
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
var functions = new List<Func<int, int>>();

for (int i = 0; i < 10; i++)
{
Func<int, int> function = x => i * x;
if (!functions.Contains(function))
{
functions.Add(function);
}
}

Console.WriteLine(functions.Count);