Тестируем интерфейсы с Rhino Mocks

Posted: Сентябрь 26, 2010 in .NET, Тестирование

Rhino Mocks — это фреймворк для создания динамических mock-объектов. Его целью является облегчение тестирования путем создания mock-реализации объектов и их проверки. Из этих предложений не совсем все понятно, потому что они являются калькой с английского языка. Давайте разберемся что здесь имелось ввиду.
Во первых Rhino Mock делает различие между mock- и stub-объектами. Заглянув в лингво, мы увидим следующий перевод
mock — подделка, фальсификация, жульничество
stub — суррогат, заглушка

С помощью mock-объектов можно записывать и проверять «ожидания» (модель Record, Playback), в то время как stub-объекты являются просто «заглушкой» (модель Arrange, Act, Assert). Опять не совсем понятно. Хорошо, рассмотрим пример. Пусть у нас есть вот такой интерфейс

public interface IRepository
{
	CommonObject GetByID(int id);
	int Count();
}

Давайте напишем unit-тест для него. Да-да, мы можем тестировать интерфейсы. Для случая Arrange, Act, Assert нам нужно создать заглушки для репозитория и метода (Arrange), вызвать метод (Act), проверить результат (Assert)

[TestMethod()]
public void CountTest()
{
	IRepository stubRepository = MockRepository.GenerateStub<IRepository>();
	stubRepository.Stub(s => s.Count()).Return(10);
	int i = stubRepository.Count();
	Assert.AreEqual(10, i);
}

Для случая Record, Playback нам нужно сначала записать наши ожидания, а затем проиграть сценарий. Посмотрим на более интересный метод GetByID(int id). Пусть у нас есть некий CommonObject, у которого есть только имя.

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

Для тестирования этого метода создадим «ожидание». Будем ожидать вызова объекта с номером 1 и возвращать некий «ожидаемый» объект. А затем непосредственно вызовем объект с номером 1.

[TestMethod()]
public void GetByIDTest()
{
	CommonObject expectedObject = new CommonObject() { Name = "Object1" };
	IRepository stubRepository;
	MockRepository mocks = new MockRepository();

	using (mocks.Record())
	{
		stubRepository = mocks.Stub<IRepository>();
		Expect.Call(stubRepository.GetByID(1)).Return(expectedObject);
	}

	CommonObject actualObject;
	using (mocks.Playback())
	{
		actualObject = stubRepository.GetByID(1);
	}
}

Есть более простая запись

[TestMethod()]
public void GetByIDTest()
{
	CommonObject expectedObject = new CommonObject() { Name = "Object1" };
	IRepository stubRepository;
	MockRepository mocks = new MockRepository();
	stubRepository = mocks.Stub<IRepository>();
	Expect.Call(stubRepository.GetByID(1)).Return(expectedObject);
	mocks.ReplayAll();
	CommonObject actualObject;
	actualObject = stubRepository.GetByID(1);
	mocks.VerifyAll();
}

И, наконец, для любителей fluent-синтаксиса

[TestMethod()]
public void GetByIDTest()
{
	CommonObject expectedObject = new CommonObject() { Name = "Object1" };
	CommonObject actualObject = null;

	MockRepository mocks = new MockRepository();
	IRepository stubRepository=mocks.Stub<IRepository>();

	With.Mocks(mocks).Expecting(delegate
	{
		Expect.Call(stubRepository.GetByID(1)).Return(expectedObject);
	})
	.Verify(delegate
	{
		actualObject = stubRepository.GetByID(1);
	});
}

Как мы видим, модель Arrange, Act, Assert гораздо проще и удобнее, чем Record, Playback. Но фреймворк постоянно обновляется, так что смотрите последнюю информацию на сайте. Возможно уже есть другая модель.

Реклама

Добавить комментарий

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

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s