пятница, 15 апреля 2011 г.

NBehave: share context between action steps

Foreword
Some terms before we started.

Specification (AKA Spec) - describes a useful product feature which is supplied by several Scenarios

Context - is a System Under Test (SUT) state. Action Steps in general are actions to manipulate and validate Context

Main Part

A couple of days ago there was a private talking in the kitchen in the office.

We were discussing our approach of writing specifications.

Usually it comes with one solid class:


The problems with it are:
  • Specs class is too huge and often became unmanagable
  • We can't extract action steps into different classes as they refer to class instance fields so we can't reuse actions steps across specs
After some discussion we refactored it to the following model:



You see: our specs from now are absolutely independent on where Action Steps either Context located! That's a great benefit!

After a small refactoring in we've got 3 clearly separated classes: Specification, Action Steps and Context.

See some code below. Full examples can be found in US#33279, Plugins/Tp.Integration.Plugin.PopEmailIntegration folder

Specification

[TestFixture]

public class EmailProcessingSagaSpecs

{

[Test]

public void ShouldSkipAttachmentToDeletedProject()

{

@"Given project 1

And requester with email 'sender@company.com'

And sender email is 'sender@company.com'

And profile has rules:

|Rule |

|then attach to project 2 |

|then attach to project 1 |

When the email arrived

Then the message should be attached to project 1"

.Execute(In.Context<EmailProcessingSagaActionSteps>());

}

Action Steps

[ActionSteps]

public class EmailProcessingSagaActionSteps

{

[BeforeScenario]

public void BeforeScenarioInit()

{

ObjectFactory.Initialize(x => { });

}

#region Action Steps

private static EmailProcessingSagaContext Context

{

get { return ObjectFactory.GetInstance<EmailProcessingSagaContext>(); }

}

[Given("project $projectId")]

public void AddProject(int projectId)

{

var projectStorage = Context.Storage.Get<ProjectDTO>();

projectStorage.Add(new ProjectDTO {ProjectID = projectId});

}

Context

public class EmailProcessingSagaContext : PopEmailIntegrationContext

{

private readonly EmailMessage _emailMessage;

private readonly IList<AttachmentDTO> _attachments = new List<AttachmentDTO>();

public EmailProcessingSagaContext()

{

//Self-register Context in object factory in order to ensure single context instance during specs execution

ObjectFactory.Configure(x => x.For<EmailProcessingSagaContext>().Use(this));

_emailMessage = new EmailMessage {FromDisplayName = "John Smith"};

}

public EmailMessage EmailMessage

{

get { return _emailMessage; }

}

public IList<AttachmentDTO> Attachments

{

get { return _attachments; }

}

Комментариев нет:

Отправить комментарий