Using ITestOutputHelper with Selenium, xUnit and C# - c#

I have recently switched a project from NUnit to xUnit so that ITestOutputHelper can be used to output to a log.
The project is a fairly standard layout
Feature Files->Step Classes->Page Classes->Help Classes. Include in the helper classes we have the hooks.class also. I am using the xUnit runner.
So in my hooks class I have created this
private readonly ScenarioContext _scenarioContext;
private ITestOutputHelper _testOutputHelper;
public Hooks(ScenarioContext scenarioContext, ITestOutputHelper testOutputHelper)
{
_scenarioContext = scenarioContext;
this._testOutputHelper = testOutputHelper;
}
public void WriteOutput(string theMessage)
{
_testOutputHelper.WriteLine(theMessage);
}
Now my question is how do I access the WriteOutput function from the other classes?
Or have I placed it in the wrong class?

Since your hooks class already accepts an ITestOutputHelper object, your other step definitions need only do the same thing. From that point on it's just good old fashioned dependency injection.
If you initialize page models and utility classes in each step definition class, since it appears ITestOutputHelper is already registered in SpecFlow's dependency injection framework, you can just pass references to the helper from constructor to constructor.
For example, add a constructor arg and field to a step definition:
[Binding]
public class LoginSteps
{
private ITestOutputHelper testOutputHelper;
private LoginPage loginPage;
private SomeUtility utility;
public LoginSteps(IWebDriver driver, ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
// Pass the test output helper to a page model
loginPage = new LoginPage(driver, testOutputHelper);
// Pass the test output helper to a utility class
utility = new SomeUtility(testOutputHelper);
}
[Given(#"the user is logged in as ""(.*)"")")]
public void GivenTheUserIsLoggedInAs(string username)
{
testOutputHelper.WriteLine("...");
loginPage.LogIn(username);
}
}
Then the page model and utility class need constructor args and fields:
public class LoginPage
{
private IWebDriver driver;
private ITestOutputHelper testOutputHelper;
public LoginPage(IWebDriver driver, ITestOutputHelper testOutputHelper)
{
this.driver = driver;
this.testOutputHelper = testOutputHelper;
}
// ...
}
public class SomeUtility
{
private ITestOutputHelper testOutputHelper;
public SomeUtility(ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
}
// ...
}

With the help of another Dev got the answer, see below
Step Class TestAppSteps
using Xunit.Abstractions;
[Binding]
public sealed class TestAppSteps : TestAppPage
{
public TestAppSteps(ITestOutputHelper output) : base(output)
{
}
code
}
Page Class TestAppPage
using Xunit.Abstractions;
public class TestAppPage : PageAssertions
{
public TestAppPage(ITestOutputHelper output) : base(output)
{
}
code
}
Utility Class PageAssertions
using Xunit.Abstractions;
public class PageAssertions : SharedClass
{
public PageAssertions(ITestOutputHelper output) : base(output) { }
code inc'
WriteToReport("Pass: URL is correct");
}
Utility Class SharedClass
using Xunit.Abstractions;
public abstract class SharedClass : OutputFunctions
{
public SharedClass(ITestOutputHelper output)
: base(output)
{
}
shared code including
WriteToReport(GetTheCurrentMethod());
}
Abstract Class OutputFunctions
using Xunit.Abstractions;
public abstract class OutputFunctions
{
protected readonly ITestOutputHelper _output;
public OutputFunctions(ITestOutputHelper output)
{
_output = output;
}
public void WriteToReport(string theMessage)
{
_output.WriteLine(theMessage);
}
}

Related

Replace ScenarioContext.Current in non binding class using context injection

I want to use context injection to remove the obsolete warnings for FeatureContext.Current and ScenarioContext.Current my code.
I have a reporter in a non binding class that requires setup before the test is run.
I have tried making a constructor and instantiating it but the values always return as Null.
In the Setup Step
namespace EclipseWebAutomationV2.Steps
{
[Binding]
class StepSetup
{
public static FeatureContext _featurecontext;
public static ScenarioContext _scenariocontext;
[BeforeTestRun]
public static void InitializeReport()
{
Reporter.ReportInit();
}
[BeforeFeature]
public static void BeforeFeature()
{
Reporter bfeature = new Reporter(_featurecontext, _scenariocontext);
bfeature.ReportFeature();
}
}
}
In the report class:
namespace EclipseWebAutomationV2.Configurations
{
class Reporter
{
private readonly FeatureContext _featurecontext;
private readonly ScenarioContext _scenariocontext;
public Reporter(FeatureContext _featurecontext, ScenarioContext _scenariocontext)
{
this._featurecontext = _featurecontext;
this._scenariocontext = _scenariocontext;
}
public static void ReportInit()
{
//does stuff
}
public void ReportFeature()
{
featureName = extent.CreateTest<Feature>(_featurecontext.FeatureInfo.Title);
}
}
}
_featurecontext always returns null. I was hoping for it to get the current feature context so i can use it to get the title and use it in other parts of the reporting class.
I am having the same issue with _scenariocontext.
The main problem is the Reporter object requires a FeatureContext and ScenarioContext object. When the [BeforeFeature] hook is executing, the ScenarioContext does not exist yet.
The [BeforeFeature] hook supports a couple of overloads, one of which accepts the newly created FeatureContext as an argument.
This coupled with removing the FeatureContext and ScenarioContext objects as dependencies for the Reporter class will solve your problem.
First, change the StepSetup class to remove the dependencies on FeatureContext and ScenarioContext, and alter the [BeforeFeature] to accept a FeatureContext object as an argument:
[Binding]
class StepSetup
{
[BeforeTestRun]
public static void InitializeReport()
{
Reporter.ReportInit();
}
[BeforeFeature]
public static void BeforeFeature(FeatureContext featureContext)
{
var reporter = new Reporter();
reporter.ReportFeature(featureContext);
}
}
Then change the Reporter class to accept a FeatureContext argument in ReportFeature:
class Reporter
{
public static ReportInit()
{
// does stuff
}
public void ReportFeature(FeatureContext featureContext)
{
featureName = extent.CreateTest<Feature>(featureContext.FeatureInfo.Title);
}
}
If the Reporter.ReportFeature method does not use any instance fields, consider making this method a static method as well, and using a static constructor instead of the Reporter.ReportInit() method:
static class Reporter
{
static Reporter()
{
// does stuff
}
public static void ReportFeature(FeatureContext featureContext)
{
featureName = extent.CreateTest<Feature>(featureContext.FeatureInfo.Title);
}
}
Then your StepSetup class becomes even simpler with no need to call a static "init" method on the Reporter class:
[Binding]
class StepSetup
{
[BeforeFeature]
public static void BeforeFeature(FeatureContext featureContext)
{
Reporter.ReportFeature(featureContext);
}
}
See Static Constructors (C# Programming Guide)

How do I provide extra parameters with Ninject?

I need to auto-resolve dependencies of my windows form. The only problem is my forms constructor expects an integer value as well. Please have a look at the implementation in the code section.
//Ninject bindings
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<LogToDB>();
Bind<ICopy>().To<CopyToFolder>();
}
}
//WinForm - Form1
public partial class Form1 : Form
{
public readonly ILogger _processRepository;
public readonly Icopy _copy;
public readonly int ValueToEdit;
public Form1(int valueToEdit, ILogger logger, ICopy copy)
{
this._processRepository = logger;
this._copy = copy;
this.ValueToEdit = valueToEdit;
InitializeComponent();
}
}
//main
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
IKernel kernel = new StandardKernel(new Bindings());
Application.Run(kernel.Get<Form1>());
}
I get an error:
Ninject.ActivationException: 'Error activating int
No matching bindings are available, and the type is not self-bindable.
How can I auto resolve the form dependencies and also be able to pass the integer value? Actually, I have used the same form for add and edit purpose so while editing, this edit value should be set.
I guess the cleanest way to solve this problem is to create a factory:
interface IForm1Factory
{
Form1 Create(int valueToEdit);
}
class Form1Factory
{
public readonly ILogger _processRepository;
public readonly Icopy _copy;
public Form1Factory(ILogger logger, ICopy copy)
{
this._processRepository = logger;
this._copy = copy;
}
public Form1 Create(int valueToEdit)
{
return new Form1(valueToEdit, _processRepository, _copy);
}
}
There is also an extension (Ninject.Extensions.Factory) that allow you to auto-generate factories such as the Form1Factory based on the interface. If you use that extension, you declare that using Bind<IForm1Factory>().ToFactory().
using Ninject;
using Ninject.Modules;
using Ninject.Parameters;
//Add new class
public class CompositionRoot
{
public static IKernel _ninjectKernel;
public static void Wire(INinjectModule module)
{
_ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return _ninjectKernel.Get<T>();
}
public static T ResolveWithArgument<T>(ConstructorArgument argument)
{
return _ninjectKernel.Get<T>(argument);
}
}
//Ninject bindings
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<LogToDB>();
Bind<ICopy>().To<CopyToFolder>();
}
}
//WinForm - Form1
public partial class Form1 : Form
{
public readonly ILogger _processRepository;
public readonly Icopy _copy;
public readonly int ValueToEdit;
public Form1(ILogger logger, ICopy copy, int valueToEdit)
{
this._processRepository = logger;
this._copy = copy;
this.ValueToEdit = valueToEdit;
InitializeComponent();
}
}
//main
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Apply the binding rule first
CompositionRoot.Wire(new Bindings());
//Then resolve your form dependencies this way using Ninject passing along the
constructor arguments.
CompositionRoot.ResolveWithArgument<Form1>(new ConstructorArgument("valueToEdit",
1)).ShowDialog();
}

