This is a migrated thread and some comments may be shown as answers.

How to use Specflow with Telerik Testing Framework, We have Silverlight Applicaion and Want to Test End to End Test Automation?

6 Answers 366 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Nitin
Top achievements
Rank 1
Nitin asked on 05 Sep 2013, 06:43 AM
How to use Specflow with Telerik Testing Framework,
We have Silverlight Application and Want to Test End to End Test Automation?

I have following:-
Telerik Testing Framework.
Silverlight Application running on Browser.
Spec flow.
VS 2012 and NUnit framework.

Could you please provide example or Jing video is really helpful.

Thanks


6 Answers, 1 is accepted

Sort by
0
Cody
Telerik team
answered on 09 Sep 2013, 05:02 PM
Hi Nitin,

We haven't worked with SpecFlow before and wanted to put together a sample project demonstrating how you can use our Telerik Testing Framework with SpecFlow. Unfortunately we don't have any online documentation on it. In theory it should follow the same basic methodology as using Telerik Testing Framework along with NUnit.

Regards,
Cody
Telerik
Quickly become an expert in Test Studio, check out our new training sessions!
Test Studio Trainings
0
Chunks
Top achievements
Rank 1
answered on 10 Sep 2013, 09:38 PM
We are also trying to implement specflow capability to write BDT acceptance tests in our existing telerik framework , I believe ensuring the SpecFlow hooks can use the tererlik test framework API is needed and If you want to use any of the SpecFlow Hooks as steps such as [BeforeTestRun],[BeforeFeature], [BeforeScenario], [AfterTestRun], [AfterFeature] or [AfterScenario], unless you add specific code you will receive an error: with browser which I run into currently. If anybody has done this, I will appreciate some input.

Thanks!
0
Cody
Telerik team
answered on 12 Sep 2013, 09:48 PM
Hi Aakash,

Since we, Telerik, haven't worked with SpecFlow we're unable to give you any guidance. Can you give more details on "unless you add specific code you will receive an error: with browser which I run into currently"? Please share with us what this error is. Am I correct in assuming you added some code to overcome this? Would you share with us this code?

Once I have this information, I can log a problem report that we can look into. I can't promise any ETA since this is a third party tool. It's a feature request at this point.

Regards,
Cody
Telerik
Quickly become an expert in Test Studio, check out our new training sessions!
Test Studio Trainings
0
Che
Top achievements
Rank 1
answered on 20 Dec 2013, 06:16 PM

Hi,
I haven't gotten everything working together to the degree I want yet, but I've solved some of the initial issues I found. 

My goals were:

-    Permit having more than one class defining the SpecFlow steps to keep code organized

-    Share steps wherever useful without rewriting them in multiple step implementation classes or making an artificial inheritance structure for sharing

SpecFlow doesn't really organize step implementations.  All steps marked with attributes are put into a big pool that it searches for the implementation of the step.  That means you cannot have the same step name defined in multiple classes, and that you can mix steps defined in multiple classes in a Specification.  Because of that you want to avoid instance or class variables between steps, but use the SpecFlow context to store any data that needs to be passed between steps.

WebAii is easiest to use when inheriting from the BaseTest class.  The problem is that the WebAii objects like manager and browser are instance variables and will not be shared between different classes defining steps.  To get around this, I made my own base test class inheriting from BaseTest, and making my own versions of the WebAii objects that retrieve the object from the SpecFlow context if available, otherwise go to the base (and make sure the context is set with the base version).  This lets the first BaseTest subclass instance set up the objects, and the other steps use the browser, manager, etc that are already created.

Here's an example of what I'm talking about, with a couple helpers I found useful. I have several step definition classes descended from my base that have implementations of steps that use the browser, etc.  I separated some concerns in a data test support class, using SpecFlow context injection as well, but that could be taken further.  It does show how you can use some SpecFlow hooks carefully to clean up data.

/// <summary>
/// Superclass browser test to handle container/dataItem setup and provide some common features for browser tests.
/// Because to use WebAii we need to inherit from BaseTest, and each step file is a different class, we have an issue
/// sharing the BaseTest properties.  If the first step is in one step class, and the second is in another step class,
/// the second one will not be hooked up to the browser automation.  For that, use the WA... properties
/// We use context injection in SpecFlow to get the session and cleanuplist.
/// </summary>
[Binding]
public class BaseBrowserDataTest : BaseTest
{
    // For additional details on SpecFlow step definitions see http://go.specflow.org/doc-stepdef
 
    // For storing and retrieving scenario-specific data see http://go.specflow.org/doc-sharingdata
    // To use the multiline text or the table argument of the scenario,
    // additional string/Table parameters can be defined on the step definition
    // method.
    public BaseBrowserDataTest() {
        // can set up other base data here
    }
    public BaseBrowserDataTest(NHibernate.ISession session, List<Helpers.DataTestSupport.cleanup> cleanupList, ILogger log)
        : this()
    {
        this.session = session;
        this.TestLog = log;
        this.cleanupList = cleanupList;
    }
    protected virtual NHibernate.ISession session { get; set; }
    protected virtual List<Helpers.DataTestSupport.cleanup> cleanupList { get; set; }
    protected virtual ILogger TestLog { get; set; }
 
