Как я могу издеваться над коллекцией с помощью Moq

Я новичок в модульном тестировании и насмешливости и все еще мокрые за ушами. Я использую структуру Moq, и мне нужно высмеять коллекцию таким образом, чтобы она давала один элемент со значением, которое я предоставляю.

Соответствующий класс коллекции - это System.Configuration.SettingsPropertyCollection, содержащий объекты SettingsProperty.

В свою очередь, SettingsProperty имеет свойство Attributes, которое возвращает SettingsAttributeDictionary.

Мне нужна моя коллекция, чтобы получить один SettingsProperty, который имеет один настраиваемый атрибут (полученный от System.Attribute) в своем Attributes.SettingsAttributeDictionary.

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

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

    [TestMethod]
    public void GetPropertySettings_Should_Return_Default_Values_When_Device_Not_Registered()
        {
        const string deviceName = "My.UnitTest";
        const string deviceType = "Switch";
        var deviceId = String.Format("{0}.{1}", deviceName, deviceType);
        Mock<IProfile> mockProfile = new Mock<IProfile>();
        Mock<SettingsContext> mockSettingsContext = new Mock<SettingsContext>();
        // Construct a SettingsPropertyCollection populated with a single property.
        // The single property will have a fixed name and default value, and will also have a single
        // attribute, giving teh ASCOM DeviceId.
        var deviceAttribute = new ASCOM.DeviceIdAttribute(deviceId);
        var attributes = new SettingsAttributeDictionary();
        attributes.Add(typeof(DeviceIdAttribute), deviceAttribute);
        var settingsProperty = new SettingsProperty(SettingName, typeof(string), null, false, SettingDefaultValue, SettingsSerializeAs.String, attributes, true, true);
        var propertyCollection = new SettingsPropertyCollection();
        propertyCollection.Add(settingsProperty);

        // Now comes the interesting part where we call our IProfile - this is where we really need Moq.
        // Expectations:
        //  - mockProfile must have it DeviceType set.
        //  - mockProfile device type (captured in setDeviceType) must match deviceType.
        //  - The returned SettingsPropertyValueCollection must not be empty.
        //  - The returned SettingsPropertyValueCollection must have exactly one entry.
        //  - The entry must match the value of SettingDefaultValue. 

        // Expectation: IProfile must have its DeviceType set.  We capture the value into setDeviceType.
        var setDeviceType = String.Empty;
        mockProfile.SetupSet(x => x.DeviceType).Callback(y => setDeviceType = y);

        // Finally, it is time to call the method we want to test
        var settingsProvider = new SettingsProvider(mockProfile.Object);

        // THE NEXT LINE THROWS AN EXCEPTION
        // IF I TRY TO STEP INTO IT, IT NEVER RETURNS AND THE TEST RUN JUST ENDS.
        var result = settingsProvider.GetPropertyValues(mockSettingsContext.Object, propertyCollection);

        // Now lets verify that everything went as expected
        // First, let test that the parsing of DeviceId was as expected: IProvider.DeviceType was set to the expected value
        Assert.AreEqual(deviceType, setDeviceType);
        // Then let test that the methods of IProvider that we mocked were called
        mockProfile.VerifyAll();

        // With this done, let turn to the output of the method
        // Firstly, we test that the resulting collection contains exactly one item of the type SettingsPropertyValue
        Assert.IsTrue(result.Count > 0);
        Assert.AreEqual(1, result.Count);
        Assert.IsTrue(result.OfType<SettingsPropertyValue>().Count() > 0);

        // Then let inspect the contained SettingsProviderValue further
        var settingsPropertyValue = result.OfType<SettingsPropertyValue>().First();

        // First IsDirty flag must never be set
        Assert.IsFalse(settingsPropertyValue.IsDirty);

        // The PropertyValue must be the default value we passed in
        Assert.AreEqual(SettingDefaultValue, settingsPropertyValue.PropertyValue);
        }

Исключением, которое бросается (как сообщает тестовый бегун), является:

Метод теста ASCOM.Platform.Test.SettingsProviderTest.GetPropertySettings_Should_Return_Default_Values_When_Device_Not_Registered throw exception: System.ArgumentException: Тип System.Configuration.SettingsContext реализует ISerializable, но не смог создать конструктор десериализации.

Ответ 1

Я считаю, что вам нужно настроить свойство Attributes вашего макета SettingsProperty, чтобы вернуть ваш mock SettingsAttributeDictionary и , затем настроить индекс вашего макета SettingsAttributeDictionary, чтобы вернуть желаемое значение, например.

mockItem.SetupGet(x => x.Attributes).Returns(mockAttributes);

mockAttributes.SetupGet(x => x[It.IsAny<System.Type>()])
              .Returns(deviceAttribute);

Изменить

Исключение составляет класс Castle DynamicProxy, который используется Moq. Я считаю, что это связано с тем, как Moq издевается над объектами, которые могут быть сериализованы. Если замок не может найти непубличный конструктор на сериализуемом объекте с сигнатурой (SerializationInfo, StreamingContext), то он выдает это исключение. Что вы можете сделать, это изменить метод GetPropertyValues ​​для настраиваемого параметра SettingsProvider, чтобы он принимал Hashtable вместо параметра SettingsContext и предоставлял макет Hashtable для вызова метода вместо макета SettingsContext. Hashtable имеет необходимый конструктор, поэтому, возможно, это сработает. Нет реального преимущества настаивать на параметре типа SettingsContext, а не на Hashtable, поскольку параметр SettingsContext тривиально выводится из Hashtable, т.е. Не добавляет никаких членов.