2015-08-26 2 views
4

В моем контроллере есть метод, который возвращает данные атрибута из текущей исполняющей сборки в частичное представление.Как вводить макет сборки для использования с Moq

В этом примере я просто занимаю название, но мне нужно сделать больше с ним.

Контроллер:

var title = ""; 

    var asm = Assembly.GetExecutingAssembly(); 
    var attrs = asm.GetCustomAttributes(typeof(AssemblyTitleAttribute)); 
    var titleAttr = (AssemblyTitleAttribute)attributes[0]; 

    title = titleAttr.Title; 

    return PartialView("_Build", title); 

При написании модульного тестирования в Moq, мне нужно найти способ, чтобы впрыснуть Ассамблея атрибутов в макете, чтобы я мог убедиться, что правильные атрибуты генерируются при Я запускаю проверку контроллера, а затем делаю x или y с моими утверждениями.

UnitTest:

//Arrange 
    //The magic I need to happen. 

    //Act 
    var controller = GetController(); 
    var result = controller.MyMethod() as PartialViewResult; 
    var title = result.Model; 

    //Assert 
    Assert.AreEqual("Title", title); //currently static, need to verify against a mock 

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

Есть ли хороший способ создания поддельной сборки? Нужно ли использовать System.Reflection.Emit?

ответ

1

Вы можете создать виртуальный метод, например. GetCustomAttributes, который предоставляет атрибуты для данного типа. Затем в вашем тестовом предложении тестируемый класс будет выводиться из класса контроллера и переопределять этот метод.

Главная Контроллер:

private IDependency _someDependency; 

public HomeController(IDependency someDependency) 
{ 
    _someDependency = someDependency; 
} 

public ActionResult MyMethod() 
{ 
    var title = ""; 
    var version = ""; 

    IEnumerable<Attribute> attributes = GetCustomAttributes(typeof(AssemblyVersionAttribute)).ToList(); 
    AssemblyVersionAttribute verAttr = attributes.OfType<AssemblyVersionAttribute>().FirstOrDefault(); 
    if (verAttr != null) version = verAttr.Version; 

    attributes = GetCustomAttributes(typeof(AssemblyTitleAttribute)).ToList(); 
    AssemblyTitleAttribute titleAttr = attributes.OfType<AssemblyTitleAttribute>().FirstOrDefault(); 
    if (titleAttr != null) title = titleAttr.Title; 

    return PartialView("_Build", title + version); 
} 

public virtual IEnumerable<Attribute> GetCustomAttributes(Type attributeType) 
{ 
    var asm = Assembly.GetExecutingAssembly(); 
    var attrs = asm.GetCustomAttributes(attributeType); 
    return attrs; 
} 

Тест:

[TestClass] 
public class MyMethodTest 
{ 
    [TestMethod] 
    public void MyMethod_WhenCalled_PartialViewIsReturned() 
    { 
     // Arrange 
     // The magic I need to happen. 

     Mock<IDependency> dependencyStub = new Mock<IDependency>(); 
     // dependencyStub.Setup(...).Returns(...); 
     var controller = new TestableHomeController(dependencyStub.Object) 
     { 
      UseFakeAttributes = true 
     }; 

     // Act 
     var result = controller.MyMethod() as PartialViewResult; 

     // Assert 
     var model = result.Model; 
     Assert.AreEqual("MyFakeTitle1.0.0.0", model); // currently static, need to verify against a mock 
    } 

    private class TestableHomeController : HomeController 
    { 
     public bool UseFakeAttributes { get; set; } 

     public TestableHomeController(IDependency someDependency) 
      :base(someDependency) 
     { } 

     public override IEnumerable<Attribute> GetCustomAttributes(Type attributeType) 
     { 
      return UseFakeAttributes 
       ? new List<Attribute> 
        { 
         new AssemblyTitleAttribute("MyFakeTitle"), 
         new AssemblyVersionAttribute("1.0.0.0"), 
         new AssemblyDescriptionAttribute("Assembly fake description") 
         // next attributes ... 
        }.Where(a => a.GetType() == attributeType) 
       : base.GetCustomAttributes(attributeType); 
     } 
    } 
} 
+0

Что делать, если наследование контроллера наследует конструктор в производном классе? –

+1

@PhillipJenkins Затем вы создаете перегрузку, которая наследуется от базы. Не стоит волноваться - это сплошной подход Майкла Перса «эффективно работать с устаревшим кодом», чтобы изолировать проблемный код. Обратите внимание на «устаревший код» - если вы в настоящее время не испытываете этой проблемы, не стоит тратить время на размышления о последствиях или «будущих доказательствах»; это время было бы лучше потрачено, просто рефакторинг кода, чтобы значение было введено. – moarboilerplate

+0

@PhillipJ см. Отредактированный ответ, контроллер имеет конструктор с параметром, который будет издеваться и вводиться в тестируемый класс. – dee

0

Поскольку это информация о статическом типе, вам нужно только оценить ее один раз для сборки. Рассмотрите возможность заполнения статической коллекции (например, словаря) этой информацией при запуске приложения. В этот момент вы можете ввести словарь в контроллер, или вы можете изменить свою конфигурацию, чтобы получить доступ к словарю и ввести только те значения, которые вам нужны. Фактически, если это не выходит из-под контроля, возможно, было бы просто иметь имена строк с именами, такие как «build» или «title», даже если их не нужно размещать в коллекции.

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

+0

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

Смежные вопросы