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 в моем тестовом проекте, и я не повторяю это.