How to teardown a specflow scenario
Asked Answered
A

1

13

I'm trying to create a new set of tests for testing a legacy web site that I'm working on. The site uses a database at the back end. I'm planning on using SpecFlow and Selenium, however I'm a bit stumped on what the best way to deal with cleaning up data is.

Currently I have a database backup with a set of sample data which I restore before each test run. This however is cumbersome so I would like to only do this for critical test runs before a release and leave the continuous integration runs working on the same database in between.

Currently I have a large number of tests that go something like this:

Secenario: Test Item Creation
    Given I am logged in
    When I create an item with a unique name
    Then an item exists with the unique name

The when step uses a GUID to ensure the name is unique and the then step has access to this via a module variable to check that it exists.

Like I said however I have a lot of tests similar to this and I'm running them multiple times on the same database so the test system is getting stuffed full of items which is slowing down searches and the like.

My question is what is the best way to deal with this? Should I create another step in the test that deletes the item again like this:

Secenario: Test Item Creation
    Given I am logged in
    When I create an item with a unique name
    Then an item exists with the unique name
    Then delete the item with the unique name

Or should my test framework somehow be able to deal with this? If so what do people do? Given SpecFlow step's global nature I'd imagine that getting the teardown steps in the correct order if multiple items with parent-child relationships could become problematic.

Ariose answered 29/3, 2016 at 12:50 Comment(0)
P
14

A good test should have no dependencies, so having test steps create and then "tear down" test data is a good idea.

One approach you could take is to store the unique name generated by:

When I create an item with a unique name

in the ScenarioContext object e.g.

ScenarioContext.Current["testItem"] = "testItemName";

This will allow you to persist this value for the duration of the scenario.

You could then create a SpecFlow hook associated with a specific tag to tear-down this data when the scenario completes e.g. The scenario would be (note the tag):

@deleteTestItem
Secenario: Test Item Creation
Given I am logged in
When I create an item with a unique name
Then an item has exists with the unique name

And the code to clean up would be:

[AfterScenario("deleteTestItem")]
public void DeleteTestItem()
{
 var testItemName = ScenarioContext.Current["testItemName"];

 // Use testItemName to clean-up your database
}

This code will then only be run for scenarios which have this tag. Note that if all your tests involved creating a test item, then you could omit the tag and simply use an AfterScenario hook.

Alternatively you could persist all of the test item names in the FeatureContext, and then delete these items in an AfterFeature hook. This would lead to less database calls (i.e. you would not be calling the database to clean-up after every scenario).

I prefer the ScenarioContext approach as I feel that if a Scenario creates data, then that scenario should be responsible for cleaning-up after itself.

Philomel answered 29/3, 2016 at 14:27 Comment(6)
I would prefer an explicit context object which is passed via the constructor of the step classes via context injection and populated there, rather then using the ScenarioContext.Current (which can't be used in parallel tests in specflow 2.0), but this is basically the right approach I feel.Bartko
@SamHolder ScenarioContext.Current is an explicit context object i.e. it is exclusive to that scenario instance. What issues does it have with parallel tests?Philomel
yes, I understand what ScenarioContext.Current is, I just think its not the best way to share state between steps in Specflow. There are basically 3 ways, and using an explicit class for specific state rather than using the generic ScenarioContext is much preferable IMHO. In the example given I would probably create a ItemContext class with a property Name which I would set to the Guid. Then you can use this class in typesafe way rather than relying on the string (and casting) based approach of the ScenarioContext.CurrentBartko
You can see the write up for the new parallel test support here. I think it explains what the issues are with using ScenarioContext.Current in this new paradigm and what the suggested solution is (ie to inject an instance of ScenarioContext), but IMHO if you are going to do that you may as well create a specific context object which has the strong typing benefitsBartko
and by explicit context I didn't mean specific to that scenario, I meant specific to a particular task (ie holding reference to the created item id in this case). But good answer, I was just pointing out additional options as I don't believe using ScenarioContext is good practice (I would have liked it to disappear in 2.0, but hey ho!)Bartko
Hi Sam, yes I am well aware that you know what ScenarioContext.Current is ;D I'll take a look at those links. Whilst using a strongly typed explicit context object is good, in this scenario, as the OP is just dealing with a single string, I think using the ScenarioContext is "good enough" and is Specflow's current text book approach. If more data had to be saved, then I'd advocate an explicit context object. As for parallelism and ScenarioContext, I was unaware of that issue with the current implementation. Boils down to whether Martin is running these tests in parallel.Philomel

© 2022 - 2024 — McMap. All rights reserved.