Подтвердить что ты не робот

Вымывание вызова метода базового класса с помощью Moq

Я модифицирую метод класса, который форматирует некоторые даты ввода paramater, которые впоследствии используются в качестве параметров в вызове метода в базовый класс (который живет в другой сборке).

Я хочу проверить, что даты, которые я передаю моему методу, находятся в правильном формате, когда они передаются методу базового класса, поэтому я хотел бы, чтобы метод запроса метода базового класса Moq. Возможно ли это с помощью Moq?

4b9b3361

Ответ 1

Если я правильно понял ваш вопрос, у вас есть класс A, определенный в какой-то другой сборке, а затем класс B реализован примерно так:

public class B : A
{
    public override MyMethod(object input)
    {
        // Do something
        base.MyMethod(input);
    }
}

И теперь вы хотите проверить, что base.MyMethod вызывается?

Я не вижу, как вы можете это сделать с помощью динамической макетной библиотеки. Все динамические макетные библиотеки (с использованием TypeMock) работают путем динамического испускания классов, которые вытекают из рассматриваемого типа.

В вашем случае вы не можете очень хорошо спросить Moq, чтобы получить от A, так как вы хотите проверить B.

Это означает, что вы должны попросить Moq дать вам Mock<B>. Однако это означает, что испущенный тип происходит от B, и хотя он может переопределить MyMethod (который по-прежнему является виртуальным) и вызвать его базу (B.MyMethod), у него нет способа добраться до исходного класса и убедиться, что B вызывает base.MyMethod.

Представьте, что вам нужно написать класс (C), который происходит от B. Хотя вы можете переопределить MyMethod, вы не можете проверить, что B вызывает A:

public class C : B
{
    public override MyMethod(object input)
    {
        // How to verify that base calls its base?
        // base in this context means B, not A
    }
}

Опять же, за исключением, возможно, TypeMock, динамические макетные библиотеки не могут делать ничего, что вы не можете сделать вручную.

Однако я бы предположил, что вызов базового метода, который вы пытаетесь проверить, имеет некоторый наблюдаемый побочный эффект, поэтому, если это возможно, можете ли вы использовать тестовое тестирование на основе состояния вместо тестирования на основе поведения, чтобы проверить результат вызова метода?

В любом случае, государственное тестирование должно быть вашим стандартным методом в большинстве случаев.

Ответ 2

С 2013 года с последним Moq вы можете. Вот пример

public class ViewModelBase
{
    public virtual bool IsValid(DateTime date)
    {
        //some complex shared stuff here
    }
} 

public class MyViewModel : ViewModelBase
{
    public void Save(DateTime date)
    {
        if (IsValid(date))
        {
            //do something here
        }
    }
}

public void MyTest()
{
    //arrange
    var mockMyViewModel = new Mock<MyViewModel>(){CallBase = true};
    mockMyViewModel.Setup(x => x.IsValid(It.IsAny<DateTime>())).Returns(true);

    //act
    mockMyViewModel.Object.Save();

    //assert
    //do your assertions here
} 

Ответ 3

Это вполне возможно насмешливый базовый класс. Но вам нужно будет изменить целевой класс.

Например, DerivedClass расширяет BaseClass. BaseClass имеет методы MethodA(), MethodB(), MethodC()... У DerivedClass есть этот метод:

void MyMethod() {
  this.MethodA();
  this.MethodB();
  this.MethodC();
}

Вы хотите издеваться над базовым классом, чтобы проверить, что все MethodA(), MethodB(), MethodC() внутри MyMethod().

Вам нужно создать поле в DerivedClass:

class DerivedClass {
  private BaseClass self = this;
  ...
}

А также вы должны изменить MyMethod():

void MyMethod() {
  self.MethodA();
  self.MethodB();
  self.MethodC();
}

Также добавьте метод, который может ввести поле this.self с помощью объекта Mock

public void setMock(BaseClass mock) {
  this.self = mock;
}

Теперь вы можете высмеивать:

DerivedClass target = new DerivedClass ();
BaseClass  mock = new  Mock(typeof(BaseClass));
target.setMock(mock);
target.MyMethod();

mock.verify(MethodA);
mock.verify(MethodB);
mock.verify(MethodC);

Используя эту технику, вы также можете имитировать вложенные вызовы методов.

Ответ 4

обернуть метод базового класса в метод и настроить этот метод например.

public class B : A
{
    public virtual BaseMyMethod(object input)
    {
        // Do something
        base.MyMethod(input);
    }    
public override MyMethod(object input)
    {
        // Do something
        BaseMyMethod(input);
    }
}

и теперь установите BaseMyMethod

Ответ 5

Согласитесь с Mark, это невозможно с помощью Moq.

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

Ответ 6

Я нашел это решение - уродливым, но он мог работать.

var real = new SubCoreClass();                        
var mock = new Mock<SubCoreClass>();
mock.CallBase = true;

var obj = mock.Object;

mock
   .Setup(c => c.Execute())
   .Callback(() => 
      {                                                                       
         obj.CallBaseMember(typeof(Action), real, "Execute");             
         Console.WriteLine(obj.GetHashCode());
      }
      );

public static Delegate CreateBaseCallDelegate(object injectedInstance, Type templateDelegate, object instanceOfBase, string methodName)
{
   var deleg = Delegate.CreateDelegate(templateDelegate, instanceOfBase, methodName);
   deleg.GetType().BaseType.BaseType.GetField("_target", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(deleg, injectedInstance);

   return deleg;
}

public static object CallBaseMember(this object injectedInstance, Type templateDelegate, object instanceOfBase, string methodName, params object[] arguments)
{
   return CreateBaseCallDelegate(injectedInstance, templateDelegate, instanceOfBase, methodName).DynamicInvoke(arguments);
}