单元测试:TypeMocking一个单例

时间:2009-04-07 10:33:50

标签: unit-testing mocking typemock

我正在使用TypeMock Isolater来模拟某些单元测试的某些对象 - 尝试使用AAA api(所以使用Isolate调用)。

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

我试过了:

  • 使用MST项目(使用Accessor类)将模拟对象直接分配给实例变量(使用Memers.MustSpecifyReturnValues伪造对象,使用WithExactArguments设置期望的Isolate.WhenCalled),但由于某种原因,模拟对象< em> always 返回null(并且没有例外)。
  • 模拟Singleton.GetInstance()以返回模拟对象。这将返回一个需要WhenCalled设置的模拟对象,但现在我所做的Isolate.WhenCalled调用似乎对假对象没有任何作用 - 所以所有调用都会抛出一个意外的调用异常。
  • 我也试过模拟实际的方法调用(例如Singleton.GetInstance()。Test()),它将用于调用该方法,但是对单例的其他方法的所有其他调用都返回null而不是抛出一个我想要的异常(因为这似乎会自动模拟所有没有Members.MustSpecifyReturnValues的对象)。

我想要的只是模拟一个单例,并且我没有明确告诉它期望抛出异常的任何调用。我觉得这很简单,但显然不是!悲伤

有谁知道我做错了什么?

由于 詹姆斯

4 个答案:

答案 0 :(得分:3)

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

[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 );
}

除非在测试之前创建单例类,否则此解决方案应适用于大多数场景。 如果您需要在创建类之后设置行为,请使用WhenCalled:

[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 );
}

答案 1 :(得分:2)

免责声明我在Typemock工作。

您无需模拟Singleton.GetInstance&lt;&gt;()。使用Isolate.Fake.AllInstances&lt;&gt;()而不是Isolate.Fake.Instance&lt;&gt;()可以模拟单例。然后通过设置应用于所有实例的伪单例行为的行为。

看看这个例子:

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());
    }

答案 2 :(得分:0)

感谢。

之前我没有尝试过NextInstance,因为它不适用于我不想改变的接口。

但是,我已经尝试了它确实有效 - 尽管我假设设置的顺序当时遇到的问题并不重要,但它肯定会这样做。例如,如果我在Swap之后执行WhenCalled,它就不起作用。它需要在Swap之前进行。 (说实话,对我来说真的没有意义 - 它应该是同一个对象)。

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

它是否与构造函数的调用方式有关?我记得有过这样的事情......

或者我可以使用Swap,但是,我希望能够在TestSetup中设置所有这些东西,并对实际测试中的期望进行微小修改,但这看起来不太可能。 ?

答案 3 :(得分:-2)

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