Как смоделировать определенный код с помощью Rhino.Mocks? - PullRequest
2 голосов
/ 10 ноября 2011

У меня есть следующий код:

public interface IFlowFolderHandler
{
  OperationResult Post(FlowFolder dto);
}

public class FlowFolderHandler : IFlowFolderHandler
{
  private readonly IResponse m_response;
  private readonly IEntityRepository m_entityRepository;

  public FlowFolderHandler(IResponse response, IEntityRepository entityRepository)
  {
    m_response = response;
    m_entityRepository = entityRepository;
  }

  public OperationResult Post(FlowFolder dto)
  {
    var ent = FlowFolderX.Cast(dto, true);
    m_entityRepository.Update(ent);
    var id = EntityIdComparer.IdToString(ent.Id);
    m_response.Headers["X-Entity-Id"] = id;
    return new OperationResult.Created
    {
      RedirectLocation = new Uri("FlowFolder/" + id, UriKind.Relative),
      ResponseResource = ent.GetEntityRelation(),
    };
  }
}

Я хочу провести модульное тестирование метода FlowFolderHandler.Post, используя макеты IResponse и IEntityRepository. Моя текущая реализация использует следующие ручные макеты:

private class EntityRepositoryMock : IEntityRepository
{
  private readonly Action<IEntityBase> m_update;
  public EntityRepositoryMock(int id, EntityRelation entityRelation)
  {
    m_update = ent => EntityRepository.ApplySaveSideEffects(ent, id, entityRelation);
  }
  public IEntityBase Fetch(EntityId entityId) { throw new NotImplementedException(); }
  public void FetchByExpression(Type entityType, Expression expr, IList list, List<Pair<string, bool>> orderBy) { throw new NotImplementedException(); }
  public void Update(IEntityBase entity) { m_update(entity); }
}

private class ResponseMock : IResponse
{
  public ResponseMock() { Headers = new HttpHeaderDictionary(); }
  public IHttpEntity Entity { get { throw new NotImplementedException(); } }
  public HttpHeaderDictionary Headers { get; private set; }
  public void WriteHeaders() { throw new NotImplementedException(); }
  public bool HeadersSent { get { throw new NotImplementedException(); } }
  public int StatusCode
  {
    get { throw new NotImplementedException(); }
    set { throw new NotImplementedException(); }
  }
}

И юнит тест:

[Test]
[Factory("YieldPostFlowFolderData")]
public void PostFlowFolder(int id, Uri uri, EntityRelation entityRelation, FlowFolder flowFolder)
{
  var entityRepository = new EntityRepositoryMock(id, entityRelation);
  var response = new ResponseMock();
  var handler = new FlowFolderHandler(response, entityRepository);

  var result = handler.Post(flowFolder);

  Assert.AreEqual((int)HttpStatusCode.Created, result.StatusCode);
  Assert.AreEqual(id, int.Parse(response.Headers["X-Entity-Id"]));
  Assert.AreEqual(uri, result.RedirectLocation);
  SUC.Utils.AssertDeepEqual(entityRelation, result.ResponseResource);
}

У меня вопрос, как я могу написать один и тот же модульный тест (более или менее) без ручных макетов EntityRepositoryMock и ResponseMock с использованием Rhino.Mocks?

Спасибо.

EDIT

Мои ручные макеты содержат конструктор только потому, что они являются ручными макетами. Я мог бы добавить выделенный метод PrepareMock вместо конструктора, чтобы убедиться, что макет готов для ... макета. Автоматические насмешки, созданные Rhino.Mocks, не будут иметь проблем, связанных с конструктором, потому что я буду пересмеивать интерфейсы, а не конкретные типы.

Ответы [ 2 ]

2 голосов
/ 10 ноября 2011
// Mocks
var repositoryMock = MockRepository.GenerateMock<IEntityRepository>();
var responseMock = MockRepository.GenerateMock<IResponse>();

Используя Expect (), вы можете настроить любое ожидаемое вами ожидание, например, exp [ectation для метода Update (), который выполняет только одно, а не генерирует исключение:

repositoryMock.Expect(m => m.Update(null)).IgnoreArguments.WhenCalled(
   mi =>
   {
      IEntityBase passedInEntity = mi.Args[0] as IEntityBase;
      EntityRepository.ApplySaveSideEffects(passedInEntity, id, entityRelation); 
   }
).Repeat.Any();

Также одним важным моментом является макетирование свойств, вы должны указать PropertyBehaviour():

repositoryMock.Expect(m => m.Entity).Proeprtybehaviour();

РЕДАКТИРОВАТЬ: Чтобы проверить, что метод был вызван один раз

repositoryMock.AssertWasCalled(m => m.Update(), mi => mi.Repeat.Once());

Полезные ссылки:

0 голосов
/ 10 ноября 2011

Из любопытства, ваши "ручные" насмешки доставляют вам неприятности, или вы просто ищете более чистый способ их реализации?Иногда «ручной» путь - лучший (самый простой) путь, так как большинство фреймворков имеют свои собственные проблемы.

В любом случае, сказав это, основные шаги к таким классам шутки, как выdone:

  1. Создать объект MockRepository
  2. Попросить объект MockRepository создать макет вашего класса / интерфейса.Вы можете запросить множество различных типов издевательств (динамические, частичные, заглушки и т. Д.), Вам необходимо прочитать документ, чтобы выяснить, какой из них подходит вам.
  3. Использованиеобработчик Do (), чтобы ваши mocks выполняли определенные биты кода, которые вы хотите (похоже, это то, что вы делаете выше, особенно с EntityRepositoryMock.Update

Обработчик Do () документированздесь: http://ayende.com/wiki/Rhino+Mocks+The+Do()+Handler.ashx

Ваши фиктивные объекты также имеют код в конструкторах. К вашему сведению, вы можете столкнуться с некоторыми проблемами с конструкторами. Из Ayende:

Техническое ограничение с классами насмешек:что создание фиктивного объекта вызывает конструктор целевого класса из-за имитации, производной от целевого класса.

На этой странице обсуждались ctors: http://ayende.com/wiki/Rhino+Mocks+Mocking+classes.ashx

...