timschlechter / SpecResults

Get better feedback from your SpecFlow testsuite
http://specflowreporting.azurewebsites.net/
MIT License
23 stars 18 forks source link

Can't call other steps from step definitions #12

Open harvinders opened 10 years ago

harvinders commented 10 years ago

If we extend the class from ReportingStepDefinitions then we can't use the feature described at Calling Steps from Step Definitions as it requires us to extend the class from Steps

michael9876 commented 9 years ago

you can with a workaround and adjusting Specflow.reporting slightly. First you need another class to inherit from Steps - in my snippet below thats simply: public class MetaSteps : Steps { }

Secondly you need to set some methods to public in Specflow.Reporting (those that are shown as inaccessable using the snippet below)

Using that, i can use the following method in my GlueCode, that does the trick:

    public void ExecuteGherkin(string gherkin, string stepType, params object[] args) {
        Action action = new Action(DummyMethod);

        var currentSteps = new Dictionary<Reporter, Step>();

        var starttime = DateTime.Now;
        foreach (var reporter in Reporters.GetAll()) {
            currentSteps.Add(reporter, reporter.CurrentStep);

            var step = Reporters.CreateStep(starttime, action.Method, args);
            step.Title = gherkin + ".";

            // wird er an den aktuellen Step angehängt, so hat das Stepergebnis wiederum Stepergebnisse -> landet im json auf einer Ebene zu tief
            //var stepContainer = reporter.CurrentStep ?? reporter.CurrentScenarioBlock;
            var stepContainer = reporter.CurrentScenarioBlock;

            stepContainer.Steps.Add(step);
            reporter.CurrentStep = step;
            Reporters.OnStartedStep(reporter);
        }

        Exception actionException = null;
        try {
            // eigentliche Ausführung
            if (stepType == StepType.Given)
                MetaSteps.Given(gherkin);
            else if (stepType == StepType.When)
                MetaSteps.When(gherkin);
            else if (stepType == StepType.Then)
                MetaSteps.Then(gherkin);
        } catch (Exception ex) {
            if (ex is TargetInvocationException && ex.InnerException != null) {
                // Exceptions thrown by ReportingMessageSink are wrapped in a TargetInvocationException
                actionException = ex.InnerException;
            } else {
                actionException = ex;
            }
        } finally {
            var endtime = DateTime.Now;

            TestResult testResult;

            if (actionException is PendingStepException) {
                testResult = TestResult.Pending;
            } else if (actionException != null) {
                testResult = TestResult.Error;
            } else {
                testResult = TestResult.OK;
            }

            foreach (var reporter in Reporters.GetAll()) {
                reporter.CurrentStep.EndTime = endtime;
                reporter.CurrentStep.Result = testResult;
                reporter.CurrentStep.Exception = actionException.ToExceptionInfo();

                // finish step (take screenshot)
                reporter.CurrentStep.UserData = new {
                    Screenshot = TakeScreenshot()
                };

                reporter.CurrentStep = currentSteps[reporter];
            }
        }
    }

To avoid null reference errors (a different issue) you also have to hand through the Specflow hooks, that dont seem to work properly (also using the modified Specflow.Reporting lib):

    [BeforeScenarioBlock]
    public static void BeforeScenarioBlock() {
        SpecFlow.Reporting.Reporters.BeforeScenarioBlock();
    }

    [BeforeScenario]
    public static void BeforeScenario() {
        SpecFlow.Reporting.Reporters.BeforeScenario();
    }

    [BeforeFeature]
    public static void BeforeFeature() {
        SpecFlow.Reporting.Reporters.BeforeFeature();
    }

    [AfterScenarioBlock]
    public static void AfterScenarioBlock() {

etc etc - just do that for all

all the posted code is part of a class that all of my GlueCode classes inherit from

maybe this helps - works for me - feel free to ask

kerly commented 8 years ago

I attempted to copy the provided solution but some of the code was outdated. Instead I found another workaround that seems to work for me.

The premise is this:

  1. Create an implementation that inherits from the Steps class (ex. SubSteps : Steps)
  2. You will need to pass an instance of the ScenarioContainer into the SubSteps object to be able to initialize the Steps class with the container
  3. In your final steps class that inherits from ReportingStepDefinitions initialize an instance of the SubSteps class
  4. Call the instance of the SubSteps class to invoke steps

Code snippets below:

SubSteps Class

using BoDi;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Infrastructure;

namespace StepDefinitions
{
    public class SubSteps : Steps
    {
        public SubSteps(IObjectContainer objectContainer)
        {
            ((IContainerDependentObject)this).SetObjectContainer(objectContainer);
        }
    }
}

StepDefinition Class

using SpecResults;
using SpecResults.Json;
using TechTalk.SpecFlow;

namespace StepDefinitions
{
    [Binding]
    public class BaseStepDefinitions : ReportingStepDefinitions
    {
        protected SubSteps subSteps;

        // Initialize SubSteps class
        public BaseStepDefinitions()
        {
            subSteps = new SubSteps(ScenarioContext.Current.ScenarioContainer);
        }

        [Then(@"I login to the website")]
        public void ThenILoginToTheWebsite()
        {
            // Navigate browser
            subSteps.Then("I navigate the browser to http://google.com");
        }

        [Then(@"I navigate the browser to ([^\s]+)")]
        public void ThenINavigateTheBrowserTo(string url)
        {
            // Logic to navigate browser
            ...
        }
    }
}

Impressions

This solution seems to work for me, however when calling the subSteps the steps are not added to the report.