    /// expose some of the WebAii objects to subclasses so that steps from multiple classes can reference the browser
    protected Log WALog { get { return WAManager.Log; } }
    protected Manager WAManager { get { return Manager.Current; } }
    protected Browser WAActiveBrowser { get { return WAManager.ActiveBrowser; } }
 
    [AfterScenario]
    public void RecycleTheBrowser()
    {
        if (this.Manager != null)
        {
            new Helpers.EMCertAppHelper(this.Manager).EnsureLoggedOut();
            Helpers.EMCertAppHelper.Reset();
            CleanUp(); // close the browser
        }
    }
     
    public string UserName
    {
        get { return (string)ScenarioContext.Current["username"]; }
        set { ScenarioContext.Current["username"] = value; }
    }
 
    public void SnapshotError(string name)
    {
        WALog.CaptureBrowser(WAActiveBrowser, name);
    }
 
 
    /// <summary>
    /// Initialize and launch the browser for the current settings.
    /// </summary>
    protected void LaunchBrowser()
    {
        if (Manager==null)
            Initialize(false);
        try
        {
            try
            {
                if (Manager.ActiveBrowser != null)
                    Manager.ActiveBrowser.Close();
            }
            catch (Exception)
            {}
            Manager.LaunchNewBrowser(SpecBrowserSettings.Type);
        }
        catch (System.TimeoutException)
        {
            // try again
            Manager.LaunchNewBrowser(SpecBrowserSettings.Type);
        }
    }
    protected void EnsureBrowser()
    {
        if (WAManager == null || WAActiveBrowser == null)
        {
            LaunchBrowser();
        }
    }
    protected void AssertBrowserContainsText(string msg)
    {
        try
        {
            Assert.IsTrue(WAActiveBrowser.ContainsText(msg.Trim()), @"Should contain text '"+msg+@"'.  Should be a snapshot in C:\WebAiiLog\");
            Assert.False(WAActiveBrowser.ContainsText("Server Error in"), @"Server Error.  Should be a snapshot in C:\WebAiiLog\"); // in case the stack trace include the message when an error occurs
        }
        catch (Exception)
        {
            SnapshotError("Should contain text " + msg);
            throw;
        }
    }
    protected void AssertBrowserDoesNotContainText(string msg)
    {
        try
        {
            Assert.IsFalse(WAActiveBrowser.ContainsText(msg), @"Should not contain text '"+msg+@"'.  Should be a snapshot in C:\WebAiiLog\");
            Assert.False(WAActiveBrowser.ContainsText("Server Error in"), @"Server Error.  Should be a snapshot in C:\WebAiiLog\"); // in case the stack trace include the message when an error occurs
        }
        catch (Exception)
        {
            SnapshotError("Should not contain text " + msg);
            throw;
        }
    }
 
    /// <summary>
    /// Assert that we have a partial match on the passed in url
    /// </summary>
    /// <param name="partialUrl"></param>
    public void AssertOnPage(string partialUrl)
    {
        Assert.IsTrue(WAActiveBrowser.Url.Contains(partialUrl), "User " + UserName + " should be on page " + partialUrl);
    }
 
    /// <summary>
    /// Wait for AJAX to complete
    /// </summary>
    public void AjaxWait()
    {
        System.Threading.Thread.Sleep(250); // let server catchup and finish save before we move onto retrieving data in a later step
        WAManager.Wait.For<int>(c => ActiveAjaxConnections() == 0, 0, 30000);
        System.Threading.Thread.Sleep(250); // let server catchup and finish save before we move onto retrieving data in a later step
    }
 
    public int ActiveAjaxConnections()
    {
        return WAActiveBrowser.Actions.InvokeScript<int>("jQuery.active");
    }
}
 
 
/// <summary>
/// Use Context Injection https://github.com/techtalk/SpecFlow/wiki/Context-Injection to set up database and container access
/// for any step definition classes that need it.  They specify what they need from the container in their constructor.
/// </summary>
[Binding]
public class DataTestSupport
{
    private readonly IObjectContainer objectContainer;
    private bool isInitialized;
    public delegate void cleanup();
 
    public DataTestSupport(IObjectContainer objectContainer)
    {
        this.objectContainer = objectContainer;
        isInitialized = false;
    }
 
    [BeforeScenario]
    public void Initialize()
    {
        if (!isInitialized)
        {
            List<cleanup> cleanupList = new List<cleanup>();
            objectContainer.RegisterInstanceAs<List<cleanup>>(cleanupList);
 
            Castle.Windsor.IWindsorContainer container = DataLayerTest.Initialize(true, false);
            objectContainer.RegisterInstanceAs<Castle.Windsor.IWindsorContainer>(container);
 
            NHibernate.ISession session = container.Resolve<NHibernate.ISession>();
            objectContainer.RegisterInstanceAs<NHibernate.ISession>(session);
 
            ILogger log = container.Resolve<ILogger>();
            objectContainer.RegisterInstanceAs<ILogger>(log);
        }
        isInitialized = true;
    }
 
    [BeforeScenario]
    public void StartScenario()
    {
        /// Keep track of when the test started so we can detect certain changes that occurred during this test.
        if (!ScenarioContext.Current.Keys.Contains("started") || ScenarioContext.Current["started"] == null)
            ScenarioContext.Current["started"] = DateTime.Now;
    }
 
    [BeforeScenario("LLSA")]
    public void SetupLLSACME()
    {
        Initialize();
        var session = objectContainer.Resolve<ISession>();
        ABEM.CertAppOnline.Spec.MOC.LLSA_CME_Registration_Steps.CleanupAllTestCourses(session);
    }
 
    /// <summary>
    /// Execute any anonymous functions registered during tests to let us cleanup dataItem.
    /// Call from TearDown or AfterScenario method.
    /// </summary>
    [AfterScenario]
    public void CleanupData()
    {
        try
        {
            List<cleanup> cleanupList = objectContainer.Resolve<List<cleanup>>();
            if (cleanupList != null)
                foreach (cleanup fn in cleanupList)
                {
                    try
                    {
                        fn.Invoke();
                    }
                    catch (Exception ex)
                    {
                        ILogger log = objectContainer.Resolve<ILogger>();
                        log.Error("Error in data cleanup", ex);
                    }
                }
        }
        catch (Exception e)
        {
            ILogger log = objectContainer.Resolve<ILogger>();
            if(log!=null)
                log.Error("Error in data cleanup", e);
        }
    }
 
    /// <summary>
    /// If we failed a browser test, take a page snapshot of the page after we failed so we can figure out what happened.
    /// </summary>
    [AfterStep("browser")]
    public static void RecordFailures()
    {
        if (ScenarioContext.Current.TestError != null && Manager.Current != null)
        {
            try
            {
                Console.WriteLine(@"Recording failure for " + TestContext.CurrentContext.Test.Name + @"- check C:\WebAiiLog\ for snapshot");
                Manager.Current.ActiveBrowser.Window.Maximize();
                Manager.Current.Log.CaptureBrowser(Manager.Current.ActiveBrowser, MakeValidFileName(TestContext.CurrentContext.Test.Name));
                Manager.Current.ActiveBrowser.Window.Restore();
            }
            catch (Exception e)
            {
                Console.WriteLine("error occurred getting screenshot for " + TestContext.CurrentContext.Test.Name + ": " + e.Message);
                Console.WriteLine(e.StackTrace);
            }
        }
    }
    public static string MakeValidFileName(string name)
    {
        var builder = new System.Text.StringBuilder();
        var invalid = System.IO.Path.GetInvalidFileNameChars();
        foreach (var cur in name)
        {
            if (!invalid.Contains(cur))
            {
                builder.Append(cur);
            }
        }
        return builder.ToString();
    }
}
    

And a simple example subclass:

public class LoginAuthenticationSteps : Helpers.BaseBrowserDataTest
{
        IWindsorContainer container;
        public LoginAuthenticationSteps(IWindsorContainer container)
        {
            this.container = container;
        }
        [Given(@"I am on the login page")]
        public void GivenIAmOnTheLoginPage()
        {
        EnsureBrowser();
        WAActiveBrowser.NavigateTo(@"online/Security/Index");
        }
}


0
George
Top achievements
Rank 1
answered on 04 Mar 2016, 09:58 AM

Hi Cody, Can you provide a brief tutorial on how to implement specflow on Telerik framework, anytime I installed the specflow nuget or add a feature file to my project it stops working until the feature file is remove or the nuget package is removed vice verse. 

I will appreciate if you can demonstrate how to implement  specflow on a telerik framework. 

Thanks

 

0
Boyan Boev
Telerik team
answered on 09 Mar 2016, 09:38 AM
Hi George,

As Cody mentioned bellow we, Telerik, haven't worked with SpecFlow we're unable to give you any guidance.

Could you please give us more detailed information no your scenario so we can try to help you out.

Thank you!

Regards,
Boyan Boev
Telerik
 
Quickly become an expert in Test Studio, check out our new training sessions!
Test Studio Trainings
 
Tags
General Discussions
Asked by
Nitin
Top achievements
Rank 1
Answers by
Cody
Telerik team
Chunks
Top achievements
Rank 1
Che
Top achievements
Rank 1
George
Top achievements
Rank 1
Boyan Boev
Telerik team
Share this question
or