Moq & Interop Types: работает в VS2012, не работает в VS2010?

У меня есть проект библиотеки .NET с примерно 500 модульными тестами. Все эти тесты работают нормально в Visual Studio 2012. Однако некоторые из моих тестов не проходят в Visual Studio 2010. В этих неудачных тестах я использую Moq для имитации нескольких типов взаимодействия из 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'.

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

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

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

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

ОБНОВЛЕНИЕ: 3 ноября 2012 г.

Хорошо, так что я понял это:

  • У меня есть два проекта; Ядро и Core.UnitTest. Core — это фактическая библиотека, а Core.UnitTest — проект модульного тестирования библиотеки 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 будет нормально работать при передаче методу в основной библиотеке. Общее свойство Range будет работать, тогда как отсутствующее свойство Index вызовет исключение MissingMethodException при доступе.
  • Вышеупомянутое поведение работает даже с фиктивными типами. Смоделированный объект Mock[Excel.ListRow] будет нормально работать при пересечении границы сборки.
  • К сожалению, поведение, описанное в предыдущем пункте, работает только при сборке сборок в Visual Studio 2012. Когда я создаю свои сборки в Visual Studio 2010 и отлаживаю свой код, я вижу, как фиктивный экземпляр ListRow передается в метод моего основного проекта. В тот момент, когда экземпляр пересекает границу сборки, все методы и свойства ListRow теряют свою реализацию и вызывают исключения MissingMethodException.
  • Теперь самое интересное: мне действительно удалось смягчить эту проблему, убедившись, что оба встроенных типа ListRow выровнены. Например. чтобы компилятор создавал одно и то же представление ListRow в обоих проектах, я убедился, что в моем проекте UnitTest используются одни и те же методы и свойства. Это означает добавление фиктивных строк, таких как: var dummy = listRow.Index. Как только компилятор создал идентичные представления моего встроенного типа ListRow, экземпляру было разрешено пересекать границы сборки без потери своей реализации.

Тем не менее, остается вопрос: в чем причина такой разницы в поведении Visual Studio 2010 и Visual Studio 2012?

ОБНОВЛЕНИЕ: 11 сентября 2012 г.

Демонстрационное решение: http://temp-share.com/show/KdPf6066h< /а>

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

ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ: 29 декабря 2012 г.

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

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

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


person Martin Devillers    schedule 02.11.2012    source источник
comment
Различные версии Microsoft.Office.Interop.Excel?   -  person Claus Jørgensen    schedule 02.11.2012
comment
Не вызовет ли это ошибок времени компиляции?   -  person Martin Devillers    schedule 02.11.2012
comment
Не обязательно, если подписи совпадают. Ваш проект UT может иметь несоответствие по сравнению с обычным проектом. Откройте файлы MSTest и проверьте.   -  person Claus Jørgensen    schedule 03.11.2012
comment
Интересно, что оба проекта ссылались на разные версии Excel. Однако проблема остается после исправления ссылок. Я заглянул внутрь своих сборок с помощью дизассемблера, но существенных различий не заметил. Я обнаружил, что сборка в VS2010, а затем тестирование в VS2012 приведет к той же ошибке. Таким образом, кажется, что VS2010 строит неработающий UT, тогда как VS2012 строит работающий UT. Любые идеи о том, как отладить это?   -  person Martin Devillers    schedule 03.11.2012
comment
Вы запускаете тесты внутри Visual Studio? В VS2012 используется другое средство запуска тестов, чем в VS2010. Поэтому, если у вас есть обновленная версия R# или конфигурация CI, вы, вероятно, неправильно запускаете тесты.   -  person Claus Jørgensen    schedule 03.11.2012
comment
Да, я запускаю тесты в Visual Studio. Я заметил следующее поведение при отладке моих тестов. В моем тесте, когда я смотрю на издевательский ListRow, я вижу, что у него есть одно свойство Range с работающим геттером. Издевательский объект передается в метод, который я хочу протестировать. Когда я вхожу в этот метод (который находится в другой сборке), мой имитируемый объект внезапно имеет больше свойств. Однако в VS2012 свойство Range все еще работает. В VS2010 этого нет. Все дополнительные свойства генерируют MissingMethodExceptions, как и следовало ожидать.   -  person Martin Devillers    schedule 03.11.2012
comment
@JeremyThompson Это странно, я только что проверил создание награды за одну из ваших приписанных наград, и это сработало. Вы видите кнопку «начать вознаграждение»? Это потому, что вы заработали первоначальную награду? В любом случае, спасибо, что ответили мне. Я также спрошу об этом одного из гуру C# в нашем офисе на следующей неделе.   -  person Martin Devillers    schedule 12.11.2012
comment
Я надеюсь, @EricLippert не грубо просит вас взглянуть. Пример 10-строчного репродукции в моем ответе.   -  person Jeremy Thompson    schedule 24.11.2012
comment
Большое спасибо за обновление щедрости. У меня не было времени связаться с нашим местным умником, но я сделаю это на следующей неделе.   -  person Martin Devillers    schedule 25.11.2012
comment
Ок, отлично. У меня осталось 14 часов, чтобы присудить награду :)   -  person Jeremy Thompson    schedule 26.11.2012


