I am creating Selenium RC test scripts in Visual Studio (C#). I am
struggling with re-factoring the tests; all my tests are in a single
file. I would appreciate any input and/or pointers to websites, books,
etc. to learn about modularizing the tests.
I have to run the same tests on different sites (same application but
configured differently for different clients and logins) which are 95%
same. Would anybody like to provide some good examples or best
practices to do this?
Thanks!
Best practise for writing Selenium tests or any UI tests is Page Object Model which is the idea that you create an Object for each of the pages. Each of these objects abstract the page so when you write a test it doesnt really look like you have been working with Selenium.
So for a blog you would do something like this to create an object for the home page
public class Home
{
private readonly ISelenium _selenium;
/// <summary>
/// Instantiates a new Home Page object. Pass in the Selenium object created in the test SetUp().
/// When the object in instantiated it will navigate to the root
/// </summary>
/// <param name="selenium">Selenium Object created in the tests
public Home(ISelenium selenium)
{
this._selenium = selenium;
if (!selenium.GetTitle().Contains("home"))
{
selenium.Open("/");
}
}
/// <summary>
/// Navigates to Selenium Tutorials Page. Selenium object wll be passed through
/// </summary>
/// <returns>SeleniumTutorials representing the selenium_training.htm</returns>
public SeleniumTutorials ClickSelenium()
{
_selenium.Click("link=selenium");
_selenium.WaitForPageToLoad("30000");
return new SeleniumTutorials(_selenium);
}
/// <summary>
/// Click on the blog or blog year and then wait for the page to load
/// </summary>
/// <param name="year">blog or blog year
/// <returns>Object representing /blog.* pages</returns>
public Blog ClickBlogYear(string year)
{
_selenium.Click("link=" + year);
_selenium.WaitForPageToLoad("30000");
return new Blog(_selenium);
}
// Add more methods as you need them
}
then you would create a test that looks like the following
[TestFixture]
public class SiteTests
{
private ISelenium selenium;
[SetUp]
public void Setup()
{
selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://www.theautomatedtester.co.uk");
selenium.Start();
}
[TearDown]
public void Teardown()
{
selenium.Stop();
}
[Test]
public void ShouldLoadHomeThenGoToXpathTutorial()
{
Home home = new Home(selenium);
SeleniumTutorials seleniumTutorials = home.ClickSelenium();
SeleniumXPathTutorial seleniumXPathTutorial = seleniumTutorials.ClickXpathTutorial();
Assert.True(seleniumXPathTutorial.
IsInputOnScreen(SeleniumXPathTutorial.FirstInput));
Assert.True(seleniumXPathTutorial
.IsInputOnScreen(SeleniumXPathTutorial.SecondInput));
Assert.True(seleniumXPathTutorial
.IsInputOnScreen(SeleniumXPathTutorial.Total));
}
}
Related
We have a Silverlight application. It has a couple of pages that sit inside tabs in our UI. In the past, we've called them SavePage, and PanelPage. Save page just has basic functionality for editing the detail of a record, creating new records, and deleting the existing record on screen. PanelPage inherits from SavePage. PanelPage is a little more sophisticated in that panels become visible/invisible based on selections you make on screen.
The code was a huge mess in the Silverlight application. But, recently, I took the step of porting this code to work in Xamarin Forms. I made two failed attempts to do this, and on my third attempt, I got the code to work on all targeted platforms: Silverlight, iOS, Android, and Windows UWP. I'm fairly happy with the class design for now. It could be simpler, but this will do the trick for a while.
The point of this design is that the UI logic is abstracted away from the physical UI controls themselves. I removed the System.Windows usings away from the shared code that sits across both platforms (SavePage, and PanelPage). These pages are more like "Controllers" in the MVC, or MVVC patterns. But, I don't think that what I have created is exactly either of those two patterns. However, I had to split these classes up in to two parts: one for abstract UI calls like SaveAsync(), and one for platform specific UI calls like ReportError. I'm struggling to get the naming of the classes right since I don't even know what design pattern I am using.
Here is a class diagram:
Here is the code for some of the interfaces concerned:
public interface IPage : IRecordSelector
{
/// <summary>
/// This event should be raised when the busy state of a tab changes
/// </summary>
event EventHandler<BusyStateChangedEventArgs> BusyStateChanged;
object PageElement { get; }
}
public interface IButtonDrivenPage : IPage
{
Task SaveClickAsync();
Task DuplicateClickAsync();
Task DeleteClickAsync();
Task NewClickAsync();
event EventHandler<ButtonVisibilityChangedEventArgs> ButtonVisibilityChanged;
IRecord GetRecord();
}
public interface ISavePage : IButtonDrivenPage, IRequestClose
{
string DataContextXmlSnapshot { get; }
bool PromptForChangeCancel { get; }
IRecord SelectedItem { get; }
Task SetSelectedItemAsync(IRecord selectedItem);
event EventHandler SelectedItemChanged;
void Close();
void SetAutomationObject(object automationObject);
ISavePageUIController SavePageUIController { get; }
}
public interface ISavePageUIController: IDisposable
{
/// <summary>
/// The UI controller is notifying the page that the UI content has been loaded
/// </summary>
event EventHandler ContentLoaded;
/// <summary>
/// Prompt the user for a yet or a no
/// </summary>
Task<bool> GetYesNoFromPrompt(string message, string title);
/// <summary>
/// Report an error to the user
/// </summary>
void ReportError(string title, string message, Exception exception);
/// <summary>
/// Notifies the UI that the DataContext/Binding context has changed
/// </summary>
void SetSelectedItem(IRecord selectedItem);
/// <summary>
/// The actual UI object that is displayed on screen as the content of the page
/// </summary>
object PageElement { get; }
/// <summary>
/// Clears residual errors from the screen if they exist
/// </summary>
void ClearErrors();
/// <summary>
/// The record was saved. The selectedItem parameter will be the saved record from the server.
/// </summary>
void CurrentRecordSaved(IRecord selectedItem);
/// <summary>
/// This event occurs when the UI wants to notify the controller that a Save button has been clicked in the UI somewhere
/// </summary>
event EventHandler SaveClicked;
}
public interface IPanelUIController : ISavePageUIController
{
void CreateAndAddPanelFromContent(PagePanel pagePanel, double? panelHeight);
IEnumerable<IPagePanel> GetIPagePanelControls();
void SetHeader(IPanelHeader pageHeader);
void SetVisiblePanels(IList<bool> visiblePanels);
void HideAllPanels();
event EventHandler<RecordsSelectedRoutedEventArgs> PanelPageRecordsSelected;
}
These interfaces have been implemented successfully in Silverlight and Xamarin Forms. So, is this similar to another UI design pattern? Can anyone recommend improvements? Or, tell me what I'd need to do to convert this to a more standard UI design pattern? How about naming? What should I name my classes and interfaces here?
To be honest, I wouldn't be too obsessed with being one or the other (MVC, MVVM or MVP), they're pretty much the same and the point is to get keep that "big split". That said, right now, IMHO you seem to be closest to MVP (Model View Presenter)
The problem is that you have a lot of intermingled logic there: IPage should really just be a View but you have it doing things that a controller typically would. Same with it's children: ISavePagehas a method called SetAutomation object that I would normally expect to see in a Controller (at least if I'm guessing correctly on it's function).
Adam Freeman did a great job talking about how to break this stuff up in ASP.NET MVC 5: http://enos.itcollege.ee/~ijogi/Nooks/Pro%20ASP.NET%20MVC%205/Pro%20ASP.NET%20MVC%205.9781430265290.pdf Check out page 51 where he breaks down what each item should be conceptually, which may help?
I would try doing it this way, given that we've established that your IPage is really a Controller.
public class PageElement
{
IPageController _controller;
// these are your models - your controller will simply allow them to be shown with your other methods
private PageData _data;
private PageData _otherData;
public PageElement(IPageController ctrl)
{
_controller = ctrl;
}
}
public class PageController : IPageController
{
IPageService _service;
public PageController(IPageService service)
{
_service = service;
}
// this is what your button calls when clicked
public void SaveAsync(object sender, SomeEventArgs args)
{
// the service does the actual work
_service.SaveAsync()
}
}
public class PageService : IPageService
{
public void SaveAsync(){ // what it does}
}
Look like a MVC, or previous controller-view pattern (prior to MVC)
I am using an external service for metatagging my pages. I implemented circuit breaker as well as fallback for it and is working perfectly. Now I want to write unit tests for it. Any thoughts?
I am pasting some part of my code for your review:
Unity Registration
.RegisterType<ITaggerRepository>(new InjectionFactory(c => Framework.CrossCutter.ApplyAspectsTo((ITaggerRepository)c.Resolve<CachedTaggerRepository>(), "TaggerRepository").Configure<ISlaAspectConfiguration<ITaggerRepository>>(config => config.Fallback = new TaggerRepositoryFallback())
.Proxy));
Fallback implementation:
public class TaggerRepositoryFallback : ITaggerRepository
{
/// <summary>
/// Requests the tags.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Tagger response</returns>
public TaggerResponse RequestTags(TaggerRequest request)
{
return new TaggerResponse
{
Url = request.PageUrl,
first of all I'm new to SpecFlow.
I have a feature file which I have / want to automate using MSTest to run as a functional test involving a fully set up server, data access ...
For this purpose I have to configure the server with the data in the SpecFlow's 'Given' blocks and start it afterwards. I also have to copy some files to the test's output directory.
In the non-SpecFlow functional tests I was using the ClassInitialize attribute to get the TestDeploymentDir from the TestContext; something like this:
[ClassInitialize]
public static void ClassSetup(TestContext context)
{
TargetDataDeploymentRoot = context.TestDeploymentDir;
}
Now with SpecFlow I can't use this attribute anymore as it is used by SpecFlow itself.
Some new attributes do exist, like BeforeFeature which acts similarly BUT it doesn't pass on the TestContext as a parameter.
I just need to get access to the TestContext's TestDeploymentDir in order to copy some files there before really lauching my functional test server - easily doable without SpecFlow but almost impossible with SpecFlow.
How to deal with this issue?
Is it possible at all?
Thanks a lot for advice!
robert
Environment:
Visual Studio 2012
SpecFlow 1.9.0.77
Since SpecFlow 2.2.1 the TestContext is available via Context Injection. (https://github.com/techtalk/SpecFlow/pull/882)
You can get it from the container directly:
ScenarioContext.Current.ScenarioContainer.Resolve<Microsoft.VisualStudio.TestTools.UnitTesting.TestContext>()
or via context injection:
public class MyStepDefs
{
private readonly TestContext _testContext;
public MyStepDefs(TestContext testContext) // use it as ctor parameter
{
_testContext = testContext;
}
[BeforeScenario()]
public void BeforeScenario()
{
//now you can access the TestContext
}
}
In order to have access to values in the TestContext you have to create partial class for each scenario file you have in which you add the .
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TechTalk.SpecFlow;
/// <summary>
/// Partial class for TestContext support.
/// </summary>
public partial class DistributionFeature
{
/// <summary>
/// Test execution context.
/// </summary>
private TestContext testContext;
/// <summary>
/// Gets or sets test execution context.
/// </summary>
public TestContext TestContext
{
get
{
return this.testContext;
}
set
{
this.testContext = value;
//see https://github.com/techtalk/SpecFlow/issues/96
this.TestInitialize();
FeatureContext.Current["TestContext"] = value;
}
}
}
Then you could access the deployment directory from your steps using
var testContext = (TestContext)FeatureContext.Current["TestContext"];
var deploymentDir = testContext.TestDeploymentDir;
If you have too many scenarios, then you probably has to automate creation of such files with T4.
You can create a Plugin and customize the IUnitTestGeneratorProvider implementation. The following should add the line to MSTest's class initialize.
// It's very important this is named Generator.SpecflowPlugin.
namespace MyGenerator.Generator.SpecflowPlugin
{
public class MyGeneratorProvider : MsTest2010GeneratorProvider
{
public MyGeneratorProvider(CodeDomHelper codeDomHelper)
: base(codeDomHelper)
{
}
public override void SetTestClassInitializeMethod(TestClassGenerationContext generationContext)
{
base.SetTestClassInitializeMethod(generationContext);
generationContext.TestClassInitializeMethod.Statements.Add(new CodeSnippetStatement(
#"TargetDataDeploymentRoot = context.TestDeploymentDir;"));
}
}
[assembly: GeneratorPlugin(typeof(MyGeneratorPlugin))]
public class MyGeneratorPlugin : IGeneratorPlugin
{
public void RegisterDependencies(ObjectContainer container)
{
}
public void RegisterCustomizations(ObjectContainer container, SpecFlowProjectConfiguration generatorConfiguration)
{
container.RegisterTypeAs<MyGeneratorProvider, IUnitTestGeneratorProvider>();
}
public void RegisterConfigurationDefaults(SpecFlowProjectConfiguration specFlowConfiguration)
{
}
}
}
And reference it in the App.config file:
<specFlow>
<plugins>
<add name="MyGenerator" type="Generator"/>
</plugins>
</specFlow>
Next time you re-save the .feature files the generated code in ClassInitialize should set the TargetDataDeploymentDirectory.
I had to do something similar. Here's my working code https://github.com/marksl/Specflow-MsTest and blog post http://codealoc.wordpress.com/2013/09/30/bdding-with-specflow/
There is a FeatureContext as well as the more commonly used
ScenarioContext. The difference of course is that the FeatureContext
exists during the execution of the complete feature while the
ScenarioContext only exists during a scenario.
For example:
Add to context:
ScenarioContext.Current.Add("ObjectName", myObject);
Get:
var myObject = ScenarioContext.Current.Get<object>("ObjectName");
You can read more about it here.
using Deployment in test configuration makes a copy every time the unit test is run which is time consuming. The data is a bunch of bitmaps that might change only after builds.
What's the convention for deploying such large test data?
I just wrote up a blog post about File Dependencies and testing here.
http://tsells.wordpress.com/2012/03/06/how-to-run-integration-tests-with-file-dependencies/
The tests you are performing are integration tests since you are going to the file system. I would use the example per class method in the post for what you are trying to achieve.
Contents of Post
Disclaimer
In many cases a developer test needs to execute with a specific file / set of files that need to be on the file system. Many “purists” think this is a bad idea and you should “Mock” your system out in such a way that this is not required. In certain cases that may be true – but I am a “realist” and understand that the complexity requirements around doing things like this can and many times do far outweigh the benefits from just leveraging the file system for the test. This does however move the test from being a true “unit” test to an “integration” test. I am content with this as it is also my belief that integration tests provide more value than unit tests. If setup and run in the correct way – this test can run locally in visual studio, via MS build and the command line, or on a build server running build agents like Team City.
You can download the source code here: TestClass.zip
Requirements for this Test
Test Runner ( I like using TestDriven.Net as it lets me execute in line as well as debug).
A file class that wraps some of the system.IO functions that allows you implement IDisposable. This is useful for creating a “sandbox” that once is out of scope – and it removes any temp files that are used so clean up is done automatically (an example class is attached in the sample).
NUnit or MSTest. I still prefer NUnit.
Usage Options
The requirements for the usage of the test files will determine how and when to set them up and clean up (delete).
Per Test – files are regenerated per test run against them
Per Test Class – files are generated once per test class
In both instances the FileSandBox class is used to create a temporary location for the files to live and then be removed once the test(s) are complete.
Per Class Usage
[TestFixture]
public class PerClass
{
private FileSandbox _sandbox;
private string _tempFileLocation;
public PerClass() {}
/// <summary>
/// Setup class - runs once per class
/// </summary>
[TestFixtureSetUp]
public void SetupClass()
{
_sandbox = new FileSandbox();
// Getting Temp file name to use
_tempFileLocation = _sandbox.GetTempFileName("txt");
// Get the current executing assembly (in this case it's the test dll)
Assembly myassembly = Assembly.GetExecutingAssembly();
// Get the stream (embedded resource) - be sure to wrap in a using block
using (Stream stream = myassembly.GetManifestResourceStream("TestClass.TestFiles.TextFile1.txt"))
{
// In this case using an external method to write the stream to the file system
_tempFileLocation = TestHelper.StreamToFile(stream, _tempFileLocation);
}
}
/// <summary>
/// Tear down class (cleanup)
/// </summary>
[TestFixtureTearDown]
public void TearDownClass()
{
_sandbox.Dispose();
}
[Test, Description("Testing doing something with files on the filesystem")]
public void MyFileSystemTest()
{
string[] lines = File.ReadAllLines(_tempFileLocation);
Assert.IsTrue(lines.Length > 0);
}
}
Per Test Usage (Option 1)
[TestFixture]
public class PerTest
{
public PerTest(){}
/// <summary>
/// Setup class - runs once per class
/// </summary>
[TestFixtureSetUp]
public void SetupClass()
{
// NOOP
}
/// <summary>
/// Tear down class (cleanup)
/// </summary>
[TestFixtureTearDown]
public void TearDownClass()
{
// NOOP
}
[Test, Description("Testing doing something with files on the filesystem")]
public void MyFileSystemTest()
{
using (FileSandbox sandbox = new FileSandbox())
{
// Getting Temp file name to use
string tempfile = sandbox.GetTempFileName("txt");
// Get the current executing assembly (in this case it's the test dll)
Assembly myassembly = Assembly.GetExecutingAssembly();
// Get the stream (embedded resource) - be sure to wrap in a using block
using (Stream stream = myassembly.GetManifestResourceStream("TestClass.TestFiles.TextFile1.txt"))
{
// In this case using an external method to write the stream to the file system
tempfile = TestHelper.StreamToFile(stream, tempfile);
string[] lines = File.ReadAllLines(tempfile);
Assert.IsTrue(lines.Length > 0);
}
}
}
}
Per Test Usage (Option 2)
[TestFixture]
public class PerEachTest
{
private FileSandbox _sandbox;
private string _tempFileLocation;
public PerEachTest() { }
/// <summary>
/// Setup class - runs once per class
/// </summary>
[TestFixtureSetUp]
public void SetupClass()
{
// NOOP
}
/// <summary>
/// Tear down class (cleanup)
/// </summary>
[TestFixtureTearDown]
public void TearDownClass()
{
// NOOP
}
[SetUp]
public void Setup()
{
_sandbox = new FileSandbox();
// Getting Temp file name to use
_tempFileLocation = _sandbox.GetTempFileName("txt");
// Get the current executing assembly (in this case it's the test dll)
Assembly myassembly = Assembly.GetExecutingAssembly();
// Get the stream (embedded resource) - be sure to wrap in a using block
using (Stream stream = myassembly.GetManifestResourceStream("TestClass.TestFiles.TextFile1.txt"))
{
// In this case using an external method to write the stream to the file system
_tempFileLocation = TestHelper.StreamToFile(stream, _tempFileLocation);
}
}
[TearDown]
public void Teardown()
{
_sandbox.Dispose();
}
[Test, Description("Testing doing something with files on the filesystem")]
public void MyFileSystemTest()
{
string[] lines = File.ReadAllLines(_tempFileLocation);
Assert.IsTrue(lines.Length > 0);
}
}
You can download the source code here: Source Code
I have set up some In Memory SQLite Unit Tests for my Fluent NHibernate Database, which looks like this. It works fine. (Using NUnit)
namespace Testing.Database {
/// <summary>
/// Represents a memory only database that does not persist beyond the immediate
/// testing usage, using <see cref="System.Data.SQLite"/>.
/// </summary>
public abstract class InMemoryDatabase : IDisposable {
/// <summary>
/// The configuration of the memorized database.
/// </summary>
private static Configuration Configuration { get; set; }
/// <summary>
/// The singleton session factory.
/// </summary>
protected static ISessionFactory SessionFactory { get; set; }
/// <summary>
/// The current session being used.
/// </summary>
protected ISession Session { get; set; }
protected InMemoryDatabase() {
SessionFactory = CreateSessionFactory();
Session = SessionFactory.OpenSession();
BuildSchema(Session);
}
/// <summary>
/// Construct a memory based session factory.
/// </summary>
/// <returns>
/// The session factory in an SQLite Memory Database.
/// </returns>
private static ISessionFactory CreateSessionFactory() {
return FluentNHibernate.Cfg.Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration
.Standard
.InMemory()
.ShowSql())
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<Data.Mappings.AspectMap>())
.ExposeConfiguration(configuration => Configuration = configuration)
.BuildSessionFactory();
}
/// <summary>
/// Builds the NHibernate Schema so that it can be mapped to the SessionFactory.
/// </summary>
/// <param name="Session">
/// The <see cref="NHibernate.ISession"/> to build a schema into.
/// </param>
private static void BuildSchema(ISession Session) {
var export = new NHibernate.Tool.hbm2ddl.SchemaExport(Configuration);
export.Execute(true, true, false, Session.Connection, null);
}
/// <summary>
/// Dispose of the session and released resources.
/// </summary>
public void Dispose() {
Session.Dispose();
}
}
}
So now, in order to use it, I just inherit InMemoryDatabase and add my Test methods, like this.
[TestFixture]
public class PersistenceTests : InMemoryDatabase {
[Test]
public void Save_Member() {
var member = // ...;
Session.Save(member); // not really how it looks, but you get the idea...
}
}
My problem isn't that this doesn't work. It does. But if I have two tests in the same class that test similar data, for instance ...
Username_Is_Unique() and then Email_Is_Unique(). Not real tests again, but it's a good example.
[Test]
public void Username_Is_Unique(){
var user = new User {
Name = "uniqueName"
Email = "uniqueEmail"
};
// do some testing here...
}
[Test]
public void Email_Is_Unique(){
var user = new User {
Name = "uniqueName"
Email = "uniqueEmail"
};
// do some testing here...
}
I realize these are very bad tests. These are not real tests, I am just citing an example.
In both cases, I would construct a mock User or Member or what-have you and submit it to the database.
The first one works fine, but since the database is in memory (which makes sense, since I told it to be), the second one doesn't. Effectively, the Unit Tests do not reflect real-world situations, because each test stands alone. But when running them sequentially in a batch, it behaves like it should in the real world (I suppose that's partially a good thing)
What I want to do is flush the in memory database after each method. So I came up with a simple way to do this by repeating the constructor. This goes in the InMemoryDatabase class.
protected void Restart() {
SessionFactory = CreateSessionFactory();
Session = SessionFactory.OpenSession();
BuildSchema(Session);
}
So now, in each method in my inheriting class, I call Restart() before I do my testing.
I feel like this isn't the intended, or efficient way to solve my problem. Can anyone propose a better solution?
If it is of any relevance, I am using Fluent nHibernate for the persistence, and Telerik JustMock for my Mocking - but for my database stuff, I've yet to need any mocking.
You need to drop and recreate the database for every test. Every test should be independent of the other. You can do do two thing, first have your test use a setup method (Assuming NUnit here but others have the same functionality)
[SetUp]
public void Setup()
{
// Create in memory database
Memdb = new InMemoryDatabase();
}
Alternatively, you can wrap each test in a using statement for the database. For example
[Test]
public void Test()
{
using(var db = new InMemmoryDatabase())
{
Do Some Testing Here
}
}