Sharing the dirver object initialized in base class to that of page classes

In the POM model, we ideally tend to have the driver object being initialized in base class. And in the page classed we pass this driver object. But the problem is to avoid passing this object as well and the tests should continue to work in parallel too in XUNit framework. Below is the structure
public class BaseClass:IDisposable
{
public IWebDriver Driver{get;set;}
public BaseClass()
{
if(Driver == null)
{
Driver = new ChromeDriver();
}
}
}
public class Page1:BaseClass
{
public void method1()
{
this.Driver.Navigate.GoToUrl("http://www.google.com")
}
}
public class Page2:BaseClass
{
public void method2()
{
this.Driver.Navigate.GoToUrl("http://www.stackoverflow.com")
}
}
public class TestClass
{
[Fact]
public void Test1()
{
new Page1().method1();
new Page2().method2();
}
}
Now in the above structure if the test executes two instances of the driver object will be created because of OOPS. If we need to avoid this we can the Driver object as static and reinitialize it if the object is null. But this will again fail when we run multiple tests in parallel. Any suggession? Thing I am trying to achieve is that full encapsulation where the Test class should not have any access to Selenium objects. These objects should be only accessible in Page class or its Operation class if we have any.
We need to ensure we create a driver singleton and its threadsafe to run parallely
[TestClass]
public class UnitTest1 : TestBase
{
[TestMethod]
public void TestMethod1()
{
new Page1().method1();
new Page2().method2();
Driver.Testcleanup();
}
[TestMethod]
public void TestMethod2()
{
new Page1().method1();
new Page2().method2();
Driver.Testcleanup();
}
public class Page1
{
public void method1()
{
Driver.Instance.Navigate().GoToUrl("http://www.google.com");
}
}
public class Page2
{
public void method2()
{
Driver.Instance.Navigate().GoToUrl("http://www.google.com");
}
}
}
Driver Class will handle the initialization of the singleton and cleanup
public sealed class Driver
{
[ThreadStatic]
public static IWebDriver driver = null;
public static IWebDriver Instance
{
get
{
if (driver == null)
{
driver = new ChromeDriver();
}
return driver;
}
}
public static void Testcleanup()
{
driver.Quit();
driver = null;
}
}

