Все очень просто -- надо создать в тестовой сборке класс, имплементирующий IActionProvider, и затем проверить, правильно ли все происходит.
Вы писали:
A>Только осваиваю юниттестирование. Возникает множество вопросов, например:
A>Как протестировать следующий код? Создавать мокап домена приложений? A>
A> public class ActionProviders
A> {
A> private static readonly Dictionary<string, IActionProvider> _providers = new Dictionary<string, IActionProvider>(StringComparer.InvariantCultureIgnoreCase);
A> static ActionProviders()
A> {
A> Load();
A> }
A> public static void Load()
A> {
A> //TODO TEST MISSING -- how to test it???
A> foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
A> {
A> foreach (Type type in assembly.GetTypes())
A> try
A> {
A> if (type.GetInterface(typeof(IActionProvider).FullName) != null)
A> {
A> if (type.GetConstructor(new Type[0]) != null)
A> _providers.Add(type.Name.Replace("Provider", ""), (IActionProvider)Activator.CreateInstance(type));
A> }
A> }
A> catch (TargetInvocationException) { }
A> catch (TypeInitializationException) { }
A> catch (ReflectionTypeLoadException) { }
A> catch (ArgumentNullException) { }
A> catch (ArgumentException) { }
A> }
A> }
A> public static IDictionary<string, IActionProvider> Providers
A> {
A> get { return _providers; }
A> }
A> }
A>
Здравствуйте, ulu, Вы писали:
ulu>Все очень просто -- надо создать в тестовой сборке класс, имплементирующий IActionProvider, и затем проверить, правильно ли все происходит.
"Все происходит" — это что? Я не хочу проверять реализаторов IActionProvider — для этого есть отдельные тесты. Мне бы убедиться, что все нужные типы загружаются и не загружаются лишние. Т.е. логику именно метода Load.
Можете код теста привести в пример?
И еще возник вопрос. Как быть, если само существование тестов уже влияет на поведение тестируемого кода?
Здравствуйте, Aggtaa, Вы писали:
ulu>>Все очень просто -- надо создать в тестовой сборке класс, имплементирующий IActionProvider, и затем проверить, правильно ли все происходит. A>"Все происходит" — это что? Я не хочу проверять реализаторов IActionProvider — для этого есть отдельные тесты. Мне бы убедиться, что все нужные типы загружаются и не загружаются лишние. Т.е. логику именно метода Load.
Вы сами выделили отдельный элемент функциональности, который вы хотите оттестировать — фильтрацию типов. Вот это и вынесите в отдельный метод и оттестируйте его. Сигнатура: IEnumerable<Type> -> IEnumerable<Type>.
A>Можете код теста привести в пример?
A>И еще возник вопрос. Как быть, если само существование тестов уже влияет на поведение тестируемого кода?
Вы тестируете именно метод Load. Вызываете его, а потом проверяете, добавился в Providers ли экземпляр того класса, который Вы создали для проверки.
А Вы MEF не пробовали здесь применить?
Вы писали:
A>Здравствуйте, ulu, Вы писали:
ulu>>Все очень просто -- надо создать в тестовой сборке класс, имплементирующий IActionProvider, и затем проверить, правильно ли все происходит. A>"Все происходит" — это что? Я не хочу проверять реализаторов IActionProvider — для этого есть отдельные тесты. Мне бы убедиться, что все нужные типы загружаются и не загружаются лишние. Т.е. логику именно метода Load.
A>Можете код теста привести в пример?
A>И еще возник вопрос. Как быть, если само существование тестов уже влияет на поведение тестируемого кода?
Здравствуйте, Lloyd, Вы писали:
A>>"Все происходит" — это что? Я не хочу проверять реализаторов IActionProvider — для этого есть отдельные тесты. Мне бы убедиться, что все нужные типы загружаются и не загружаются лишние. Т.е. логику именно метода Load. L>Вы сами выделили отдельный элемент функциональности, который вы хотите оттестировать — фильтрацию типов. Вот это и вынесите в отдельный метод и оттестируйте его. Сигнатура: IEnumerable<Type> -> IEnumerable<Type>.
Так?
private static IEnumerable<Type> GetAssemblyProviderTypes(IEnumerable<Type> types)
{
foreach (Type type in types)
if ((type.GetInterface(typeof(IActionProvider).FullName) != null)
&& (type.GetConstructor(new Type[0]) != null))
yield return type;
}
Для тестирования придется этот метод делать public или хотя бы internal. Вытаскивать с мясом наружу кусок логики неправильно, мне кажется. И ведь я хочу протестировать еще и загрузку. Типа "валидно написанные классы валидно загружаются, невалидно написанные и левые классы не загружаются".
Но это мелочи по сравнению с public логикой. Мне неинтересно тестировать маленький аспект работы кода в отдельных 3 строчках.
Мне нужно убедиться, что вся публичная функциональность этого кода ведет себя предсказуемо, повторяемо и правильно. В этом же смысл юниттестирования?
A>>И еще возник вопрос. Как быть, если само существование тестов уже влияет на поведение тестируемого кода? L>Просто вынесите код тестов в отдельную сборку.
Сборка с тестами все равно будет загружена в домен на момент выполнения теста.
Здравствуйте, ulu, Вы писали:
A>>"Все происходит" — это что? Я не хочу проверять реализаторов IActionProvider — для этого есть отдельные тесты. Мне бы убедиться, что все нужные типы загружаются и не загружаются лишние. Т.е. логику именно метода Load. ulu>Вы тестируете именно метод Load. Вызываете его, а потом проверяете, добавился в Providers ли экземпляр того класса, который Вы создали для проверки.
Как быть с "не загружаются лишние"?
ulu>А Вы MEF не пробовали здесь применить?
Не пробовал, потому что .Net 2.0.
Здравствуйте, Aggtaa, Вы писали:
L>>Вы сами выделили отдельный элемент функциональности, который вы хотите оттестировать — фильтрацию типов. Вот это и вынесите в отдельный метод и оттестируйте его. Сигнатура: IEnumerable<Type> -> IEnumerable<Type>. A>Так? A>
A> private static IEnumerable<Type> GetAssemblyProviderTypes(IEnumerable<Type> types)
A> {
A> foreach (Type type in types)
A> if ((type.GetInterface(typeof(IActionProvider).FullName) != null)
A> && (type.GetConstructor(new Type[0]) != null))
A> yield return type;
A> }
A>
Или
return types.Where(IsValidType);
A>Для тестирования придется этот метод делать public или хотя бы internal. Вытаскивать с мясом наружу кусок логики неправильно, мне кажется. И ведь я хочу протестировать еще и загрузку. Типа "валидно написанные классы валидно загружаются, невалидно написанные и левые классы не загружаются".
Ну, как не крути, а необходимость тестировать все-таки вынуждает иначе подходить к проектированию. От этого не уйти.
A>Но это мелочи по сравнению с public логикой. Мне неинтересно тестировать маленький аспект работы кода в отдельных 3 строчках. A>Мне нужно убедиться, что вся публичная функциональность этого кода ведет себя предсказуемо, повторяемо и правильно. В этом же смысл юниттестирования?
Это нормально. SRP.
A>>>И еще возник вопрос. Как быть, если само существование тестов уже влияет на поведение тестируемого кода? L>>Просто вынесите код тестов в отдельную сборку. A>Сборка с тестами все равно будет загружена в домен на момент выполнения теста.
Здравствуйте, Aggtaa, Вы писали:
A>Здравствуйте, ulu, Вы писали:
A>>>"Все происходит" — это что? Я не хочу проверять реализаторов IActionProvider — для этого есть отдельные тесты. Мне бы убедиться, что все нужные типы загружаются и не загружаются лишние. Т.е. логику именно метода Load. ulu>>Вы тестируете именно метод Load. Вызываете его, а потом проверяете, добавился в Providers ли экземпляр того класса, который Вы создали для проверки. A>Как быть с "не загружаются лишние"?
Ну, например, создать еще один класс, который *не* реализует IActionProvider, и проверить, что он не загружается.
A>Для тестирования придется этот метод делать public или хотя бы internal. Вытаскивать с мясом наружу кусок логики неправильно, мне кажется. И ведь я хочу протестировать еще и загрузку. Типа "валидно написанные классы валидно загружаются, невалидно написанные и левые классы не загружаются".
Как уже было сказано, у тебя нарушается SRP: 1. Загрузка 2. Фильтрация. Поэтому п. 2 выделяем в отдельную роль (интерфейс) — AssemblyProviderFilter с твоим методом GetAssemblyProviderTypes(), который с удовольствием передаем в метод Load() (раз уж у тебя зачем-то статический класс, а то бы — в конструктор). А его реализацию элементарно тестируем отдельно.
A>Но это мелочи по сравнению с public логикой. Мне неинтересно тестировать маленький аспект работы кода в отдельных 3 строчках. A>Мне нужно убедиться, что вся публичная функциональность этого кода ведет себя предсказуемо, повторяемо и правильно. В этом же смысл юниттестирования?
Нет, смысл юнит-тестирования в тестировании отдельных аспектов поведения (по одному на тест) единичного класса. А то, что ты хочешь сделать — это интеграционный тест (как небольшая совокупность классов взаимодействует между собой и с внешним, неконтролируемым миром). Такие тесты нужны, но их, во-первых, мало, а во-вторых они не стремятся покрыть весь функционал, они что-то вроде smoke-тестов. Их мало, потому что создавать и поддерживать их сложно, а диагностируемость оставляет желать много лучшего.