Moq & Interop Типы: работает в VS2012, не работает VS2010?

У меня есть проект библиотеки .NET с примерно 500 модульными тестами. Все эти тесты выполняются отлично в Visual Studio 2012. Однако некоторые из моих тестов не работают в Visual Studio 2010. В этих неудачных тестах я использую Moq, чтобы высмеять несколько типов Interop из Microsoft.Office.Interop.Excel. При попытке получить доступ к этим издеваемым типам взаимодействия тест немедленно не выполняется:

Error: Missing method 'instance class Microsoft.Office.Interop.Excel.Range [ExcelAddIn.Core] Microsoft.Office.Interop.Excel.ListRow::get_Range()' from class 'Castle.Proxies.ListRowProxy'.

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

_listRowMock.Setup(m => m.Range).Returns(_rangeMock.Object);

Теперь я могу представить, что Moq может не работать слишком хорошо с Interop Types. Но то, что я нахожу наиболее озадачивающим, заключается в том, что эти тесты отлично работают в Visual Studio 2012, но не работают в Visual Studio 2010.

Почему моя Visual Studio влияет на поведение моего кода?

ОБНОВЛЕНИЕ: 3-11-2012

Хорошо, поэтому я понял:

  • У меня есть два проекта; Core и Core.UnitTest. Core - это фактическая библиотека, а Core.UnitTest - это проект unit test библиотеки Core.
  • Оба проекта ссылаются на Microsoft.Office.Interop.Excel с включенными встроенными типами взаимодействия.
  • Поскольку EIT включен, оба проекта включают в себя их собственное "представление" библиотеки Microsoft.Office.Interop.Excel. Представление включает все классы, методы и свойства, которые используются в их соответствующем проекте.
  • Поскольку оба проекта используют разные классы, методы и свойства Microsoft.Office.Interop.Excel, встроенные типы обеих библиотек различаются. Например. ListRow в Core имеет свойство Index и Range, тогда как ListRow в Core.UnitTest имеет свойство Range.
  • Хотя оба типа отличаются друг от друга и не имеют общего интерфейса или суперкласса, они эквивалент. Это означает, что CLR будет обрабатывать их, как если бы они были одинаковыми, и позволит вам использовать эти типы по границам сборки. Например. экземпляр ListRow из Core.UnitTest будет отлично работать при передаче методу в библиотеке Core. Свойство общего диапазона будет функционировать, в то время как недостающее свойство Index будет генерировать исключение MissingMethodException при доступе.
  • Вышеупомянутое поведение даже работает с издеваемыми типами. Обманутый объект Mock [Excel.ListRow] отлично работает при пересечении границы сборки.
  • К сожалению, поведение, описанное в предыдущем пункте, работает только тогда, когда я строю свои сборки в Visual Studio 2012. Когда я создаю свои сборки в Visual Studio 2010 и отлаживаю свой код, я могу увидеть, как посмеянный экземпляр ListRow передается в метод моего основного проекта. В тот момент, когда экземпляр пересекает границу сборки, все методы и свойства ListRow теряют свою реализацию и бросают MissingMethodExceptions.
  • Теперь для забавной части мне действительно удалось смягчить эту проблему, убедившись, что оба встроенных типа ListRow выровнены. Например. чтобы компилятор мог создать тот же вид ListRow в обоих проектах, я убедился, что использовал те же самые методы и свойства в своем проекте UnitTest. Это означает добавление фиктивных строк, таких как: var dummy = listRow.Index. Как только у меня появился компилятор, создающий идентичные представления моего встроенного типа ListRow, экземпляру разрешалось пересекать границы сборки, не теряя при этом своей реализации.

Вопрос по-прежнему сохраняется: что вызывает эту разницу в поведении между Visual Studio 2010 и Visual Studio 2012?

ОБНОВЛЕНИЕ: 9-11-2012

Демо-решение: http://temp-share.com/show/KdPf6066h

Я создал небольшое решение для демонстрации эффекта. Решение состоит из библиотеки и проекта UnitTest. Оба относятся к Microsoft.Office.Interop.Excel.Range с включенным EIT. Тест отлично работает в VS2012, но генерирует MissingMethodException в VS2010. Раскомментирование фиктивной строки в тесте заставит ее работать в VS2010.

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ: 29-12-2012

