Можно ли получить макет из экземпляра, используя Moq? - PullRequest
0 голосов
/ 13 июня 2018

предположим, у меня есть этот интерфейс и этот класс:

public interface IScheduler
{
    void StopTimer();

    // some other methods
}

public class Scheduler : IScheduler
{
    private static readonly IScheduler scheduler = new Scheduler();

    private readonly Timer timer;

    public Scheduler()
    {
        refreshTimer = new Timer
        {
            Enabled = false,
            AutoReset = false
        };
    }

    public static IScheduler GetScheduler()
    {
        return scheduler;
    }

    public void StopTimer()
    {
        timer.Stop();
    }

    // some other methods
}

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

[TestMethod]
public void Execute_ButtonClicked_StopTimer()
{
    // arrange
    var mockScheduler = Mock.Get(Scheduler.GetScheduler());
    var command = GetCommandInstance();

    // act
    command.Execute();

    // assert
    mockScheduler.Verify(m => m.StopTimer());
}

, но не сработало, там написано "System.ArgumentException: экземпляр объекта не был создан Moq."

Любой совет, пожалуйста?

внутри класса команд есть что-то вроде этого:

public void Execute()
{
    // some code
    Scheduler.GetScheduler().StopTimer();
}

Ответы [ 3 ]

0 голосов
/ 13 июня 2018

Я предлагаю другой подход, который устраняет необходимость в этом ...

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

Общепринятыми подходами к этому являются использование группового набора фабричного метода или шаблона локатора службы (например, UnityContainer от Microsoft).Любой из них может быть направлен на то, чтобы выставить этот класс как одноэлементный, оставляя класс просто реализацией того, за что отвечает класс.

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

С этими шаблонами необходимость делать то, что запрашивается в вопросе, исчезает, и приводит к коду с четким разделением интересов..

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

0 голосов
/ 13 июня 2018

Непонятно, что вы пытаетесь проверить с помощью макета.

Если для проверки необходимо проверить частное статическое поле, то использование рефлексии будет правильным, но в конечном итоге даст тот же результат, что иScheduler.GetScheduler().

Основной подход для получения частного статического поля из типа:

var field = typeof(MyType).GetField( "MethodName", BindingFlags.NonPublic | BindingFlags.Static);
var fieldValue = field.GetValue(myTypeInstance);

Проверяет работу с виртуальными адресными таблицами (во время выполнения MSIL проверяет НДС дляадрес метода для запуска).Методы абстрактных базовых и виртуальных экземпляров, как и интерфейсы, являются насмешливыми.

Если к подписи для StopTimer в экземпляре будет добавлено virtual , это будет Mockable, что все, что можно получить из Mock<IScheduler>.

0 голосов
/ 13 июня 2018

Если вы заключаете вызов, который пытаетесь заменить, в виртуальный вызов, вы можете сделать это следующим образом:

public class Scheduler : IScheduler
{
    private static readonly IScheduler scheduler = CreateScheduler();

    private readonly Timer timer;

    public Scheduler()
    {
        refreshTimer = new Timer
        {
            Enabled = false,
            AutoReset = false
        };
    }

    public static IScheduler GetScheduler()
    {
        return scheduler;
    }

    public void StopTimer()
    {
        timer.Stop();
    }

    public virtual IScheduler CreateScheduler()
    {
         return new Scheduler();
    }
    // some other methods
}

Затем в классе модульного теста:

[TestClass]
public class TestClass
{
    [TestMethod]
    public void testMethod()
    {
        var scheduler = new Mock<Scheduler>();
        // you can now call .setup on the scheduler instance and use this mock.
        var sut = new SchedulerWrapper(scheduler.Object);

        ///var sut....sometests
    }
}

internal class SchedulerWrapper : Scheduler
{
    private Scheduler _scheduler;
    public SchedulerWrapper(Scheduler scheduler)
    {
         _scheduler;
    }
    public overrride IScheduler CreateScheduler()
    {
         return _scheduler;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...