Как мне сбросить результат для свойства в заглушке без сброса всей заглушки? - PullRequest
1 голос
/ 02 ноября 2009

Я новичок в Rhino Mocks, так что я могу что-то упустить полностью.

Допустим, у меня есть интерфейс с полдюжины свойств:

public interface IFoo {
  string Foo1 { get; } // Required non-null or empty
  string Foo2 { get; } // Required non-null or empty
  string Foo3 { get; }
  string Foo4 { get; }
  int Foo5 { get; }
  int Foo6 { get; }
}

И реализация, которая принимает похожий объект, но без тех же ограничений и создает экземпляр IFoo:

public interface IFooLikeObject {
  string FooLikeObject1 { get; } // Okay non-null or empty
  string FooLikeObject2 { get; } // Okay non-null or empty
  string FooLikeObject3 { get; }
  string FooLikeObject4 { get; }
  string FooLikeObject5 { get; } // String here instead of int
  string FooLikeObject6 { get; } // String here instead of int
}

public class Foo : IFoo {
  public Foo(IFooLikeObject fooLikeObject) {

    if (string.IsNullOrEmpty(fooLikeObject.Foo1)) {
      throw new ArgumentException("fooLikeObject.Foo1 is a required element and must not be null.")
    }

    if (string.IsNullOrEmpty(Foo2)) {
      throw new ArgumentException("fooLikeObject.Foo2 is a required element and must not be null")
    }

    // Make all the assignments, including conversions from string to int...

  }
}

Теперь в моих тестах я хочу проверить, что исключения генерируются в нужное время, а также исключения, возникающие при неудачном преобразовании строки в int.

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

public IFooLikeObject CreateBasicIFooLikeObjectStub(MockRepository mocks) {
  IFooLikeObject stub = mocks.Stub<IFooLikeObject>();

  // These values are required to be non-null
  SetupResult.For(stub.FooLikeObject1).Return("AValidString");
  SetupResult.For(stub.FooLikeObject2).Return("AValidString2");
  SetupResult.For(stub.FooLikeObject5).Return("1");
  SetupResult.For(stub.FooLikeObject6).Return("1");
}

Этого достаточно для тестирования Foo3 и Foo4, но при тестировании Foo1, 2, 5 или 6 я получаю:

System.InvalidOperationException : The result for IFooLikeObject.get_FooLikeObject1(); has already been setup. Properties are already stubbed with PropertyBehavior by default, no action is required

Например:

[Test]
void Constructor_FooLikeObject1IsNull_Exception() {
  MocksRepository mocks = new MocksRepository();
  IFooLikeObject fooLikeObjectStub = CreateBasicIFooLikeObjectStub(mocks);

// This line causes the exception since FooLikeObject1 has already been set in CreateBasicIFooLikeObjectStub()
  SetupResult.For(fooLikeObjectStub.FooLikeObject1).Return(null); 

  mocks.ReplayAll();

  Assert.Throws<ArgumentException>(delegate { new Foo(fooLikeObjectStub); });
}

Как я могу настроить его так, чтобы я мог переопределить отдельное свойство, для которого уже установлено возвращаемое значение, без необходимости повторного выполнения всех остальных?

Ответы [ 2 ]

1 голос
/ 04 ноября 2009

Это можно сделать с помощью конструкции Repeat.Any().

Я не проверял это с помощью синтаксиса SetupResult.For, но он работает с лямбда-синтаксисом:

public IFooLikeObject CreateBasicIFooLikeObjectStub(MockRepository) {
  IFooLikeObject stub = MockRepository.GenerateStub<IFooLikeObject>();

  // These values are required to be non-null
  stub.Stub(s => s.FooLikeObject1).Return("AValidString");
  stub.Stub(s => s.FooLikeObject2).Return("AValidString2");
  stub.Stub(s => s.FooLikeObject5).Return("1");
  stub.Stub(s => s.FooLikeObject6).Return("1");
}

[Test]
void Constructor_FooLikeObject1IsNull_Exception() {
  IFooLikeObject fooLikeObjectStub = CreateBasicIFooLikeObjectStub();

  // This line no longer causes an exception
  stub.Stub(s => s.FooLikeObject1).Return(null).Repeat.Any(); // The Repeat.Any() is key. Otherwise the value wont be overridden.

  Assert.Throws<ArgumentException>(delegate { new Foo(fooLikeObjectStub); });
}

Единственное замечание, которое я нашел, это то, что вы не можете сделать это дважды.

0 голосов
/ 02 ноября 2009

Возможно, я что-то упустил, но вы пытались просто сделать это?

var stub = mocks.Stub<IFooLikeObject>();

stub.FooLikeObject1 = "AValidString";
stub.FooLikeObject2 = "AValidString2";
stub.FooLikeObject5 = "1";
stub.FooLikeObject6 = "1";

С заглушками вы можете просто установить свойства так, как вы хотите, чтобы они были непосредственно.

Если свойство доступно только для чтения, вы можете сделать это следующим образом:

var stub = mocks.Stub<IFooLikeObject>();

stub.Stub( x => x.FooLikeObject1).Return("AValidString");
stub.Stub( x => x.FooLikeObject2).Return("AValidString2");
stub.Stub( x => x.FooLikeObject5).Return("1");
stub.Stub( x => x.FooLikeObject6).Return("1");
...