SpecFlow: ClassInitialize and TestContext - c#

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.

Related

OneTimeSetUp: No suitable constructor was found when using nunit and autofac

I'm trying to get nunit and autofac working together for a Selenium test framework.
I understand why I'm getting the OneTimeSetUp: No suitable constructor was found error (because nunit cannot start UnitTest1 if it has a ctor which isn't empty), but I can't figure out how I can work around this.
It seems like a chicken and egg problem; nunit requires IHomePage for the test to run, but the container isn't created because SetUp or OneTimeSetUp aren't called until the test has started running.
[TestFixture]
public class UnitTest1
{
IHomePage _homePage;
private static IContainer Container { get; set; }
[SetUp]
public void SetUp()
{
var builder = new ContainerBuilder();
builder.RegisterType<HomePage>().As<IHomePage>();
builder.RegisterType<LoginPage>().As<ILoginPage>();
Container = builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
var writer = scope.Resolve<ITestRunner>();
writer.RunTest();
}
}
public UnitTest1(IHomePage homePage)
{
_homePage = homePage;
}
[Test]
public void TestMethod11()
{
// do something testing with _homePage
_homePage.ClickLogin();
}
}
public class HomePage : IHomePage
{
ILoginPage _loginPage;
public HomePage(ILoginPage loginPage)
{
_loginPage = loginPage;
}
public ILoginPage ClickLogin()
{
return _loginPage;
}
}
This will only be a partial answer, 'cause I know nothing about Autofac.
BUT from the NUnit point of view...
NUnit constructs your classes. To do that it needs either
A default constructor, OR
A non-default constructor for which you have supplied arguments.
You have a non-default constructor, but you are not telling NUnit what args to use with that constructor.
The way you tell NUnit what args to use is to supply them as arguments to the
TestFixtureAttribute or by using a TestFixtureSourceAttribute, which adds a level of indirection. Perhaps somebody else can add how Autofac interacts with NUnit in this situation.

xUnit class constructor should get arguments passed from theory test methods

On the xUnit website it says the following about constructor:
xUnit.net creates a new instance of the test class for every test that
is run, so any code which is placed into the constructor of the test
class will be run for every single test. This makes the constructor a
convenient place to put reusable context setup code where you want to
share the code without sharing object instances (meaning, you get a
clean copy of the context object(s) for every test that is run).
I have the following code:
public class ProfilePageTest
{
public ProfilePageTest(Role role)
{
AuthRepository.Login(role)
}
[Theory]
[Roles(Role.Editor, Role.Viewer)]
public void OpenProfilePageTest(Role role)
{
var profile = GetPage<ProfilePage>();
profile.GoTo();
profile.IsAt();
}
}
Is it possible to pass the role from the theory attribute to the constructor, so I don't have to do AuthRepository.Login(role) in every test method that I have.
No, that's not possible. The constructor will be run before anything else, as with any constructor you're used to. I don't see the harm in calling AuthRepository.Login(role) in every test though, because it's a single line of code.
This is quite an excellent blog post about the different ways you can pass data into xUnit tests, but all of them are passing in data to individual methods (tests) rather than in the constructor.
If you are looking to set something up for multiple tests you should have a look int IClassFixture
Quick run down, you setup a class with the shared data:
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
Db = new SqlConnection("MyConnectionString");
// ... initialize data in the test database ...
}
public void Dispose()
{
// ... clean up test data from the database ...
}
public SqlConnection Db { get; private set; }
}
And then in your tests you can "inject" the class (along with the data) into the test class:
public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
DatabaseFixture fixture;
public MyDatabaseTests(DatabaseFixture fixture)
{
this.fixture = fixture;
}
// ... write tests, using fixture.Db to get access to the SQL Server ...
}

Need simple example of FitNesse SetUpFixture under .NET

