Привет, я хочу иметь возможность генерировать различные значения на основе ICustomization
используя ISpecimenBuilder.CreateMany
. Мне было интересно, какое решение будет лучшим, поскольку AutoFixture будет генерировать одинаковые значения для всех объектов.
`` С #
открытый класс FooCustomization: ICustomization
{
public void Customize (прибор IFixture)
{
var specimen = fixture.Build
.OmitAutoProperties ()
.With (x => x.CreationDate, DateTime.Now)
.With (x => x.Identifier, Guid.NewGuid (). ToString (). Substring (0, 6)) // Должны генерироваться разные значения
.With (x => x.Mail, $ "[email protected]")
.Создавать();
fixture.Register(() => specimen);
}
}
`` ''
PS .: Основная цель - уменьшить объем кода в моих тестах, поэтому я должен избегать вызова With()
для настройки значений для каждого теста. Есть ли правильный способ сделать это?
Обновление: я только что прочитал этот ответ от @ploeh : https://github.com/AutoFixture/AutoFixture/issues/361#issuecomment -86873060, это может быть то, что я ищу.
У этого подхода много недостатков: во-первых, вызов Create<List<Foo>>()
кажется действительно нелогичным, потому что это как бы противоречит тому, чего ожидать от CreateMany<Foo>
; который сгенерирует список с жестко заданным размером> (?). Другой недостаток состоит в том, что мне пришлось бы иметь две настройки для каждой сущности; один для создания пользовательских коллекций, а другой - для создания одного экземпляра, поскольку мы переопределяем поведение
Create<T>
для создания коллекций.
Это кажется хорошим кандидатом на вопрос о переполнении стека . Нет ничего плохого в том, чтобы задать здесь вопрос, но я думаю, что этот вопрос относится к тому типу, где ответ может принести пользу и другим людям, и легче найти вопросы и ответы на Stack Overflow по сравнению с проблемами GitHub. Во-первых, Google хорошо индексирует вопросы о переполнении стека, тогда как кажется, что он оценивает (закрытые) проблемы GitHub ниже ...
@ploeh Я с тобой согласен ....
Я только что создал вопрос SO, основанный на этой проблеме. Это ссылка для будущих ссылок 👍
@ploeh Спасибо, что
@thiagomajesk На самом деле здесь нет проблем с
@zvirja Спасибо, что разъяснили это 😄
@zvirja Я собираюсь закрыть вопрос о SO на основе отличного ответа
Но я хотел бы попросить немного больше информации о деталях реализации на этом.
Поскольку я не хочу еще больше отклоняться от темы, я думаю, что было бы лучше спросить об этой специфике здесь. Итак, у меня есть такая реализация:
`` С #
открытый класс UniqueShortGuidBuilder
{
частная строка только для чтения propName;
частный только для чтения, длина;
public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
propName = ((MemberExpression)expr.Body).Member.Name;
this.lenght = lenght;
}
общедоступный объект Create (запрос объекта, контекст ISpecimenContext)
{
var pi = запрос как PropertyInfo;
if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
return new NoSpecimen();
return Guid.NewGuid().ToString().Substring(0, lenght);
}
`` ''
Я ожидал, что при создании множества объектов с помощью AutoFixture ISpecimenBuilder.Create
будет вызываться несколько раз, генерируя отдельные Guids для моих объектов; но, видимо, это неправда, и на самом деле они разделяют одно и то же.
Что ж, я только что протестировал ваш код, и он сработал именно так, как хотелось:
`` С #
открытый класс Foo
{
общедоступная строка PropertyToCustomize {получить; установленный; }
}
открытый класс UniqueShortGuidBuilder
{
частная строка только для чтения propName;
частный только для чтения, длина;
public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
propName = ((MemberExpression) expr.Body).Member.Name;
this.lenght = lenght;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
return new NoSpecimen();
return Guid.NewGuid().ToString().Substring(0, lenght);
}
}
[Факт]
public void TestCustomization ()
{
var fixture = new Fixture ();
fixture.Customizations.Add (новый UniqueShortGuidBuilder
var manyFoos = fixture.CreateMany<Foo>(10);
var uniqueIds = manyFoos
.Select(x => x.PropertyToCustomize)
.Where(x => x.Length == 5)
.Distinct()
.ToArray();
Assert.Equal(10, uniqueIds.Length);
}
`` ''
Поэтому я хотел бы еще раз попросить вас посмотреть, чем отличается ваш реальный код, чтобы мы могли понять, почему эта разница имеет значение 😟
@zvirja Это странно. Приведенный ниже код является минимальным воспроизведением проблемы, с которой я столкнулся.
Если я удалю Fixture.Customize(new FooCustomization());
тест будет пройден, в противном случае - нет.
`` С #
используя Систему;
с помощью Microsoft.VisualStudio.TestTools.UnitTesting;
с использованием AutoFixture.Kernel;
using System.Linq.Expressions;
using System.Reflection;
с помощью AutoFixture;
using System.Linq;
пространство имен UnitTestProject1
{
открытый класс Foo
{
общедоступная строка PropertyToCustomize {получить; установленный; }
}
public class FooCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
var specimen = fixture.Build<Foo>()
.OmitAutoProperties()
.With(x => x.PropertyToCustomize)
.Create();
fixture.Register(() => specimen);
}
}
public class UniqueShortGuidBuilder<TEntity> : ISpecimenBuilder
{
private readonly string propName;
private readonly int lenght;
public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
propName = ((MemberExpression)expr.Body).Member.Name;
this.lenght = lenght;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
return new NoSpecimen();
return Guid.NewGuid().ToString().Substring(0, lenght);
}
}
[TestClass]
public class UnitTest1
{
public static Fixture Fixture { get; private set; }
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
Fixture = new Fixture();
Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.PropertyToCustomize, 5));
Fixture.Customize(new FooCustomization());
}
[TestMethod]
public void TestMethod1()
{
var manyFoos = Fixture.CreateMany<Foo>(10);
var uniqueIds = manyFoos
.Select(x => x.PropertyToCustomize)
.Where(x => x.Length == 5)
.Distinct()
.ToArray();
Assert.AreEqual(10, uniqueIds.Length);
}
}
}
`` ''
Рекомендация: если вы полагаетесь на извлечение первых символов _n_ из строки GUID, не называйте класс UniqueShortGuidBuilder
. Значения могут быть довольно уникальными, если длина равна 5, но если вы установите ее на 1, вы, скорее всего, увидите столкновения. В любом случае уникальность не гарантируется.
Если вас интересует только длина строки плюс негарантированная уникальность, почему бы просто не попросить AutoFixture указать число, скажем, от 0 до 99 999, и не превратить его в строку? IIRC, числа уникальны, пока диапазон не исчерпан.
Если я удалю Fixture.Customize (новый FooCustomization ()); тест пройдёт, иначе нет.
Что ж, на самом деле это ожидаемое поведение, если вы посмотрите на ситуацию более внимательно 😅 См. Мой ответ в # 962. Предоставленная реализация FooCustomization
создает экземпляр типа Foo
и настраивает прибор так, чтобы он всегда возвращал этот экземпляр. Следовательно, когда позже вы создаете несколько экземпляров типа Foo
, каждый раз возвращается один и тот же объект и ваш конструктор больше не вызывается.
Если вы хотите, чтобы новый экземпляр создавался каждый раз, когда вы запрашиваете Foo
, используйте Customize<>
API, как я предлагал здесь .
@ploeh Спасибо за подсказку, но этот код - всего лишь пример для тестирования. В реальных случаях длина не будет слишком короткой, чтобы повлиять на уникальность Guids 😄.
Если вас интересует только длина строки плюс негарантированная уникальность, почему бы просто не попросить AutoFixture указать число, скажем, от 0 до 99 999, и не превратить его в строку?
Бьюсь об заклад, это хорошее решение для большинства случаев, но я не могу этого сделать в данном конкретном случае из-за буквенно-цифровых проверок в моей системе. Кроме того, у меня есть более сложные проверки, связанные с этим полем.
Что ж, на самом деле это ожидаемое поведение, если вы посмотрите на ситуацию более внимательно.
@zvirja Это ясно после вашего ответа в здесь ) 😁.
В заключение благодарим вас за терпение и прекрасные объяснения.
Я уверен, что это будет полезно и для других в будущем 😉.
Хорошо, честно, у меня может быть склонность воспринимать вещи слишком буквально 😄