need to call controller's action method into another class in c# mvc

I have created one method in a controller, but I need to call this method into anther class and I don't know how to call it.
my sample code :-
public class ReportController : BaseController
{
private readonly IPermissionService _permissionService;
private readonly ICompaniesService _companyService;
public ReportController(IPermissionService permissionService,
ICompaniesService companyService)
{
this._permissionService = permissionService;
this._companyService = companyService;
}
public void Reporting()
{
// code
}
}
public class Home {
public void Execute()
{
//I need to execute the Reporting method here
}
}
I have tried many things to call my method in another class method but I can't get it to work.
It's a bad design way to have some code in a controller who have to be called in several places. Create a class in your project or in an external DLL who contains this code and call this class in the controllers who need this method.
Create a class somewhere (in you project, or in a class library) :
public class MyClass
{
public MyClass()
{}
public void MyMethod()
{
// Some code here
}
}
And implement this code in your controllers or classes who need this.
public class ReportController : BaseController
{
private readonly IPermissionService _permissionService;
private readonly ICompaniesService _companyService;
public ReportController(IPermissionService permissionService,
ICompaniesService companyService)
{
this._permissionService = permissionService;
this._companyService = companyService;
}
public void Reporting()
{
MyClass myClass = new MyClass();
myClass.MyMethod();
}
}
By the way, if your code doesn't need any instance, you can create a static class, like :
public static class MyClass
{
public static void MyMethod()
{
// Some code here
}
}
public class ReportController : BaseController
{
private readonly IPermissionService _permissionService;
private readonly ICompaniesService _companyService;
public ReportController(IPermissionService permissionService,
ICompaniesService companyService)
{
this._permissionService = permissionService;
this._companyService = companyService;
}
public void Reporting()
{
MyClass.MyMethod();
}
}
There are two ways of accomplishing this. You are using dependency injection in your controller's constructor, so you have two options of doing this (not sure if I have missed any?):
Create an instance of each of your services in the calling method and pass these instances through when instantiating the controller, or
Add a constructor to your home class, set the services and pass them through when instantiating the controller
Option 1:
public class Home
{
public void Execute()
{
// Create the variables that are needed by the constructor
IPermissionService permissionService = new PermissionService();
ICompaniesService companiesService = new CompaniesService();
// Create an instance of your controller and pass through
// the variables created above
ReportController reportController = new ReportController(permissionService, companiesService);
// Now that you have an instance of your controller call the method
reportController.Reporting();
}
}
It works on the same principle as creating an instance of a class and then calling its methods.
Option 2:
public class Home
{
private IPermissionService permissionService;
private ICompaniesService companiesService;
public Home(IPermissionService permissionService, ICompaniesService companiesService)
{
this.permissionService = permissionService;
this.companiesService = companiesService;
}
public void Execute()
{
ReportController reportController = new ReportController(permissionService, companiesService);
reportController.Reporting();
}
}
ReportController reportController = new ReportController();
reportController.Reporting();
As you would any other class that's method isn't static
Please read this answer, they go into a lot more detail than I do