Мои извинения за последнее обновление. Мой коллега нашел решение, однако я не смог воспроизвести его на своей машине. Тем временем наша компания сделала переход на TFS2012, так что это уже не проблема блокировки для меня. Два самых важных вывода, сделанных моим коллегой:

  • Семантика платформы "Любой процессор" изменилась с Visual Studio 2010 в Visual Studio 2012. Это приведет к .DLL создается в зависимости от того, используете ли вы VS2010 или VS2012.
  • Оба проекта ссылаются на разные версии Microsoft.Office.Interop.Excel.

Я проверил свои проекты и выпрямил ссылки, но это не имело никакого значения. После этого я пробовал различные варианты платформ как в VS2010, так и VS2012, но не смог добиться удовлетворительного результата. Я отвечу на ответ Джереми, поскольку он был самым полезным. Спасибо всем за помощь.

Ответ 1

Изменить:. Это работает для меня, когда я пытаюсь использовать его в Visual Studio 2012 и целевую .Net 4.0, используя только .Net PIA, а не COM ref. Такое же решение не работает в VS2010.

VS2010 загружает версию 10.0.30319.1 из Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll's и VS2012 загружает версию 11.0.50727.1. Вы можете увидеть другую версию в окне "Модули".


Мне удалось заставить его работать в VS2010:

enter image description here

Вот мое решение http://temp-share.com/show/Pf3Ypip62 для каждого удобства. В него включены все ссылки Moq. У меня Excel 2007 (т.е. v12) - поэтому, пожалуйста, отредактируйте ссылки на Office 14.

Проект с проверенными методами должен использовать PIA Microsoft.Office.Interop.Excel через вкладку ссылок .Net.

В проекте Unit Test вы должны использовать Microsoft Excel 1X.0 Object Library через вкладку ссылок COM - ее ActiveX.

Сложная вещь в обозревателе решений, которую они оба называют: Microsoft.Office.Interop.Excel

Есть еще одна оговорка, о которой я не знаю, как обходиться - вы должны использовать инфраструктуру .Net 3.5 , и я действительно надеялся, что Microsoft исправила ее в 2012 году, когда вы нашли, потому что я не могу работать, как сделать это со ВСЕМИ проектами в .Net 4.0. Некоторые решения со смешанными проектами, ориентированными на .NET 3.5 и 4.0, в порядке.

У меня было много проблем с этим, см. здесь Как избежать использования динамического при издевательстве в Excel.workheet?, а также увидеть этот вопрос, который я задал: Вредоносный объект не имеет всех свойств, показанных в Intellisense, в одном проекте, но имеет их в другом.

В любом случае, так это, как заставить его работать в VS 2010. Я рад, что он разрешен в 2012 году!

Ответ 2

Я попытался воспроизвести это, и для меня это не работает даже в VS 2012.

Когда вы компилируете проект, используя "Embed Interop Types", компилятор С# генерирует внутренний тип, который имеет только те элементы, к которым вы обращаетесь, и реализация на самом деле, похоже, использует IDispatch для вызова метода объекта COM по id.

Из вашего описания я понимаю, что ваш тестовый проект в VS 2012 не имеет доступа к свойствам (даже не для их издевательства), но тест по-прежнему преуспевает, и сгенерированный тип в тестовом проекте имеет эти члены.

Если это действительно то, что вы испытываете, можете ли вы посмотреть содержимое вашей тестовой DLL и посмотреть, как был создан тип взаимодействия? Вы можете использовать такой инструмент, как ildasm.exe.

Если тип interop в test.dll содержит все члены, даже те, к которым у вас нет доступа в тесте, это может дать понять, что разница связана с тем, как типы interop генерируются в VS 2012.

Кроме того, если вы можете приложить небольшое минимальное решение VS 2012, которое воспроизводит проблему, это может значительно помочь в диагностике этого.

Ответ 3

Прежде всего, проверяйте при добавлении библиотеки в проект в VS2010, убедитесь, что вы создали макет-объект, например

Mock<DocumentService> _mock = new Mock<DocumentService>();

Также .NET 4.0 позволяет встраивать первичные сборки interop в вашу сборку, чтобы вам не нужно было развертывать их вместе с вашим приложением. Откройте вкладку свойств в сборке в VS2010 и проверьте типы Embed Interop. правда.

И чтобы создать экземпляр excel, Excel.Application xlapp = new Excel.Application();

Надеюсь, что это сработает.

Ответ 4

Я обнаружил, что, по крайней мере, в VS2015, я все еще мог вставлять типы interop в свою тестируемую сборку, но установить "Embed" в false на ссылку сборки PIA в моем тестовом проекте, и я не повторяю это.