首页 文章

是否可以使用Moq从实例获取模拟实例?

提问于
浏览
2

假设我有这个接口和这个类:

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 回答

  • 3

    我提出了一种不同的方法,可以避免这样做......

    Scheduler类正在实现单例模式来控制其构造 . 你需要能够抽象出依赖于构造方式的东西.640275_ . 所以别的东西应该负责管理调度程序的构建:它不应该负责(single responsibility principle) .

    常见的方法是使用Gang-of-Four Factory method patternservice locator pattern(例如Microsoft的UnityContainer) . 这些中的任何一个都可以被指示将该类暴露为单例,使该类只是该类负责的实现 .

    Dependency Injection完成拼图,因为当类注入了它们的依赖项时,它们本身就会从它们使用的东西的构造中抽象出来 . 所以需要一个 IScheduler 的类会有一个注入并使用它 .

    有了这些模式,在问题中执行请求的需求就会消失,并导致代码明确分离关注点 .

    脚注:我知道这些模式可能看起来令人生畏,似乎它为此做了很多努力,而且很难看到它的好处;但请相信我: try 这个(我真的是试试吧,你可以't just have a half-hearted attempt, because it is quite a step change in approach). I used to write code just like you'发布 . 我被建议看看这些模式 . 我有严重的疑虑,但我从来没有回头,现在我用这种方式编写我的所有代码 .

  • 0

    如果您在虚拟呼叫中打包要尝试替换的呼叫,则可以这样做:

    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;
        }
    }
    
  • 0

    目前还不清楚你要通过模拟测试什么 .

    如果测试需要检查私有静态字段,那么利用反射将是可行的方法,但最终会得到与 Scheduler.GetScheduler() 相同的结果 .

    从类型中获取私有静态字段的基本方法是:

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

    模拟对虚拟地址表起作用(在运行时,MSIL检查VAT以查找要运行的方法的地址) . 抽象基础和虚拟实例方法是可模拟的,接口也是如此 .

    如果虚拟被添加到实例中的 StopTimer 的签名中,则它将是Mockable,这是从 Mock<IScheduler> 获得的所有内容 .

相关问题