Using Castle Windsor in Windows Form Application

I want to add a logger to the Windows Form Application by using Castle IOC.
I registered the logger like below:
static class Program
{
[STAThread]
static void Main()
{
IWindsorContainer container = new WindsorContainer().Install(FromAssembly.This());
container.Register(Component.For<ICommonLogger>().ImplementedBy(typeof(CommonLogger)).LifeStyle.Singleton);
container.Kernel.Resolver.AddSubResolver(new EAE_Automation.COM.LoggerResolver(container.Kernel));
}
}
Then another form of the application, I tried to use the logger as property but It comes null to the program.
namespace Test.KNXManagement
{
public partial class Test: Form
{
public ICommonLogger Logger { get; set;}
public Tunneling()
{
Logger.Info("Testing.......................");
}
}
}
I think I am missing an important point here
I also register the Form class to resolve sub dependencies.
container.Register(Classes.FromThisAssembly().BasedOn<Form>());
Then also run the application as below
Application.Run(container.Resolve<Test>());
Thats solved my problem.
Thanks everyone.
The following code, based on yours, works perfectly fine for me:
private static IWindsorContainer _container;
static Program()
{
Debug.Listeners.Add(new ConsoleTraceListener());
_container = new WindsorContainer().Install(FromAssembly.This());
_container.Register(Component.For<ICommonLogger>().ImplementedBy(typeof(CommonLogger)).LifeStyle.Singleton);
}
private static ICommonLogger Logger { get; set; }
private static void Main(string[] args)
{
Logger = _container.Resolve<ICommonLogger>();
Logger.Write("Text");
Console.ReadLine();
}
public interface ICommonLogger
{
void Write(string str);
}
public class CommonLogger : ICommonLogger
{
public void Write(string str)
{
Console.WriteLine(str);
}
}
The output of the program in the console is Text.

Categories

Resources