Могу ли я заставить Moq добавить атрибуты в класс mock? - PullRequest
22 голосов
/ 12 февраля 2009

Я пишу интерфейс командной строки для моего проекта. Пользователь вводит «create project foo», находит контроллер, отвечающий за «project», и затем вызывает метод Create, передавая «foo» в качестве первого аргумента.

Он сильно зависит от атрибутов и отражения: контроллер выглядит примерно так:

[ControllerFor("project")]
class ProjectController
{
    [ControllerAction("create")]
    public object Create(string projectName) { /* ... */ }
}

Я бы хотел использовать Moq в модульных тестах для парсера, что-то вроде этого:

Mock<IProjectsController> controller = new Mock<IProjectsController>();
controller.Expect(f => f.Create("foo"));

parser.Register(controller.Object);
parser.Execute("create project foo");

controller.VerifyAll();

Добавление атрибутов в интерфейс, похоже, не работает - они не наследуются производными классами.

Могу ли я заставить Moq добавить атрибуты к классу, который надо смоделировать?

1 Ответ

32 голосов
/ 12 февраля 2009

Обновление: Я только что понял, что вы можете добавить атрибуты к существующему типу, используя TypeDescriptor.AddAttributes , который может быть выполнен для экземпляра или типа:

Mock<IRepository> repositoryMock = new Mock<IRepository>();

CustomAttribute attribute = new CustomAttribute();

// option #1: to the instance
TypeDescriptor.AddAttributes(repositoryMock.Object, attribute );

// option #2: to the generated type
TypeDescriptor.AddAttributes(repositoryMock.Object.GetType(), attributes);

Если вам это нужно, AddAttribute возвращает TypeDescriptorProvider, который может быть передан TypeDescriptor.RemoveProvider для последующего удаления атрибутов.

Имейте в виду, что Attribute.GetCustomAttributes не найдет атрибуты, добавленные во время выполнения таким образом. Вместо этого используйте TypeDescriptor.GetAttributes .

Оригинальный ответ

Я не верю, что Moq (или любая другая насмешливая структура) поддерживает пользовательские атрибуты. Я знаю, что Castle Proxy (фреймворк, обычно используемый для создания класса) поддерживает , но не было бы способа получить к нему доступ через Moq.

Лучше всего абстрагироваться от метода загрузки атрибутов в интерфейс (который принимает тип и тип атрибута), а затем смоделировать это.

Редактировать: Например:

public interface IAttributeStrategy
{
    Attribute[] GetAttributes(Type owner, Type attributeType, bool inherit);
    Attribute[] GetAttributes(Type owner, bool inherit);
}

public class DefaultAttributeStrategy : IAttributeStrategy
{
    public Attribute[] GetAttributes(Type owner, Type attributeType, bool inherit)
    {
        return owner.GetCustomAttributes(attributeType, inherit);
    }

    public Attribute[] GetAttributes(Type owner, bool inherit)
    {
        return owner.GetCustomAttributes(inherit);
    }
}

Класс, которому требуются атрибуты, использует экземпляр IAttributeStrategy (либо через контейнер IoC, либо с возможностью его передачи в конструктор). Обычно это DefaultAttributeStrategy, но теперь вы можете смоделировать IAttributeStrategy, чтобы переопределить вывод.

Это может показаться запутанным, но добавить слой абстракции гораздо проще, чем пытаться на самом деле насмехаться над Атрибутами.

...