Макет статического свойства с moq

Я довольно новичок в использовании moq. Я в создании какого-то модульного теста для HttpModule и все работает нормально, пока я не ударил static свойство следующим

this.applicationPath = (HttpRuntime.AppDomainAppVirtualPath.Length > 1) ? HttpRuntime.AppDomainAppVirtualPath : String.Empty;

Я не знаю, как создавать издевательства для static класс и свойство как HttpRuntime.AppDomainAppVirtualPath, context, request а также response был хорошо посмеян с примером кода, который я получаю из moq. Я буду признателен, если кто-то может помочь мне в этом.

6 ответов

Moq не может подделывать статичные элементы.

В качестве решения вы можете создать класс-оболочку (шаблон адаптера), содержащий статическое свойство, и подделать его члены.
Например:

public class HttpRuntimeWrapper
{
    public virtual string AppDomainAppVirtualPath 
    { 
        get
        { 
            return HttpRuntime.AppDomainAppVirtualPath; 
        }
    }
}

В рабочем коде вы можете получить доступ к этому классу вместо HttpRuntime и подделать это свойство:

[Test]
public void AppDomainAppVirtualPathTest()
{
    var mock = new Moq.Mock<HttpRuntimeWrapper>();
    mock.Setup(fake => fake.AppDomainAppVirtualPath).Returns("FakedPath");

    Assert.AreEqual("FakedPath", mock.Object.AppDomainAppVirtualPath);
}

Другое решение заключается в использовании Isolation Framework (как Typemock Isolator), в котором вы можете подделывать статические классы и члены.
Например:

Isolate.WhenCalled(() => HttpRuntime.AppDomainAppVirtualPath)
       .WillReturn("FakedPath");

Отказ от ответственности - я работаю в Typemock

Вы не можете Moq статические методы с Moq.

В действительности это неплохо, статические методы и классы имеют свое место, но для логики они затрудняют юнит-тестирование. Естественно, вы столкнетесь с ними при использовании других библиотек. Чтобы обойти это, вам нужно написать адаптер (оболочку) вокруг статического кода и предоставить интерфейс. Например:

// Your static class - hard to mock
class StaticClass
{
   public static int ReturnOne() 
   {
       return 1;
   }
}

// Interface that you'll use for a wrapper
interface IStatic
{
    int ReturnOne();
}

Обратите внимание, я опустил конкретный класс, который использует IStatic для производственного кода. Все, что это будет, - это класс, который использует IStatic, и ваш производственный код будет использовать этот класс, а не StaticClass выше.

Затем с Moq:

var staticMock = new Mock<IStatic>();
staticMock.Setup(s => s.ReturnOne()).Returns(2);

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

Тем не менее, недавно я обнаружил проект Moles. С домашней страницы; "Moles позволяет заменить любой метод.NET делегатом. Moles поддерживает статические или не виртуальные методы". Это может быть полезно для вашей текущей ситуации.

Вы можете использовать Microsoft Fakes для этого. Это определенно решит проблему. См. https://msdn.microsoft.com/en-us/library/hh549175.aspx

Лучшее решение, которое я нашел до сих пор, это JustMock от Telerik - к сожалению, только платная версия позволяет высмеивать статику.

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

Использование Microsoft Fakes, как предлагает @Sujith, является жизнеспособным решением. Вот как вы на самом деле это делаете:

  • найти System.Web в вашей ссылке на ваш тестовый проект и щелкните правой кнопкой мыши
  • Выберите "Добавить". Это добавляет ссылку System.Web.4.0.0.0.Fakes
  • Используйте следующий код:

    using (Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create ()) {System.Web.Fakes.ShimHttpRuntime.AppDomainAppVirtualPathGet = () => "/"; // Делать то, что когда-либо нуждается в фальшивом AppDomainAppVirtualPath }

Другие вопросы по тегам