C# Specflow - BeforeScenario hook is not being called - c#

packages:
.net core 3.1,
Specflow 3.8.7
Solution Structure:
I have Step definitions in project UMW.Selenium.UI (A)
namespace UMW.Selenium.UI.Steps
{
[Binding]
public class CalculatorStepDefinitions : UIFramework
{
UIBrowser uiBrowser;
public CalculatorStepDefinitions()
{
uiBrowser = new UIBrowser();
}
[Given(#"the first number is (.*)")]
public void GivenTheFirstNumberIs(int p0)
{
uiBrowser.NavigateToURL("https://demoqa.com/browser-windows");
}
}
}
I have Hooks (BeforeTestRun, BeforeScenario etc.) in another project Selenium.UI.Framework (B).
namespace Selenium.UI.Framework.Framework.Utilities.ScenarioFactory
{
using LogBuffer = List<string>;
[Binding]
[TestClass]
public class SetupAndTearDown
{
internal readonly ScenarioContext _scenarioContext;
internal readonly FeatureContext _featureContext;
private readonly IObjectContainer _objectContainer;
public SetupAndTearDown()
{
}
public SetupAndTearDown(IObjectContainer objectContainer, FeatureContext featureContext, ScenarioContext scenarioContext)
{
this._objectContainer = objectContainer;
_featureContext = featureContext;
_scenarioContext = scenarioContext;
}
[BeforeTestRun]
public static void InitializeTestSuite()
{
ReportsFactory.Report.StartTestSuite();
}
[BeforeScenario]
public void InitializeTestScenario()
{
ReportsFactory.Report.StartTestCase();
//_objectContainer.RegisterInstanceAs(Webdriver.Driver);
}
}
}
When I execute scenario from A, it does not call BeforeTestRun/BeforeScenario from B. Here project A uses functions from project B. The test runs successfully bypassing hooks.

You need to declare bindings from an external assembly in specflow.json.
{
"stepAssemblies": [
{
"assembly": "Selenium.UI.Framework"
}
]
}
Note: the name of the assembly, not the namespace, is required with no file extension. You will need to double check the name of the DLL file created by the Selenium.UI.Framework project.

Related

Sequence contains no matching element error when returning class Type of Interface using IsAssignableFrom

This is a follow up question to another post I created around implementing a UI test solution that could toggle which classes to execute code from based on interfaces. The whole goal was to re use test code on versions of apps that are identical (Web vs WPF).
The code compiles fine, but after the test is ran it bombs out on the GetPageModelType method call. Below is my implementation pretty much identical to the linked post, with a few minor adjustments to abstract some of the page object creation on a TestClassBase
UI Test that can determine which classes to execute code from at runtime using interfaces
Interface and corresponding Page Object classes
public interface ILogin
{
void Login(string username, string password);
}
public class WebLogin : ILogin
{
private readonly IWebDriver driver;
public WebLogin(IWebDriver driver)
{
this.driver = driver;
}
public void Login(string username, string password)
{
Console.WriteLine("Web Success!");
}
}
public class WPFLogin : ILogin
{
private readonly WindowsDriver<WindowsElement> session;
public WPFLogin(WindowsDriver<WindowsElement> session)
{
this.session = session;
}
public void Login(string username, string password)
{
Console.WriteLine("WPF Success!");
}
}
Page Object factory classes
public interface IPageModelFactory
{
ILogin CreateLogin();
}
public class WebPageModelFactory : IPageModelFactory
{
private readonly IWebDriver driver;
public WebPageModelFactory(IWebDriver driver)
{
this.driver = driver;
}
public ILogin CreateLogin()
{
return new WebLogin(driver);
}
}
public class WPFPageModelFactory : IPageModelFactory
{
private readonly WindowsDriver<WindowsElement> session;
public WPFPageModelFactory(WindowsDriver<WindowsElement> session)
{
this.session = session;
}
public ILogin CreateLogin()
{
return new WPFLogin(session);
}
}
public class PageModelFactory
{
private readonly object client;
public PageModelFactory(object client)
{
this.client = client;
}
// Create Page Objects
public ILogin CreateLoginPage()
{
var pageModelType = GetPageModelType<ILogin>();
var constructor = pageModelType.GetConstructor(new Type[] { client.GetType() });
return (ILogin)constructor.Invoke(new object[] { client });
}
private Type GetPageModelType<TPageModelInterface>()
{
return client.GetType().Assembly.GetTypes().Single(type => type.IsClass && typeof(TPageModelInterface).IsAssignableFrom(type));
}
}
TestClassBase - base class for tests, simplifies test scripts
[TestFixture]
public class TestClassBase
{
// WinAppDriver variables
private static string WinAppDriverExe = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe";
private string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
// Sessions
public WindowsDriver<WindowsElement> session;
public IWebDriver driver;
// Declare Page Objects
public ILogin login = null;
[SetUp]
public void SetUp()
{
if (GlobalData.targetHost.Equals("WPF"))
{
// Capabilities
AppiumOptions appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("app", GetExeFile());
appCapabilities.AddAdditionalCapability("appWorkingDir", GetWorkingDirectory());
// Create Session
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities, TimeSpan.FromMinutes(3));
session.Manage().Window.Maximize();
// Pass session to page objects
PageModelFactory wpfPages = new PageModelFactory(session);
login = wpfPages.CreateLoginPage();
} else if (GlobalData.targetHost.Equals("Web"))
{
}
}
[TearDown]
public void TearDown()
{
// Clean up code...
}
}
LoginTests
public class LoginTests : TestClassBase
{
[Test]
public void Login()
{
// Login
login.Login("", "");
}
}
Whats not pictured above is my GlobalData.cs class which just contains a bunch of hardcoded variables that are used in the tests. I have the targetHost variable set to "WPF" while testing this against the WPF host. The StartUp code does launch the app as expected, it fails when we call GetPageModelType on PageModelFactory.CreateLoginPage();
I wasn't able to see this in my answer on your original question. The assembly in which the "client" resides and the assembly in which the page models reside are different. That means the PageModelFactory will need a second constructor parameter to know which assembly to search when initializing new page models:
public class PageModelFactory
{
private readonly object client;
private Assembly Assembly => GetType().Assembly;
public PageModelFactory(object client)
{
this.client = client;
}
// Create Page Objects
public ILogin CreateLoginPage()
{
var pageModelTypes = GetPageModelTypes<ILogin>();
var constructorSignature = new Type[] { client.GetType() };
foreach (var type in pageModelTypes)
{
var constructor = type.GetConstructor(constructorSignature);
if (constructor != null)
return (ILogin)constructor.Invoke(new object[] { client });
}
throw new InvalidOperationException($"No class found implementing ILogin with a constructor that accepts {client.GetType().FullName} as an argument in assembly {Assembly.Name}");
}
private IEnumerable<Type> GetPageModelTypes<TPageModelInterface>()
{
return Assembly.GetTypes()
.Where(type => type.IsClass
&& typeof(TPageModelInterface).IsAssignableFrom(type));
}
}

Servicestack Test: Method not found: 'Int32 ServiceStack.DataAnnotations.CustomFieldAttribute.get_Order()

Trying to build integration test with connection to db in ServiceStack.
My ServiceStack app is working fine, but when I run simple test I got this error message in line:22
System.MissingMethodException: 'Method not found: 'Int32 ServiceStack.DataAnnotations.CustomFieldAttribute.get_Order()'.'
There is a lite cod:
using ServiceStack;
using ServiceStack.OrmLite;
using ServiceStack.Data;
using NUnit.Framework;
using ServiceStack.DataAnnotations;
using System.Collections.Generic;
namespace oth.Tests.IntegrationTests
{
public class AppHost2 : AppSelfHostBase
{
public AppHost2() : base("Customer REST Example", typeof(CustomerService).Assembly) { }
public override void Configure(Container container)
{
var connectionString = "Host=localhost;Port=5432;Database=test_1234;Username=postgres;Password=local";
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, PostgreSqlDialect.Provider));
using var db = container.Resolve<IDbConnectionFactory>().Open();
db.CreateTableIfNotExists<Customer>();
}
}
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
[Route("/customers", "GET")]
public class GetCustomers : IReturn<GetCustomersResponse> { }
public class GetCustomersResponse
{
public List<Customer> Results { get; set; }
}
public class CustomerService : Service
{
public object Get(GetCustomers request)
{
return new GetCustomersResponse { Results = Db.Select<Customer>() };
}
}
public class CustomerRestExample
{
const string BaseUri = "http://localhost:2000/";
ServiceStackHost appHost;
public CustomerRestExample()
{
//Start your AppHost on TestFixture SetUp
appHost = new AppHost2()
.Init()
.Start(BaseUri);
}
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
/* Write your Integration Tests against the self-host instance */
[Test]
public void Run_Customer_REST_Example()
{
var client = new JsonServiceClient(BaseUri);
var all = client.Get(new GetCustomers());
Assert.That(all.Results.Count, Is.EqualTo(0));
}
}
}
Anytime you see a missing type or missing method exceptions when using the MyGet pre-release packages it means you have a dirty installation (i.e. using pre-release packages from different build times).
In which case you'd need to Clear your Nuget packages cache and download the latest packages again, which ensures all your packages are from the latest same build:
$ dotnet nuget locals all -clear

