Модульное тестирование, когда требуется универсальный тип - PullRequest
0 голосов
/ 07 февраля 2019

У меня следующая проблема.Я хочу протестировать универсальный класс MyClass <'T>.Тем не менее, меня не волнует тип, используемый для T. Однако, когда я создаю экземпляр MyClass в тесте, мне нужно указать, каким должен быть T.Я могу сделать это следующим образом:

var sut = new MyClass<Object>();

Конечно, это работает, однако я не люблю помещать объект конкретно, потому что он предлагает кому-то, читающему этот тест, что, возможно, тип объекта здесь имеет какое-то значение.

Мне нравится подход, используемый AutoFixture, когда каждое значение создается с использованием fixture.Create<T>(), потому что оно показывает читателю, что значение не очень важно.Пример этого:

[Fact]
public void IntroductoryTest()
{
    // Arrange
    Fixture fixture = new Fixture();

    int expectedNumber = fixture.Create<int>();
    MyClass sut = fixture.Create<MyClass>();
    // Act
    int result = sut.Echo(expectedNumber);
    // Assert
    Assert.Equal(expectedNumber, result);
}

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

Есть литакое решение, но в отношении типов, используемых с родовыми классами?

Что-то вроде: var sut = MyClass<fixture.GenericType>(); Мне все равно, что это за тип.Чтобы объяснить мой вариант использования немного подробнее: у меня есть модуль AutoFac, который используется для регистрации MyClass.В моем тесте я просто хочу проверить, правильно ли модуль регистрирует MyClass.Для этой регистрации я не хочу указывать какой-то конкретный тип, который будет использоваться для MyClass.Это может быть любой.

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

В настоящее время AutoFixture не поддерживает активацию закрытых универсальных типов, и вряд ли она изменится однажды.

Во-первых, это связано с тем, что сам язык C # поддерживает закрытые универсальные типы для отражениятолько цельВы не можете объявить переменную типа MyClass<>, не закрывая тип над определенным T.Следовательно, даже если AutoFixture подставит вам тип и активирует экземпляр, вам будет сложно работать с результатом - вам придется либо использовать переменную типа object, либо положиться на ковариацию , если вашТип поддерживает это.Ни один из этих параметров не выглядит пригодным для использования.

Во-вторых, сама задача очень сложна, так как универсальный тип может иметь ограничения.Иногда ограничения могут сойти с ума и, например, иметь циклические зависимости: class MyClass<T1, T2> where T1 : T2 where T2 : IEnumerable<T1>.Библиотека AutoFixture.Idioms уже пытается решить эту проблему для GuardClauseAssertion, но успех составляет в среднем .Эта функция требует динамического генерирования типов (что BTW не поддерживается на всех платформах) и очень расширенного использования Reflection.Emit API, что затрудняет его правильную реализацию.

0 голосов
/ 07 февраля 2019

Возможно, вы можете определить базовый тип, скажем IBase, где все ваши T-типы реализуют IBase (Class1<T> where T : IBase), а затем вы можете сказать var sut = new MyClass<IBase>();

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...