Archive for the ‘Тестирование’ Category

NBehave и BDD

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

Давеча нашел в интернете библиотеку NBehave. На главной странице проекта было сказано, что данный фреймворк базируется на rbehave и использует DSL (язык специфичный для конкретной предметной области). Пройдя по ссылке «Introducing rbehave», наткнулся на интересное предложение «rbehave is a framework for defining and executing application requirements». То есть этот фреймворк позволяет «исполнять» требования к приложению. Понятно, что чтобы «исполнять» требования, последние должны быть записаны по определенным правилам. Но в каком виде, и кто будет писать эти требования? Особенностью работы по BDD является возможность использования DSL. То есть писать сценарии могут нетехнические специалисты (опять же по определенным правилам). NBehave помогает реализовывать BDD-разработку. Сначала создается история того, что происходит, затем создаются несколько конкретных сценариев с использование слов Given, When, Then, And, But. Для каждого сценария разработчик создает класс, для каждого предложения из сценария создает методы и запускает dll на соответствие сценарию. Идея в общем неплохая. По сути это надстройка над DDD и юнит-тестированием, в том плане, что размечая Given, вы создаете начальные данные. Размечая When, вы производите некоторые действия. Размечая Then, вы сравниваете то, что получили, с тем, что должно было быть на выходе. Кстати, для тестирования придется использовать один из фреймворков NUnit, XUnit, MbUnit или MsTest.
Более подробное описание есть по адресам

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. Но фреймворк постоянно обновляется, так что смотрите последнюю информацию на сайте. Возможно уже есть другая модель.