Webdriver inheritance between clases

Question is probably really trivial but I cannot handle it in proper way. I'm using Selenium with NUnit, having two clases:
1) "DemoTest" which involves one simply test "DummyTest":
public class DemoTest : TestBase
{
public class RunTest
{
[Test, Category("Main-Tests"), Order(1)]
public void DummyTest()
{
}
}
}
2) "Test base" class where I want to place all of the NUnit/ driver attributes like: "SetUp" / "TearDown"
[TestFixture]
public class TestBase
{
public IWebDriver driver;
public IWebDriver Driver
{
get { return driver; }
set { driver = value; }
}
public string pageURL = "http://automationpractice.com/";
[SetUp]
public void SetUp()
{
driver = new ChromeDriver();
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(15);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(0);
driver.Navigate().GoToUrl(pageURL);
}
[TearDown]
public void TearDown()
{
driver.Close();
driver.Dispose();
}
}
}
As NUnit attributes are declared (SetUp section) my test from DemoTest class should at least move on the page under pageURL variable.
Result is that after running a test it's immediately jump on "passed" without opening the specified address.
"DemoTest" inherits from "Test base" class. Nuget packages are installed correctly. When I'm placing test inside the "Test base" class everything works correctly but I want to have it separated.
Try to fix DemoTest class as follows:
[TestFixture]
public class DemoTest : TestBase
{
[Test, Category("Main-Tests"), Order(1)]
public void DummyTest()
{
}
}

