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
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.
Cody
Telerik
Test Studio Trainings
Thanks!
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.
Cody
Telerik
Test Studio Trainings
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"
);
}
}
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
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
Test Studio Trainings