Думаю, большинство читателей согласится, что автоматизированное тестирование - полезный, а во многих областях даже необходимый, этап создания программ. А так как программисты - народ ленивый, то и инструментов, облегчающих этот этап существует немало. Одним из таких инструментов является AutoFixture - средство для генерации тестовых экземпляров. Этот инструмент уже не раз упомянался на Хабре, например тут. Далее я расскажу, с какой проблемой столкнулся в попытке применить AutoFixture в своей работе и как решил эту проблему. Вкратце напомню, как выглядит использование AutoFixture на практике. Как видно из приведённого выше примера, инструмент способен создавать и встроенные типы, и пользовательские, и коллекции произвольных типов. Главное - чтобы у них был доступен конструктор, а типы его параметров, в свою очередь подходили под эти же условия. Мне в работе понадобилось создавать тестовые данные типов gRPC сообщений. Сами эти типы генерируются автоматически по proto-файлам. Для начала, давайте создадим экземпляр сообщения для такого контракта: Пока всё работает: экземпляр создаётся, свойство инициализируется непустой строкой, класс! Попробуем добавить поле с атрибутом repeated. По спецификации protobuf такие поля могут иметь любое количество элементов. Бам!!! Что случилось? Коллекция LuckyNumbers в экземпляре сгенерированного типа оказывается пустой. Дело в том, что AutoFixture по умолчанию инициализирует экземпляр типа, вызывая его конструктор, а затем все доступные сеттеры свойств. А repeated-поля контракта становятся свойствами, у которых есть только геттер, а сеттера нет: Из кода видно, что у свойства LuckyNumbers отсутсвует доступный сеттер, поэтому-то AutoFixture и не смог заполнить коллекцию элементами! Быстрое "гугление" подсказало, что можно покрутить настройки AutoFixture таким образом: Такая настройка должна сообщить инструменту, что нужно заполнять свойства-коллекции даже если у них отсутствует доступный сеттер. Лишь бы был геттер, да метод Add у коллекции. Пробуем: и получаю Бам №2!!! : Тут я, признаюсь, немного приуныл. Затем решил проверить, в чём же дело: в AutoFixture или в сгенерированном по контракту коде. Для этого я набросал небольшой класс с таким же свойством без сеттера с той лишь разницей, что в этот раз типом коллекции был простой List<int>. На этот раз никакого исключения не вылетело, в коллеции лежали элементы, как и положено. Подозрение, что в прошлый раз исключение появилось из-за особенностей класса RepeatedField<T> всё крепло. Я зарылся в отладчик, пытаясь понять, что же такого неоднозначного (ambiguous) было в RepeatedField, чего не было у List. В отладчике ставлю точку останова на исключение System.Reflection.AmbiguousMatchException. Довольно быстро выяснилось, что исключение происходит в методе InstanceMethodQuery.SelectMethods. Благо, исходный код инструмента открыт, привожу текст метода: И при этом MethodName имеет значение "Add". Обозреватель сборок в Rider-е показал (см. картинку), что у типа RepeaterField есть два публичных метода Add: один для одиночного элемента, другой - для их последовательности. Поэтому-то AutoFixture не мог выбрать, какой именно метод ему нужен и падал с ошибкой. А если точнее, то падал метод GetMethod в кишках дотнетовского рантайма. Ну что же, причина проблемы стала ясна. Оставалось придумать решение. Я решил добавить в AutoFixture дополнительную настройку, позволяющую инициализировать именно экземпляры типа RepeatedField<T>. По счастью, у этого злополучного типа оказался метод AddRange, который я и собрался использовать для наполнения коллекции. Я решил идти проверенным методом copy-paste и продублировать код ReadonlyCollectionPropertiesBehavior, меняя его лишь по необходимости. Оказалось, что менять придётся совсем немного: поиск подходящего метода инициализации (того самого AddRange) и подготовку параметров для него. Потому что если ReadonlyCollectionPropertiesBehavior заполнял коллекцию поэлементно, вызывая Add, то мне предстояло сперва подготовить последовательность элементов, и лишь затем единожды вызвать AddRange, передав её всю целиком. Тут уже никаких сложностей не осталось. Готовое решение можно найти в моём репозитории на гитхабе. Я благодарен авторам AutoFixture за такой полезный инструмент и призываю всех шарпистов рассмотреть возможность использовать его в своей практике.