C# Interface? categories methods with the same name

how to do something like this. So the scenario is I have 2 "DoLogin" methods in a different platform (Mobile Version, Desktop Version)
I want to make the code more readable and look something like this
Ex.
if I wanted to login to a desktop version.
webDriver.Desktop.Dologin(accountModel)
If I wanted to login to a mobile version.
webDriver.Mobile.DoLogin(accountModel)
this is what I currently have.
public static class Desktop
{
public static void DoLogin(this ChromeDriver webDriver, AccountModel account)
{
}
}
public static class Mobile
{
public static void DoLogin(this ChromeDriver webDriver, AccountModel account)
{
}
}
EDIT
This is what I'm currently doing to organize it.
public static class Desktop
{
public static void DesktopDoLogin(this ChromeDriver webDriver, AccountModel account)
{
}
}
public static class Mobile
{
public static void MobileDoLogin(this ChromeDriver webDriver, AccountModel account)
{
}
}
whenever I call a method let say desktop what I do is.
webDriver.DesktopDoLogin(account)
or
webDriver.MobileDoLogin(account)
I guess whats important is it works.
I personally don't like using extension methods. This is a preference. I would have most likely implemented it as follows. I have never used Selenium before, so I'm not sure what interface WebDriver is/implements but you can get the gist of the pattern.
public interface IPlatform
{
void Login<T>(T model);
}
public class Desktop : IPlatform
{
private readonly WebDriver _webDriver;
public Desktop(WebDriver driver)
{
_webDriver = driver;
}
public void Login<T>(T model)
{
// do login here
}
}
// usage
IPlatform desktop = new Desktop(/*chromedriver*/); // or inject
desktop.Login<AccountModel>(model);
It seems to me that you can get this:
void Main()
{
var webDriver = new ChromeDriver();
webDriver.Desktop.DoLogin(new AccountModel());
webDriver.Mobile.DoLogin(new AccountModel());
}
By doing this:
public interface IDoLogin
{
void DoLogin(AccountModel account);
}
public class Desktop : IDoLogin
{
private ChromeDriver _webDriver;
public Desktop(ChromeDriver webDriver)
{
_webDriver = webDriver;
}
public void DoLogin(AccountModel account) { }
}
public class Mobile : IDoLogin
{
private ChromeDriver _webDriver;
public Mobile(ChromeDriver webDriver)
{
_webDriver = webDriver;
}
public void DoLogin(AccountModel account) { }
}
public class ChromeDriver
{
public Desktop Desktop;
public Mobile Mobile;
public ChromeDriver()
{
this.Desktop = new Desktop(this);
this.Mobile = new Mobile(this);
}
}
You can do it easily using some static voids like that:-
Public class desktop {
Public static void DoLogin(AccountModel val){
//code for desktop
}
}
Public class mobile {
Public static void DoLogin(AccountModel val){
//code for mobile
}
}
Public class webDriver{
Public static desktop Desktop;
Public static mobile Mobile;
}
//usage
webDriver.mobile.DoLogin(...);
//or
mobile.DoLogin();
Why not using Interface?
Create one common interface with DoLogin Method, and Implement that interface in Desktop and Mobile class.
Common Interface:
public interface ICommon
{
void Login<T>(T model);
}
Implement this interface in your Desktop and Mobile Class.