Ответы (4)


Редактировать: Это работает для меня, когда я пробую это в 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:

введите здесь описание изображения

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

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

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

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

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

У меня было много проблем с этим, см. здесь Как мне избежать использования dynamic при насмешке над Excel.worksheet?, а также посмотреть этот вопрос, который я задал: У фиктивного объекта не все свойства, показанные в Intellisense - в одном проекте, но они есть в другом.

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

person Jeremy Thompson    schedule 11.11.2012
comment
Большое спасибо, Джемери Томпсон, за ваш ответ. Однако что заставляет тест работать в VS2012 и не работать в VS2010? Это другая версия UnitTestFramework? Если да, то как я могу заставить его работать в VS2012, сначала скомпилировав в VS2010, а затем выполнив тест в VS2012? Разве это не означает, что это проблема времени компиляции, а не проблема времени выполнения? - person Martin Devillers; 12.11.2012
comment
Dll загружаются динамически. Проблема в том, что каждая версия VS загружает другую версию .dll. Неважно, какую версию вы используете для компиляции, важно только, какая версия .dll загружается во время выполнения. При выполнении в VS2012 загружается правильная версия .dll, чтобы предотвратить появление проблемы. Если бы был способ заставить VS2010 загружать ту же версию UnitTestFramework .dll, что и VS2012, при выполнении .exe, вы бы тоже решили проблему. - person ThunderGr; 24.11.2012
comment
@ThunderGr Большое спасибо за участие, OP и я отчаянно нуждаемся в документации, объясняющей обновления в версии 11.0.50727.1 Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll. Не только Moq, у других фреймворков Mocking такая же проблема. ИМХО, что-то подозрительное происходит с Embed Interop Types enabled в VS2010 по сравнению с VS2012, вы случайно не знаете причину? - person Jeremy Thompson; 24.11.2012
comment
Возможно, этот вопрос может помочь вам определить другую версию для VS2010. К сожалению, MS выпускает информацию только для того, что им нужно, и, похоже, они ничего не выпустили об этой конкретной .dll. - person ThunderGr; 24.11.2012
comment
Это не работает в данной ситуации. Я включил downloadable repro в свой ответ, чтобы люди воспроизвели проблему, а не догадывались. к вашему сведению: вот почему я связался с Эриком Липпертом - person Jeremy Thompson; 24.11.2012
comment
@JeremyThompson Приношу свои извинения за бездействие. Коллега, о котором я говорил, действительно сумел найти причину и решение проблемы. У меня не было времени написать хороший ответ, но я планировал сделать это в ближайшее время. Если я не сделаю этого к концу этого года, я приму ваш ответ :-) - person Martin Devillers; 27.12.2012

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

Когда вы компилируете проект с использованием «встроенных типов взаимодействия», компилятор C# генерирует внутренний тип, который имеет только члены, к которым вы обращаетесь, и реализация фактически использует IDispatch для вызова метода COM-объекта по идентификатору.

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

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

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

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

person Ran    schedule 08.11.2012
comment
Я добавил минимальное решение VS на свой вопрос. Мои тесты работают нормально в VS2012, но не работают в VS2010. Минимальное решение демонстрирует такое поведение. - person Martin Devillers; 09.11.2012
comment
@MartinDevillers Я думаю, что это проблема с кодом, сгенерированным компилятором. Если вы укажете 0 вместо _rangeMock.Object в настройке, он пройдет. Вы проверяли свои сборки другим браузером/декомпилятором ILSpy? как вы упомянули, исключение подразумевает, что я забыл настроить соответствующий метод получения свойств на моем макете. - person hridya pv; 09.11.2012

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

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

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

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

Надеюсь получится..

person hridya pv    schedule 08.11.2012
comment
Спасибо за ваш ответ. Однако ваш ответ не соответствует заданному вопросу. Я умею имитировать типы и уже использую Embed Interop Types. Проблема заключается в использовании двух разных сборок, для которых включены Embed Interop Types. - person Martin Devillers; 08.11.2012
comment
Упс... Я только что прочитал заголовок и дал ответ, извините. Теперь прочитаю весь вопрос. - person hridya pv; 09.11.2012

Я обнаружил, что, по крайней мере, в VS2015 я все еще мог встраивать типы взаимодействия в мою тестируемую сборку, но установил для параметра «Встроить» значение false в ссылке на сборку PIA в моем тестовом проекте, и я не воспроизвел это.

person Heath    schedule 13.01.2017