Is having specific user story scenarios evil?
So I know that when it comes to user stories scenarios, being specific is a good thing. However, I frequently come to a point where I ask myself: How specific should my scenario be?
For instance, for the user story:
In order to allow project members to collaborate over a project, as a project manager, I want to be able to register new projects
We can have the following scenario:
Given a project has never been registered in the system, when a project manager registers that project, the registered project should appear in the specified member's list of projects
Or we can be more specific with something like:
Given Scott is a project manager and stackoverflow integration project has never been registered in the system, when Scott registers stackoverflow integration project specifying Jane as a project member, then stackoverflow integration project should appear in Jane's list of projects
I've found the 2nd "more specific" one to be handy when writing BDD specifications. Where having something like scottTheProjectManager instead of projectManagerStub is:
- more aligned to the real world (we don't have stubs working as project managers.. or do we?)
- easier to refer to whenever needed in that specification context (otherwise, I will keep saying "that project manager" or "the project manager who registered the project"... etc.
Am I right in my conclusion? Will that specificity harm me when a change occur on a story?
Thanks a lot!
Update
The question above is not only about having person names instead of roles names, it's about replacing all placeholders in your scenario with names of real instances. And by real instances I don't mean that we actually have someone called Scott working as a project manager, it's just giving names to abstract placeholders in order to realize the aforementioned benefits.
I will try to show how these benefits are realized by including the following code which represents a full BDD style specification using StoryQ framework
[TestFixture]
public class ProjectRegistrationSpecs
{
[Test]
public void ProjectRegistration()
{
new Story("Project Registration")
.InOrderTo("allow project members to collaborate over a project")
.AsA("project manager")
.IWant("to be able to register new projects")
.WithScenario("New Project Registration")
.Given(ScottIsAProjectManager)
.And(StackoverflowIntegrationProjectHasNeverBeenRegistered)
.When(ScottRegistersStackoverflowIntegrationProjectSpecifyingJaneAsAnAnalyst)
.Then(StackoverflowIntegrationProjectShouldAppearInJanesListOfProjects)
.Execute();
}
//Since Scott and Jane are just instances that have meaning in the context of this user story only, they're defined private
private ProjectManager scottTheProjectManager;
private Project stackOverFlowIntegrationProject;
private Employee janeTheAnalyst;
//Initialize the stubs in the constructor
public ProjectRegistrationSpecs()
{
scottTheProjectManager = new ProjectManager()
{
Id = new Guid("{A1596CFC-5FA5-4f67-AC7E-5B140F312D52}")
};
stackOverFlowIntegrationProject = new Project()
{
Id = new Guid("{F4CD5DDE-861E-4e18-8021-730B7F47C232}"),
Name = "Stack Overflow Integration"
};
}
private void ScottIsAProjectManager()
{
container.Get<IDataProvider>().Repository<ProjectManager>().Add(scottTh开发者_如何转开发eProjectManager);
}
private void StackoverflowIntegrationProjectHasNeverBeenRegisteredInTheSystem()
{
var provider = container.Get<IDataProvider>();
var project = provider.Repository<Project>().SingleOrDefault(p => p.Name == stackOverFlowIntegrationProject.Name);
if (null != project)
provider.Repository<Project>().Delete(project);
}
private void ScottRegistersStackoverflowIntegrationProjectSpecifyingJaneAsAProjectMember()
{
stackOverFlowIntegrationProject.Members.Add(janeTheAnalyst);
scottTheProjectManager.RegisterProject(stackOverFlowIntegrationProject);
}
//instead of saying something like TheProjectThatWasAddedByTheProjectManagerShouldAppearInTheProjectMembersList, we have:
private void StackoverflowIntegrationProjectShouldAppearInJanesListOfProjects()
{
Assert.That(janeTheAnalyst.Projects.Any(p => p.Id == stackOverFlowIntegrationProject.Id));
}
}
The first "scenario" you give is actually acceptance criteria. It specifies a rule which should work for all types of project managers, members and specified projects.
The second scenario is an example of the acceptance criteria in action, using realistic data. The most important aspect of BDD is to use examples like this in conversation so that any other aspects of the acceptance criteria for the story can be discussed.
For instance, given the stackoverflow integration project has already been registered, what happens when Scott tries to register it again? Is there any scenario in which Jane's assignment to the project can be rejected? Is Jane's assignment to the project the only valuable outcome? Does she get an email about it?
You can probably see already how much easier it is to use specific data when discussing a scenario. Remember too that:
- having the conversations is more important than
- capturing the conversations which is more important than
- automating the conversations.
If you get to do all three at the same time then kudos, but don't compromise on the conversations for the sake of automation.
"User personas" are a very powerful but difficult to master concept. Done right, they can steer you towards simplified, optimized user experiences. Done wrong, they take up time and give people on a team a subjective reason to yell at each other.
Check out http://www.agile-ux.com/2009/12/02/personas-in-agile-development-yes-we-can/ for a starting point. There is tons of literature on this topic though mostly not specific to BDD.
The most important piece of advice I can give is that when defining your personas, it is important to be very specific about what delights/motivates/frustrates them but that specificity should be in terms of the world as a whole or computing in general - don't tie those things to specific aspects of your product or personal aspirations for the product because otherwise 1) the persona will quickly become outdated and need to be recreated and 2) the persons will end up being used as a subjective weapon to further your personal aspirations rather than as a fact-based tool to improve make the best possible product for real people. This is much easier said than done.
It is good to use names for the roles there, as they make it easier to understand the story; they engage more of your primate brain, for it loves individuals and hates abstraction. However, you must take care! Try to ensure that the names you use don't match the name of anyone you're actually work with – that'd be horribly confusing – and don't encode prejudices – the project is not a prejudiced thing, as it can't have opinions. Also don't use the same name for different roles; while reality might be a messy mix with people having many roles, the user stories should not be mixed up that way because it's just plain confusing.
Compare the following from your example:
Given Scott is a project manager and stackoverflow integration project has never been registered in the system, When Scott registers stackoverflow integration project specifying Jane as a project member, then stackoverflow integration project should appear in Jane's list of projects
VS:
Given a Project Manager and an unregistered Project, When the Project Manager registers the Project and specifies a Team Member, Then the Project should appear in the Team Member's list of projects.
Notice that the wording of the second example above is not too different from the first example that you gave in your question, yet it is subtly different so that it reads as if it is a little more complete. I think in a way that this is what you may feel is missing, and if that is the case, I would treat it as a kind of a "story smell" and if that is the case I would keep asking questions and rewording the until the story/scenario feels more complete.
Personally I find the use of persona's distracts me from the task at hand, and I don't like ending up with my test code littered with text that doesn't read as if it belongs to the stuff that I am testing. If on the other hand you are taking a data-driven approach and you define some sort of data structure for the purpose, it then makes sense to populate that structure with names that can make sense within the scope where they are being tested.
I think that one of the risks with using persona's is that they can sometimes end up as a substitute for lazy method naming, or even worse as a means for bored programmers to try and inject a little humour into their code. Not that I think it's bad to have a little fun with your names, but if they don't provide value to the stake holders then they shouldn't be used.
Another thought occurs to me though, that you can have the best of both worlds. If you specify your user persona's as data to feed to your story scenario's, then you have the benefit of keeping your code clean and to the point, yet you can make the output appear a little more specific in terms of the actual test execution. For example:
new Story("Project Registration")
.InOrderTo("allow project members to collaborate over a project")
.AsA("project manager")
.IWant("to be able to register new projects")
.WithScenario("New Project Registration")
.Given(AProjectManager, "Scott")
.And(AnUnRegisteredProject, "Stack Overflow Integration")
.When(TheProjectManagerRegistersTheProject)
.And(TheProjectManagerSpecifiesATeamMember, "Jane")
.Then(ThenTheProjectShouldAppearInTheTeamMembersListOfProjects)
.Execute();
While your aproach having a named specific user bay be much easier to understand for the domain expert (because the domain expert knows *Scott_the_project_manager* and his tasks) but i think this aproach violates the Single_responsibility_principle: One Person can have multible roles.
Scott may also be involved in marketing and *Magret_the_project_manager* may have different responsibilities than Scott
精彩评论