Inject Dependency into Core Module of a ASP Boilerplate project

I have NotificationJob class where I have all the functions related to Notification Feature for my .Net Core application. It has some injected dependencies from Domain services. I am having a problem trying to inject INotificationJob interface of the class into the CoreModule of the project.
I initially tried injecting the interface directly into the CoreModule but failed so I created another module in the same file called NotificationModule where I inject INotificationJob interface. Then I try to link it with the CoreModule using [DependsOn(typeof(oasisCoreModule))] annotation.
Core Module of the project
[DependsOn(
typeof(AbpZeroCoreModule),
typeof(AbpHangfireAspNetCoreModule),
typeof(AbpWebCommonModule)
)]
public class oasisCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.Modules.AbpWebCommon().SendAllExceptionsToClients = true;
Configuration.BackgroundJobs.UseHangfire();
Configuration.Auditing.IsEnabledForAnonymousUsers = true;
// Declare entity types
Configuration.Modules.Zero().EntityTypes.Tenant = typeof(Tenant);
Configuration.Modules.Zero().EntityTypes.Role = typeof(Role);
Configuration.Modules.Zero().EntityTypes.User = typeof(User);
oasisLocalizationConfigurer.Configure(Configuration.Localization);
// Enable this line to create a multi-tenant application.
Configuration.MultiTenancy.IsEnabled = oasisConsts.MultiTenancyEnabled;
// Configure roles
AppRoleConfig.Configure(Configuration.Modules.Zero().RoleManagement);
Configuration.Settings.Providers.Add<AppSettingProvider>();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(oasisCoreModule).GetAssembly());
}
public override void PostInitialize()
{
IocManager.Resolve<AppTimes>().StartupTime = Clock.Now;
}
}
// This is the custom module that I created in the same file as the core module.
[DependsOn(typeof(oasisCoreModule))]
public class NotificationModule : AbpModule
{
INotificationJob _job;
public NotificationModule(INotificationJob job)
{
_job = job;
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
public override void PostInitialize()
{
_job.Loop();
}
}
INotificationJob Interface I am Injecting into the NotificationModule
public interface INotificationJob: IDomainService
{
void Loop();
void CheckTickets();
void CheckReminders(string email, string ticket);
}
Class Implementation of INotificationJob Interface
public class NotificationJob: DomainService, INotificationJob
{
private readonly ITicketRefManager _ticketRefManager;
private readonly IClientManager _clientManager;
private readonly IEmailManager _emailManager;
public NotificationJob(
ITicketRefManager ticketRefManager,
IClientManager clientManager,
IEmailManager emailManager,
)
{
_ticketRefManager = ticketRefManager;
_clientManager = clientManager;
_emailManager = emailManager;
}
public void Loop()
{
RecurringJob.AddOrUpdate(() => CheckTickets(), Cron.Minutely);
}
}
When I run the solution, I am presented with an error saying as shown:
Are there any other steps that I need to take to complete the Dependency Injection process? Or are the steps that I described flawed?
I'm not sure what you're trying to do with you "interface injecting", but you can try this if I understand correctly what you're trying to do :
Core Module
[...]
public override void PostInitialize()
{
var recurrentJobs = IocManager.Resolve<NotificationJob>();
RecurringJob.RemoveIfExists("JobName");
RecurringJob.AddOrUpdate("JobName", () => recurrentJobs.CheckTickets(), Cron.Minutely);
}
Your class
public class NotificationJob : ISingletonDependency
{
private readonly ITicketRefManager _ticketRefManager;
private readonly IClientManager _clientManager;
private readonly IEmailManager _emailManager;
public NotificationJob(
ITicketRefManager ticketRefManager,
IClientManager clientManager,
IEmailManager emailManager,
)
{
_ticketRefManager = ticketRefManager;
_clientManager = clientManager;
_emailManager = emailManager;
}
public void CheckTickets()
{
//Do something
}
}
Does it helps ?

Categories