I've been trying to implement FitNesse tests for our C# application. I'm using the latest FitNesse jar and the fitSharp .NET 2.2 4.0 libraries.
I have been able to successfully use scenarios using Slim. However, in the interests of making tests that are more legible to a non-technical audience, I'm interested in implementing DoFixtures, and I surmise I'll need SetUpFixtures to create common data conditions for tests.
I can't seem to get the data I set up into the DoFixture instance, though. I've looked at several resources (Gojko's book chief among them), but none seem to talk about this concept in isolation. Attaching Visual Studio as a debugger to test runs hasn't yielded any insight. I could really use a trivial example to analyze and build upon.
Would someone be willing to share an example that includes just:
A SetUpFixture class that sets up some data
A DoFixture class that uses the data from the SetUpFixture
Wiki syntax to invoke them?
Looks like SetUp was not called. Use Debugger.Launch() for debug
Update:
!|BirdCall|
|birdName|noise|
|duck|quack|
public class SkylarkBunting : fitlibrary.DoFixture
{
public BirdCall BirdCall;
public SkylarkBunting(BirdCall birdCall)
{
BirdCall = birdCall;
}
public string GetCall()
{
return BirdCall.Noise;
}
public string GetName()
{
return BirdCall.BirdName;
}
}
I've got a partial answer.
Using these classes:
public class BirdCall : fitlibrary.SetUpFixture
{
public void BirdNameNoise(string birdName, string noise)
{
BirdName = birdName;
Noise = noise;
}
public string BirdName;
public string Noise;
public override string ToString()
{
return string.Format("BirdCall: {0}, {1}", BirdName, Noise);
}
}
public class SkylarkBunting : fitlibrary.DoFixture
{
public BirdCall BirdCall;
public string Call;
public Fixture CryOut()
{
BirdCall = new BirdCall();
Call = BirdCall.Noise;
return BirdCall;
}
public string GetCall()
{
return BirdCall.Noise;
}
public string GetName()
{
return BirdCall.BirdName;
}
}
and this Wiki markup:
!define TEST_RUNNER {C:\temp\fitsharp\Runner.exe}
!define COMMAND_PATTERN {%m -r fitnesse.fitserver.FitServer,c:\temp\fitsharp\fit.dll %p}
!path C:\temp\FitNesseIntegration\bin\Debug\FitnesseIntegration.dll
!|import|
|FitnesseIntegration|
!|FitNesseIntegration.Skylark.SkylarkBunting|
!3 Testing getting data from a setup fixture into a do fixture
!|cry out|
|bird name|noise|
|Tweetie|Caw!|
|check|call||
|check|get call||
|check|get name||
|check|bird call||
I get:
|check|call|null|
|check|get call|Caw!|
|check|get name|Tweetie|
|check|bird call|BirdCall: Tweetie, Caw!|
This is good, I have variables in the DoFixture that were populated by executing the SetUpFixture with values injected by FitNesse via the page.
I'd still like to know why Call returns null, though.

Where to deploy large data for unit tests?

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

How to write output in the [ClassInitialize()] of a Unit Test class?

I am writing some unit tests for the persistence layer of my C#.NET application. Before and after the tests of a test class execute, I want to do some cleaning up to erase possibly inserted dummy values, therefore, this cleaning up happens in methods marked with the attributes [ClassInitialize()] and [ClassCleanup()].
(I know that a better way would be to use an in-memory database, but it is not really doable so far as we depend on lots of stored procs....)
I would like to output some information about the results of the cleaning up, but I can not find a way to get the output in the test results with VISUAL Studio 2010.
This is what I am doing so far :
///... lots of stuff before ...
//global for the test run
private static TestContext context;
//for each test
private IRepository repo;
#region Initialisation and cleanup
/// <summary>
/// Execute once before the test-suite
/// </summary>
[ClassInitialize()]
public static void InitTestSuite(TestContext testContext)
{
context = testContext;
removeTestDataFromDb();
}
[ClassCleanup()]
public static void CleanupTestSuite()
{
removeTestDataFromDb();
}
private static void removeTestDataFromDb()
{
context.WriteLine("removeTestDataFromDb starting");
using (ISession session = NHibernateHelper.OpenSession())
{
IDbConnection cn = session.Connection;
IDbCommand cmd = cn.CreateCommand();
//remove anyt test data
cmd.CommandText = #"DELETE FROM SomeTable
WHERE somefield LIKE 'easyToFindTestData%Test'";
int res = cmd.ExecuteNonQuery();
context.WriteLine("removeTestDataFromDb done - affected {0} rows", res);
}
}
[TestInitialize()]
public void InitTest()
{
repo = new MyRepositoryImplementation();
}
[TestCleanup()]
public void CleanupTest()
{
//cleanup
repo = null;
}
#endregion
I'm trying to use context.WriteLine() ...
I also tried just using Console.WriteLine() with the same results.
How do you write to standard output in the ClassInitialize part and where can you access that output ?
The [ClassInitialize] and [ClassCleanup] run just once for all the tests in that class. You'd be better of using [TestInitialize] and [TestCleanUp] which run before and after each test. Also try wrapping the complete test in a database transaction. This way you can simply rollback the operation (by not committing the transaction) and your database stays in a consistent state (which is essential for trustworthy automated tests).
A trick I do for integration tests is to define a base class that all my integration test classes can inherit from. The base class ensures that each test is ran in a transaction and that this transaction is rolled back. Here is the code:
public abstract class IntegrationTestBase
{
private TransactionScope scope;
[TestInitialize]
public void TestInitialize()
{
scope = new TransactionScope();
}
[TestCleanup]
public void TestCleanup()
{
scope.Dispose();
}
}
Good luck.
The trace output from a ClassInitialize and ClassCleanup appears in the result summary.
You can access it by doing the following
Open the Test Results windw [ Test -> Windows -> Test Results ]
There should be a link named "Test run completed" on the top left corner of the [Test Results] window.
Click the clink
It should open a window with the header "Result Summary" and it will show the debug trace created during ClassInitialize and ClassCleanup
You can see the Console output on each test if you double-click the test method in the Test Results pane. It is also present in the .trx xml results file.
In addition, if you specify the "Define DEBUG constant", you can use the
System.Diagnostics.Debug.WriteLine("ClassInitialize Method invoked, yeah.");
.. which will end up in the "Output" pane.

Categories