Unit testing 单元测试:对单例进行类型模拟

Unit testing 单元测试:对单例进行类型模拟,unit-testing,mocking,typemock,Unit Testing,Mocking,Typemock,我正在使用TypeMock Isolater为一些单元测试模拟一些对象——尝试使用AAA api(因此隔离调用) 我有一个简单的单例类,在其中调用静态GetInstance(),然后返回该类的一个实例。我原以为这将是一个简单的模拟问题,但我遇到了一个非常令人沮丧的问题!我似乎无法使GetInstance()在设置预期调用的情况下正确返回模拟对象 我试过: 使用MST项目(使用访问器类)将模拟对象直接分配给实例变量(使用Memers.mustSpecificReturnValues和Isolat

我正在使用TypeMock Isolater为一些单元测试模拟一些对象——尝试使用AAA api(因此隔离调用)

我有一个简单的单例类,在其中调用静态GetInstance(),然后返回该类的一个实例。我原以为这将是一个简单的模拟问题,但我遇到了一个非常令人沮丧的问题!我似乎无法使GetInstance()在设置预期调用的情况下正确返回模拟对象

我试过:

  • 使用MST项目(使用访问器类)将模拟对象直接分配给实例变量(使用Memers.mustSpecificReturnValues和Isolate.WhenCalled使用WithExactArguments设置期望值假装对象),但由于某些原因,模拟对象始终返回null(没有异常)
  • 模拟Singleton.GetInstance()以返回模拟的对象。这将返回一个模拟对象,该对象需要设置WhenCalled,但现在我所做的Isolate.WhenCalled调用似乎对假对象没有任何作用-因此所有调用都会引发意外的调用异常
  • 我还尝试模拟实际的方法调用(例如Singleton.GetInstance().Test()),这将适用于对该方法的调用,但对Singleton上其他方法的所有其他调用都会返回null,而不是按照我的要求引发异常(因为这似乎会自动模拟没有成员的所有对象。MustSpecifyReturnValues)
我只想模拟一个单例,任何调用我都没有明确告诉它期望抛出异常。我以为这很简单,但显然不是!悲哀的

有人知道我做错了什么吗

谢谢
James

最好的解决方案是不使用单例(或任何其他静态可变数据)。只需创建一个实例并使用依赖项注入将其传递给所有需要它的对象


我认为简单的解决方案是创建singleton类的假实例,并在调用实际的类构造函数之前使用swapniquestace:

[TestMethod]
public void SetBhaciorOnSingleton()
{
   var fake = Isolate.Fake.Instance<SingletonClass>();

   Isolate.WhenCalled(() => fake.SomeFunction()).WillReturn(10);
   // Set additional behavior on singleton class

   Isolate.Swap.NextInstance<SingletonClass>().With(fake);

   // This is where the class constructor is being called
   var result = SingletonClass.GetInstace().SomeFunction();
   Assert.AreEqual(10, result );
}
[TestMethod]
公共空间设置为Haciooronsingleton()
{
var fake=Isolate.fake.Instance();
孤立.WhenCalled(()=>false.SomeFunction()).WillReturn(10);
//在singleton类上设置其他行为
使用(false)隔离.Swap.NextInstance();
//这是调用类构造函数的地方
var result=SingletonClass.getInstance().SomeFunction();
断言。等于(10,结果);
}
除非在测试之前创建singleton类,否则此解决方案应该适用于大多数场景。 如果需要在创建类后设置行为,只需在调用时使用:

[TestMethod]
public void SetBhaciorOnSingleton()
{
    var fake = Isolate.Fake.Instance<SingletonClass>();

    Isolate.WhenCalled(() => fake.SomeFunction()).WillReturn(10);

    Isolate.WhenCalled(() => SingletonClass.GetInstace()).WillReturn(fake);

    var result = SingletonClass.GetInstace().SomeFunction();
    Assert.AreEqual(10, result );
}
[TestMethod]
公共空间设置为Haciooronsingleton()
{
var fake=Isolate.fake.Instance();
孤立.WhenCalled(()=>false.SomeFunction()).WillReturn(10);
隔离.WhenCalled(()=>SingletonClass.getInstance()).WillReturn(false);
var result=SingletonClass.getInstance().SomeFunction();
断言。等于(10,结果);
}
谢谢

我以前没有尝试NextInstance,因为它在我不想更改的接口上不起作用

但是,我已经尝试过了,它确实有效——尽管我假设设置何时调用的顺序并不重要,但它确实起作用。例如,如果我在交换之后进行WhenCalled,它就不起作用了。它需要在交换之前完成。(老实说,对我来说没有什么意义——它应该是同一个对象)

然而,最后一个例子(我尝试过的方法之一)对我不起作用。我伪造,在伪造上设置Expection,然后在Singleton上设置期望值以返回伪造的实例-但现在它返回具体的实例

这可能与调用构造函数的方式有关吗?我记得看到一些关于那个的


或者,我可以使用交换,但是,我希望能够在TestSetup中设置所有这些内容,并在实际测试中对预期进行微小的修改,但这看起来不可能

免责声明我在Typemock工作

您不需要模拟Singleton.GetInstance()。使用Isolate.Fake.AllInstances()而不是Isolate.Fake.Instance()可以模拟单例。然后,通过将行为设置为应用于所有实例的假单例行为

看看这个例子:

public class Singleton
    {
        private Singleton() { }
        static readonly Singleton instance = new Singleton();

        public static Singleton Instance { get {  return instance; } }

        public int ReturnZero()
        {
            return 0;
        }
    }

    [TestMethod]
    public void FakeSingleton()
    {
        // Here we are setting the same behavior on all instances.
        // The behavior we set on fake will apply to past instance as well
        var fakeSingleton = Isolate.Fake.AllInstances<Singleton>();
        Isolate.WhenCalled(() => fakeSingleton.ReturnZero()).WillReturn(10);

        // Assert that the behavior works.
        Assert.AreEqual(10, Singleton.Instance.ReturnZero());
    }
公共类单例
{
私有单例(){}
静态只读单例实例=新单例();
公共静态单例实例{get{return Instance;}}
公共int ReturnZero()
{
返回0;
}
}
[测试方法]
公开作废伪造文件()
{
//在这里,我们在所有实例上设置相同的行为。
//我们设置为false的行为也将应用于过去的实例
var fakeSingleton=Isolate.Fake.AllInstances();
当调用(()=>fakeSingleton.ReturnZero())时,将返回(10);
//断言该行为有效。
AreEqual(10,Singleton.Instance.ReturnZero());
}