JohnMeers Ответов: 1

Тестируйте детали MEF с помощью отражения


I have an application using MEF and am trying to write integration tests for the Plug-ins.
The code I have is:-

        <pre lang="c#">
        public string Run()
        {
            IFunction IUT = null; string Wanted = "NewUser";
            foreach (Lazy<IFunction, IFunctionData> func in functions)
            {
                if (func.Metadata.FunctionName == Wanted) IUT = func.Value;
            }
            bool res = false;
            res = TestOne(IUT);
            if (res) TestTwo();
            if (res) return "Pass -  NewUser Test";
            return "Fail -  NewUser Test";
        }
        // Normal New User OK
        private bool TestOne(IFunction IUT)
        {
            bool res = false;
            // Set up the VM properties
            var VM = IUT.GetType();
            PropertyInfo prop=VM.GetProperty("UserName");prop.SetValue(prop, "Smith");
            MethodInfo Method = VM.GetMethod("CreateNewUser");
            //...
            //...
            return res;
        }

Все части IFunction являются ViewModels, и метод Run находит правильный вариант OK.
То, что я хочу сделать, это установить свойство UserName этой виртуальной машины, а затем запустить метод Create

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

Что я уже пробовал:

Поисковые системы Google придумали предложения по модульному тестированию, но мне нужно протестировать интеграцию с остальной частью приложения

johannesnestler

Он должен работать по отражению, как вы показали - так в чем же ваша точная проблема с этим (довольно часто задаются частные поля для тестов по отражению).
Может быть, проблема в том, что ваш тест не знает о вашем конкретном типе виртуальной машины?

JohnMeers

Это верно, тест только знает, что это плагин и типа NewUser, я не могу найти способ привести к фактической конкретной виртуальной машине

1 Ответов

Рейтинг:
10

Graeme_Grant

Вам не нужно использовать отражение с MEF, вы просто экспортируете и импортируете данные для совместного использования - данные могут существовать в DLL/EXE вне основного приложения. Пока MEF может составлять, данные будут видны. Это то, что мы сделали в другом Решение MEF[^] где отражение было запрошено для системы плагинов, и плагины автоматически загружаются, когда DLL/EXE помещается в папку основного приложения.

Итак, ответ заключается в том, чтобы создать класс для хранения ваших пользовательских данных в отдельном классе (репозитории), определить Export с собой PartCreationPolicy(CreationPolicy.Shared) Это создаст статический/общий объект. Вот вам пример:

public interface IUserRepository
{
    string UserName { get; set; }
}

[Export(typeof(IUserRepository)),
 PartCreationPolicy(CreationPolicy.Shared)]
public class UserRepository : ObservableBase, IUserRepository
{
    private string userName = "**Not Set**";

    public string UserName
    {
        get => userName;
        set => Set(ref userName, value);
    }
}

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

Вот ObservableBase класс:
public abstract class ObservableBase : INotifyPropertyChanged
{
    public void Set<TValue>(ref TValue field, TValue newValue, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<TValue>.Default.Equals(field, default(TValue)) || !field.Equals(newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public bool IsInDesignMode => DesignerProperties.GetIsInDesignMode(new DependencyObject());
}

А теперь ты import то UserRepository:
[Import(typeof(IUserRepository), RequiredCreationPolicy = CreationPolicy.Shared)]
public IUserRepository Repo { get; set; }

... и пользовательские данные теперь совместно используются везде, где указано выше Import предназначенный:
Repo.UserName = "MainUser";

Никакого кодирования отражения от вас не требуется, так как все это обрабатывается MEF. Наслаждайтесь!

PS: да, MEF использует отражение, так что вам это не нужно! ;)


JohnMeers

Я понимаю это, но я использую ImportMany, так как у меня есть несколько плагинов, которые все являются IFunctions. Метаданные позволяют мне найти правильный вариант, но это все еще IFunction, и видны только общие методы IFunction и т. д., а не те, которые предназначены для конкретной функции NewUser. Без ссылки на NewUser (что отрицало бы цель использования MEF) Я не могу привести IFunction к NewUserViewModel, хотя это так.

Я сдался и реализовал общий метод IFunction 'Test(object[])' для установки свойств. Очень неэлегантно так что если у кого то есть лучший способ это будет приветствоваться

Graeme_Grant

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

JohnMeers

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

Graeme_Grant

Разделение проблем - то есть: удаление уровня данных из вашей логики бизнес-уровня.

То, что он также позволяет, - это замена репозитория и реализация макетного тестирования или изменение уровня данных, а также его интеграционное тестирование.