Как можно использовать оператор равенства для байтовых массивов в Linq-to-Entities во время модульного тестирования?

1

В моей базе данных есть несколько столбцов таблицы varbinary которые мне нужно проверить на равенство с байтовым массивом, все происходит через Entity Framework и Linq-to-Entities. Для Linq-to-Objects это очень легко достигается с помощью метода расширения SequenceEquals, однако EF не поддерживает его, к сожалению, поэтому я полагаюсь на оператор == который правильно переводится в SQL-запрос.

Например

byte[] deviceId = GetDeviceId();

Device device = _deviceRepository.QueryAll() //backed by a DbContext, returns IQueryable<Device>
.Include(d => d.RelatedEntity1)
.Include(d => d.RelatedEntity2)
.Include(d => d.RelatedEntityEtc)
.FirstOrDefault(d => d.DeviceUniqueIdentifier == deviceId && ...);

LoginUserOnDevice(device);

Однако проблема заключается в том, что при модульном тестировании фрагмента кода с использованием запроса Linq-to-Entities с поддельным _deviceRepository, для которого я использую обычный List<T> преобразованный через .AsQueryable() в IQueryable<T>, оператор равенства применяется с его исходной семантикой, то есть ссылочным равенством, поэтому сравнение явно терпит неудачу. Конечно, это не происходит строго для модульного тестирования, но в любой ситуации, когда запрос linq больше не выполняется для источника данных SQL.

Какой был бы оптимальный подход здесь? Предпочтительно, не меняя код запроса, поэтому он корректно работает как при опросе реальной базы данных (с использованием EF 6), так и при модульном тестировании. Материализация всей коллекции в память путем вызова .ToList() сразу после Include или чего-либо в этом направлении не может быть и речи в моем сценарии.

Теги:
unit-testing
entity-framework
linq-to-entities

1 ответ

2

Вы можете и должны избегать всего этого. Вместо того, чтобы выполнять запрос в вашем поддельном репозитории, вы должны позволить ему просто вернуть данные, необходимые для вашего теста; в противном случае вы в основном пытаетесь проверить, выполняет ли EF свою работу (хотя здесь вы издеваетесь над ней).

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

Если вы действительно хотите протестировать свой запрос LINQ, вы в первую очередь смотрите на тест интеграции: вы тестируете, как ваша система взаимодействует с внешним. На этом этапе должно быть очевидно, что вам также не понадобится поддельный репозиторий, поскольку это не будет проверять взаимодействие с вашей (dev) базой данных.

Это действительно препятствие для работы с устаревшим кодом, который препятствует правильному тестированию. Как вы заметили, вы больше не можете делать это так, как хотите (и как обычно вы это делаете: возвращая макет данных из вашего репозитория, используя тот же запрос LINQ).

Если бы можно было заставить запрос вести себя так, как вы хотите (что я очень не уверен), это будет слишком много хлопот. Вместо этого я предлагаю вам сделать это, чтобы издеваться над вызовом в ваш репозиторий, вместо того, чтобы обрезать сам репозиторий. Цепочка ответственности была нарушена путем помещения запроса за пределы репозитория, поэтому изменение хранилища не решит вашу проблему.

Я не совсем уверен, что это будет вести себя так, как я хочу, чтобы так поиграть с ним немного, но я считаю, что это должно сделать это (используя Moq):

var device = new Device(id: 5, name: "washingmachine", price: 5000);
var repository = new Mock <IDeviceRepository>();
repository.Setup(x => x.QueryAll())
          .Returns(device);

Если теперь вы выполните тест с запросом LINQ, он будет игнорировать все, что он делает, и вместо этого вернет ваше предопределенное device.

  • 0
    Это хороший совет, если бы это был более ранний период в жизни моего проекта, я мог бы изменить хранилище так, чтобы оно возвращало строго IEnumerable<T> вместо IQueryable<T> с соответствующими методами, но сейчас уже немного поздно. Это цена, которую я плачу за сильную связь модели предметной области с моделью сущностей. Кроме того, я на самом деле не тестирую запрос EF, я на самом деле хочу заглушить хранилище устройства, чтобы сам запрос не мешал, а затем я использую извлеченное (поддельное) устройство, чтобы утверждать вещи дальше.
  • 0
    Не могли бы вы отредактировать ваше сообщение с более сложным примером кода? Неясно, где именно находится этот запрос LINQ.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню