Создание насмешек вызывает PropertyChanged при изменении - PullRequest
4 голосов
/ 01 марта 2010

Я использую RhinoMocks, и у меня есть Mock, у которого есть свойство, которое мне нужно вести себя как реальное свойство - обновлять его значение при его установке, а также запускать PropertyChanged при изменении свойства.

Интерфейс смоделированного объекта по сути такой:

public interface IFoo
{
    event PropertyChangedEventHandler PropertyChanged;
    int Bar { get; set; }
}

При создании макета я устанавливаю PropertyBehavior - что заставляет его фактически обновлять его поддельное значение:

var mocks = new MockRepository();
var fakeFoo = mocks.DynamicMock<IFoo>();
SetupResult.For(fakeFoo.Bar).PropertyBehavior();

Но когда я обновляю значение, PropertyChanged не срабатывает. Теперь интерфейс не реализует интерфейс INotifyPropertyChanged, поскольку он является интерфейсом. Как я могу сделать PropertyChanged запущенным?

Ответы [ 2 ]

7 голосов
/ 02 марта 2010

Роль слушателя и мутатора иногда может быть объединена в одном классе (например, в адаптере), но обе роли не должны тестироваться вместе.

В одном тесте вы просто проверяете, что ваш класс прослушивания реагирует на событие PropertyChanged в соответствии с планом. Вас не волнует, что вызвало изменение свойства в этом тесте:

[Test]
public void Updates_Caption_when_Bar_PropertyChanged()
{
   var foo = MockRepository.GenerateStub<IFoo>();
   foo.Bar = "sometestvalue1";
   var underTest = new UnderTest(foo);

   // change property and raise PropertyChanged event on mock object
   foo.Bar = "sometestvalue2";
   foo.Raise(x=>x.PropertyChanged+=null,
       foo,
       new PropertyChangedEventArgs("Bar"));

   // assert that the class under test reacted as designed
   Assert.AreEqual("sometestvalue2", underTest.Caption);

   // or if the the expected state change is hard to verify, 
   // you might just verify that the property was at least read
   foo.AssertWasCalled(x => { var y = foo.Bar; } );
}

В другом тесте вы проверяете, что ваш класс играет свою роль мутатора, как было задумано:

[Test]
public void Reset_clears_Foo_Bar()
{
   var foo = MockRepository.GenerateStub<IFoo>();
   foo.Bar = "some string which is not null";
   var underTest = new UnderTest(foo);

   underTest.Reset();

   // assert that the class under test updated the Bar property as designed
   Assert.IsNull(foo.Bar);
}

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

1 голос
/ 02 марта 2010

Я не эксперт в RhinoMocks, но я бы не стал пытаться делать это с какой-либо из известных мне макетов (TypeMock, я знаю больше всего)

Я бы реализовал что-то вроде:

public class FooFake: IFoo
{
    public event PropertyChangedEventHandler PropertyChanged;
    int _bar;
    public int Bar
    {
       set
       {
           if( PropertyChanged != null )
               PropertyChanged();
           _bar = value;
       }
       get
       {
          return _bar;
       }
    }
}

Извините. Ничего особо умного. Но мне нравятся такие заглушки, так как их можно использовать повторно.

...