Я использую Moq, чтобы издеваться над некоторыми объектами ранней связи CRM, чтобы я мог тестировать свои плагины. Я хочу подделать или издеваться над Активной учетной записью, но проблема в том, что statecode
- это только чтение, а не виртуальное поле. Например, когда я пытаюсь дразнить Account
ранней переплет сущности, чтобы указать, что он должен вернуться, если его statecode
доступен я получаю:Mocking/Faking Active CRM Entity
var accountMock = new Mock<Account>();
accountMock.SetupGet(x => x.statecode).Returns(AccountState.Active);
и NotSupportedException
брошен: Invalid setup on a non-virtual (overridable in VB) member: x => x.statecode
. Это происходит потому, что в раннем классе обертки для учетной записи, предоставленной SDK, поле statecode
не является виртуальным. Мок не может переопределить его, как я прошу его сделать! Я подумал: «Почему бы не сделать обертку для класса Account
?».
Я мог изменить сгенерированный код, установив атрибут каждого объекта, я хочу, чтобы дразнить к virtual
statecode
, но это не будет придерживаться вокруг, когда/если объектные обертки регенерируют. Это также не похоже на путь Мока, но я могу ошибаться.
В настоящее время я работаю с откатом, чтобы прочитать XML-сериализованную учетную запись, которая уже активна из файла, но это действительно побеждает цель насмешки, поскольку у меня в основном есть образец файла данных для чтения. Это работает, но это не насмешка.
Мое самое многообещающее мероприятие заключалось в создании обертки TestAccount, которая расширила учетную запись и заставила ее выглядеть так, как вы могли бы установить, а также получить statecode
. Это было самым многообещающим, потому что я мог на самом деле высмеять этот класс TestAccount, сообщив об этом службе OrganizationService с активными statecode
и statuscode
(что он и сделал!) И подтвердите, что когда он был типа Entity
, у него были правильные поля. Он не удался, когда экземпляр TestAccount был в конечном итоге преобразован в ранний тип учетной записи. Настройка statuscode
застряла, но настройка statecode
не предполагала, потому что нет публичного сеттера для statecode
, как есть для statuscode
.
Я объясню с помощью кода!
// Wrapper class for Account so I can mock active and inactive Accounts by changing the statecode and statuscode
public class AccountWrapper : Account
{
// the member to store our "set statecode" values; only for use in testing and mocking
private AccountState? _statecode;
// override and replace the base class statecode
public new virtual AccountState? statecode
{
get { return _statecode; }
// this is how I intend to get around the read-only of this field in the base class
// the wrapper pretends to allow the statecode to be set, when it really does not stick on the actual Account entity
set { _statecode = value; }
}
}
И конкретный случай установки для организации службы издеваться, чтобы вернуть активный счет, когда Retrieve счета называется:
var activeAccountMock = new Mock<AccountWrapper>();
activeAccountMock.SetupGet(x => x.statecode).Returns(AccountState.Active);
var serviceMock = new Mock<IOrganizationService>();
serviceMock.Setup(t =>
t.Retrieve(It.Is<string>(s => s == Account.EntityLogicalName),
It.IsAny<Guid>(), // don't care about a specific Account
It.IsAny<ColumnSet>())) // don't care about a specific ColumnSet
.Returns(activeAccountMock.Object);
Когда я осмотреть объект, возвращаемый методом service.Retrieve, он делает почти точно, что я хочу! Учетная запись типа Entity
имеет statecode
так, как я хочу, но в тот момент, когда предприятие преобразовано в Account
, statecode
возвращается к нулевому значению. Вероятно, это связано с тем, что преобразование вызывает конструктор учетной записи, который создает объект «Учетная запись» со всеми полями null, а затем устанавливает все поля, которые имеют общедоступный сеттер со значениями, доступными. В принципе, когда Учетная запись является поздней, это то, что я хочу почти, и когда она ранняя, я теряю значение своего состояния, которое я установил.
// this guy is type Entity, and its statecode is what I want!
var accountLateBound = service.Retrieve(Account.EntityLogicalName, accountId, new ColumnSet(true));
// accountLateBound["statecode"] is AccountState.Active YAY!
// this clobbers my glorious statecode mock...
Account accountEarlyBound = accountLateBound.ToEntity<Account>();
// accountEarlyBound.statecode is null BOO!
Мой код продукция использует ранние переплете объектов исключительно по уважительной причине - в основном развитие с ранней оценкой не отстой (Тары быть intellissense, компилятор проверяет, и т.д.). Я не хочу менять производственный код так, чтобы он лучше менялся с помощью Moq. Это и ранние сущности удивительны!
Я что-то не так с Moq?Нужно ли мне сосать его и использовать AccountWrapper в моем производственном коде? Должен ли я не конвертировать в раннюю границу в этом случае, чтобы я не потерял этот код состояния? Тогда мне пришлось бы изменить производственный код, чтобы смешивать поздний и ранний ... yuck. Я беспокоюсь об этом, поскольку оболочка дает идею, что вы можете установить код состояния объекта напрямую через account.statecode = AccountState.[Active|Inactive]
, а не использовать SetStateRequest
. I это не так, комментариев объяснить это не так, но факт, что он выглядит, как вы можете, означает, что кто-то это сделает и ожидает, что он сработает.
Вся идея насмешки логики плагина была так, что мне не нужно было вообще обращаться к CRM! Я мог высмеивать все, что мне нужно было использовать. Когда блок тестирования логики плагинов, нет никаких причин, чтобы использовать реальные данные
Резиновая уточка уже нечестным надоело слушать меня ...
Т.Л., д-р - Могу ли я издеваться только для чтения statecode из раннего связанного объекта CRM, чтобы я мог выполнить единичный тест с объектами различных кодов состояний с использованием Moq и наследования/обертки/интерфейсы, если это необходимо? Если да, то как? Если нет, то почему?
Т.Л., д-р я не могу говорить с конкретной методикой обновления или создания записи таким образом, но глубоко в моем нечеткой мозга я припоминаю, вы должны (или, может быть, он «должен») всегда устанавливается как statecode, так и statuscode одновременно, так как код состояния зависит от кода состояния (как подкатегория, эффективно), поэтому, обновляя вместе, вы избегаете проблемы их смещения – AdamV
Интересно ... Я займусь этим. I_think_ существует только один конструктор для ранней привязки сущности, и он не принимает никаких параметров. – gfritz