Подтвердить что ты не робот

Издевательские статические методы

В последнее время я начал использовать Moq до unit test. Я использую Moq для издевательства над классами, которые мне не нужно тестировать.

Как вы обычно относитесь к статическим методам?

public void foo(string filePath)
{
    File f = StaticClass.GetFile(filePath);
}

Как может быть вызван этот статический метод StaticClass.GetFile()?

P.S. Я был бы признателен за любые материалы для чтения, которые вы рекомендуете в Moq и Unit Testing.

4b9b3361

Ответ 1

Mocking frameworks, такие как Moq или Rhinomocks, могут создавать только макеты экземпляров объектов, это означает, что издевательские статические методы невозможны.

Вы также можете искать Google для получения дополнительной информации.

Кроме того, есть несколько ранее заданных вопросов о StackOverflow здесь, здесь и .

Ответ 2

@Pure.Krome: хороший ответ, но я добавлю несколько деталей

@Kevin: вам нужно выбрать решение в зависимости от изменений, которые вы можете внести в код.
Если вы можете его изменить, некоторые инъекции зависимостей делают код более подверженным тестированию. Если вы не можете, вам нужна хорошая изоляция.
Благодаря бесплатной фальшивой структуре (Moq, RhinoMocks, NMock...) вы можете только издеваться над делегатами, интерфейсами и виртуальными методами. Таким образом, для статических, герметичных и не виртуальных методов у вас есть 3 решения:

  • Изолятор TypeMock (может издеваться над всем, кроме дорогого)
  • JustMock Telerik (новый, дешевый, но все еще не бесплатно)
  • Moles Microsoft (единственное бесплатное решение для изоляции)

Я рекомендую Moles, потому что он бесплатный, эффективный и использует лямбда-выражения, такие как Moq. Только одна важная деталь: Родинки обеспечивают окурки, а не насмешки. Таким образом, вы все еще можете использовать Moq для интерфейса и делегатов;)

Mock: класс, который реализует интерфейс и позволяет динамически устанавливать значения для возврата/исключений, чтобы выбрасывать из определенных методов и предоставляет возможность проверять, были ли определенные методы вызваны/нет называется.
Stub: Как класс mock, за исключением того, что он не дает возможности проверить, что методы были вызваны/не вызваны.

Ответ 3

В .NET есть возможность исключить MOQ и любую другую насмешливую библиотеку. Вы должны щелкнуть правой кнопкой мыши на проводнике решений на сборке, содержащей статический метод, который вы хотите высмеять, и выбрать Добавить сборку подделок. Затем вы можете свободно издеваться над статическими методами сборки.

Предположим, что вы хотите издеваться над статическим методом System.DateTime.Now. Сделайте это, например, следующим образом:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
    Assert.AreEqual(DateTime.Now.Year, 1837);
}

У вас есть аналогичное свойство для каждого статического свойства и метода.

Ответ 4

Вы можете добиться этого с помощью библиотеки Pose, доступной от nuget. Это позволяет вам высмеивать, помимо прочего, статические методы. В методе тестирования напишите:

Shim shim = Shim.Replace(() => StaticClass.GetFile(Is.A<string>()))
    .With((string name) => /*Here return your mocked value for test*/);
var sut = new Service();
PoseContext.Isolate(() =>
    result = sut.foo("filename") /*Here the foo will take your mocked implementation of GetFile*/, shim);

Для дальнейшего чтения см. Здесь https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-without-using-interfaces-978d372478e8

Ответ 5

Мне понравилась Поза, но я не мог заставить ее перестать бросать InvalidProgramException, которая кажется известной проблемой. Теперь я использую Smocks следующим образом:

Smock.Run(context =>
{
    context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));

    // Outputs "2000"
    Console.WriteLine(DateTime.Now.Year);
});

Ответ 6

Я знаю, что это немного поздно, но это круговое решение позволило мне издеваться над статическим методом с использованием Moq.

Чтобы сделать это, я создал класс (позвоню ему Placeholder), один метод которого называется статическим методом StaticClass.GetFile.

public class Placeholder{  

    //some empty constructor

    public File GetFile(){

        File f = StaticClass.GetFile(filePath);
        return f;
    }
}

Затем вместо вызова StaticClass.GetFile в foo я создал экземпляр Placeholder и вызвал функцию GetFile.

public void foo(string filePath)
{
    Placeholder p = new Placeholder();
    File f = p.GetFile(filePath);
}

Теперь, в модульных тестах, вместо того, чтобы пытаться подделать StaticClass.GetFile, я смог издеваться над нестационарным методом GetFile из класса Placeholder.

Ответ 7

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

Это не будет использовать какую-либо структуру тестирования и будет полностью защищенным решением, однако рефактор не повлияет на подпись вашего вызывающего абонента, и поэтому он будет относительно безопасным.

Для этого вам нужно будет иметь доступ к статическому методу, поэтому он не будет работать для любых внешних библиотек, таких как System.DateTime.

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

Основной статический класс:

public static class LegacyStaticClass
{
    // A static constructor sets up all the delegates so production keeps working as usual
    static LegacyStaticClass()
    {
        ResetDelegates();
    }

    public static void ResetDelegates()
    {
        // All the logic that used to be in the body of the static method goes into the delegates instead.
        ThrowMeDelegate = input => throw input;
        SumDelegate = (a, b) => a + b;
    }

    public static Action<Exception> ThrowMeDelegate;
    public static Func<int, int, int> SumDelegate;

    public static void ThrowMe<TException>() where TException : Exception, new()
        => ThrowMeDelegate(new TException());

    public static int Sum(int a, int b)
        => SumDelegate(a, b);
}

Тесты единиц (xUnit и Shouldly)

public class Class1Tests : IDisposable
{
    [Fact]
    public void ThrowMe_NoMocking_Throws()
    {
        Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
    }

    [Fact]
    public void ThrowMe_EmptyMocking_DoesNotThrow()
    {
        LegacyStaticClass.ThrowMeDelegate = input => { };

        LegacyStaticClass.ThrowMe<Exception>();

        true.ShouldBeTrue();
    }

    [Fact]
    public void Sum_NoMocking_AddsValues()
    {
        LegacyStaticClass.Sum(5, 6).ShouldBe(11);
    }

    [Fact]
    public void Sum_MockingReturnValue_ReturnsMockedValue()
    {
        LegacyStaticClass.SumDelegate = (a, b) => 6;
        LegacyStaticClass.Sum(5, 6).ShouldBe(6);
    }

    public void Dispose()
    {
        LegacyStaticClass.ResetDelegates();
    }
}