Как получить прокладки для базовых классов с помощью Microsoft Fakes?

class Parent{
   public string Name{ get; set; }
}

class Child :Parent{
   public string  address{ get; set; }
}

[TestClass]
class TestClass{
   [TestMethod]
   public void TestMethod()
   {
      var c = new Fakes.Child();
      c.addressGet = "foo"; // I can see that
      c.NameGet = "bar"; // This DOES NOT exists
   }
}

Как установить "имя" в приведенном выше примере кода?

Ответ 1

Вам придется объявить его в базовом классе. Самый простой способ - назвать базовый класс своим свойством AllInstances:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        ClassLibrary1.Child myChild = new ClassLibrary1.Child();

        using (ShimsContext.Create())
        {
            ClassLibrary1.Fakes.ShimChild.AllInstances.addressGet = (instance) => "foo";
            ClassLibrary1.Fakes.ShimParent.AllInstances.NameGet = (instance) => "bar";

            Assert.AreEqual("foo", myChild.address);
            Assert.AreEqual("bar", myChild.Name);
        }

    }
}

Также всегда старайтесь добавить ShimsContext, чтобы обеспечить правильную очистку вашей прокладки. В противном случае ваши другие юнит-тесты также получат возвращаемые значения, которые вы объявили ранее. Информацию о ShimsContext можно найти здесь: http://msdn.microsoft.com/en-us/library/hh549176.aspx#ShimsContext

Ответ 2

Сгенерированный класс для Parent будет иметь конструктор, который выглядит так: ShimParent(Parent p).

Все, что вам нужно сделать, это:

var child = new ShimChild();
var parent = new ShimParent(child);

И установите соответствующие значения для соответствующих Shim's.

Ответ 3

Я собрал решение на основе предыдущих ответов, документации Microsoft и моих собственных экспериментов. Я также немного изменил TestMethod, чтобы показать, как я на самом деле использую его для тестирования. Примечание. Я не скомпилировал этот конкретный код, поэтому прошу прощения, если он не работает, как есть.

[TestClass]
class TestClass
{
   [TestMethod]
   public void TestMethod()
   {
      using (ShimsContext.Create())
      {
         Child child = CreateShimChild("foo", "bar");

         Assert.AreEqual("foo", child.address);  // Should be true
         Assert.AreEqual("bar", child.Name);     // Should be true
      }
   }

   private ShimChild CreateShimChild(string foo, string bar)
   {
      // Create ShimChild and make the property "address" return foo
      ShimChild child = new ShimChild() { addressGet = () => foo };

      // Here the trick: Create a ShimParent (giving it the child)
      // and make the property "Name" return bar;
      new ShimParent(child) { NameGet = () => bar };

      return child;
   }
}

Я понятия не имею, как возвращенный ребенок знает, что его Name должен возвращать "бар", но он это делает! Как вы можете видеть, вам даже не нужно сохранять ShimParent в любом месте; он создается только для того, чтобы указать значение для свойства Name.

Ответ 4

Ни один из предложенных подходов до сих пор не работал бы, на мой взгляд. После многих проб и ошибок я придумал этот ниже код, который работал у меня. В основном вам нужно будет определить делегата, который инициализирует ваш дочерний класс, и внутри этого делегата вы подключите Shim родителя, который должен наследовать ваш дочерний класс.

public void TestMethod()
    {
        //var c = new Fakes.Child();
        //c.addressGet = "foo"; // I can see that
        //c.NameGet = "bar"; // This DOES NOT exists

        using (ShimsContext.Create())
        {
            ShimChild childShim = null;
            ShimChild.Constructor = (@this) =>
            {

                childShim = new ShimChild(@this);
                // the below code now defines a ShimParent object which will be used by the ShimChild object I am creating here
                new ShimParent()
                {
                    NameSetString = (value) =>
                    {
                        //do stuff here
                    },
                    NameGet = () =>
                    {
                        return "name";
                    }
                };
            };
        }
    }