如何使C#.net POCO线程安全?
我是C#net的新手,目前正在从事一个Selenium C#项目,该项目具有BrowserFactory类,并将POCO命名为Driver InitBrowser方法设置其值 当尝试在NUnitFramework中使用具有2个并行测试的并行测试运行测试时,肯定会出现错误,因为正如您所看到的,代码不是线程安全的,因此无法在并行测试环境中工作 代码如下:如何使C#.net POCO线程安全?,c#,.net,selenium,.net-core,C#,.net,Selenium,.net Core,我是C#net的新手,目前正在从事一个Selenium C#项目,该项目具有BrowserFactory类,并将POCO命名为Driver InitBrowser方法设置其值 当尝试在NUnitFramework中使用具有2个并行测试的并行测试运行测试时,肯定会出现错误,因为正如您所看到的,代码不是线程安全的,因此无法在并行测试环境中工作 代码如下: namespace SeleniumAutomationFramework.WrapperFactory { class BrowserF
namespace SeleniumAutomationFramework.WrapperFactory {
class BrowserFactory
{
private static readonly IDictionary<string, IWebDriver> Drivers = new Dictionary<string, IWebDriver>();
private static IWebDriver driver;
public static IWebDriver Driver
{
get
{
if (driver == null)
throw new NullReferenceException("The WebDriver browser instance was not initialized. Try to call the method InitBrowser instead.");
return driver;
}
private set
{
driver = value;
}
}
public static void InitBrowser(string browserName)
{
switch(browserName.ToLower())
{
case "firefox":
driver = new FirefoxDriver();
Drivers.Add("Firefox", Driver);
break;
case "chrome":
var options = new ChromeOptions();
options.AddArguments(
"--headless",
"--no-sandbox",
"--disable-dev-shm-usage"
);
driver = new ChromeDriver(options);
Drivers.Add("Chrome", Driver);
break;
case "ie":
driver = new InternetExplorerDriver();
Drivers.Add("IE", Driver);
break;
//case "chromium":
// driver = new ChromeDriver();
// break;
default:
driver = new ChromeDriver();
Drivers.Add("Chrome", Driver);
break;
}
}
public static void LoadApplication(string url)
{
Driver.Navigate().GoToUrl(url);
}
public static void CloseAllDrivers()
{
foreach (var key in Drivers.Keys)
{
Drivers[key].Close();
Drivers[key].Quit();
}
}
} }
名称空间SeleniumAutomationFramework.WrapperFactory{
类浏览器工厂
{
私有静态只读IDictionary驱动程序=新字典();
专用静态IWebDriver;
公共静态IWebDriver
{
得到
{
if(驱动程序==null)
抛出新的NullReferenceException(“WebDriver浏览器实例未初始化。请尝试调用InitBrowser方法。”);
返回驱动器;
}
专用设备
{
驱动因素=价值;
}
}
公共静态浏览器(字符串浏览器名)
{
开关(browserName.ToLower())
{
案例“firefox”:
驱动程序=新的FirefoxDriver();
添加(“Firefox”,驱动程序);
打破
案例“铬”:
var options=新的ChromeOptions();
options.AddArguments(
“--无头”,
“--没有沙箱”,
“--禁用开发人员shm使用”
);
驱动程序=新的色度驱动程序(可选);
驱动程序。添加(“铬”,驱动程序);
打破
案例“ie”:
驱动程序=新的InternetExplorerDriver();
驱动程序。添加(“IE”,驱动程序);
打破
//案例“铬”:
//驱动程序=新的ChromeDriver();
//中断;
违约:
驱动程序=新的ChromeDriver();
驱动程序。添加(“铬”,驱动程序);
打破
}
}
公共静态void加载应用程序(字符串url)
{
Driver.Navigate().gotour(url);
}
公共静态void CloseAllDrivers()
{
foreach(驱动程序中的var键。键)
{
驱动程序[key].Close();
驱动程序[key]。退出();
}
}
} }
页面生成器类正在调用驱动程序属性。
namespace SeleniumAutomationFramework.PageObjects
{
public static class Page
{
private static T GetPage<T>() where T : new()
{
var page = new T();
PageFactory.InitElements(BrowserFactory.Driver, page);
return page;
}
public static HomePage Home
{
get { return GetPage<HomePage>(); }
}
public static LoginPage Login
{
get { return GetPage<LoginPage>(); }
}
}
}
namespace SeleniumAutomationFramework.PageObjects
{
公共静态类页面
{
私有静态T GetPage(),其中T:new()
{
var page=newt();
PageFactory.InitElements(BrowserFactory.Driver,第页);
返回页面;
}
公共静态主页主页
{
获取{return GetPage();}
}
公共静态登录页面登录
{
获取{return GetPage();}
}
}
}
我认为最好的方法是使用Lazy类和ConcurrentDictionary
但是有司机财产的原因是什么?如果它并行运行,则只有最后加载的驱动程序。所以对于所有测试,您将使用一个驱动程序。我认为你应该只使用ConcurrentDictionary并这样做
var page = new HomePage(BrowserFactory.GetDriver("chrome"));
更新以更好地满足要求
我将使用的代码
class BrowserFactory
{
private static readonly ConcurrentDictionary<string, IWebDriver> Drivers = new ConcurrentDictionary<string, IWebDriver>();
private static IWebDriver InitDriver(string browserName)
{
switch (browserName.ToLower())
{
case "firefox":
return new FirefoxDriver();
case "chrome":
var options = new ChromeOptions();
options.AddArguments(
"--headless",
"--no-sandbox",
"--disable-dev-shm-usage"
);
return new ChromeDriver(options);
case "ie":
return InternetExplorerDriver();
default:
return new ChromeDriver();
}
}
public static IWebDriver GetDriver(string browserName)
{
if (!Drivers.ContainsKey(browserName))
{
var driver = InitDriver(browserName);
Drivers.TryAdd(browserName, driver);
}
return Drivers[browserName];
}
public static void LoadApplication(string browserName, string url)
{
Drivers[browserName].Navigate().GoToUrl(url);
}
// close should be called explicit by browserName or driver directly
// by closing all drivers you will broke all other still running tests
/*
public static void CloseAllDrivers()
{
foreach (var key in Drivers.Keys)
{
Drivers[key].Close();
Drivers[key].Quit();
}
}
*/
}
public abstract class PageBase
{
protected PageBase(IWebDriver driver)
{
Driver = driver;
}
protected IWebDriver Driver { get; private set; }
}
public class HomePage : PageBase
{
public HomePage(IWebDriver driver) : base(driver)
{
}
public IWebElement SomeElement => Driver.FindElement(By.CssSelector("selector"));
}
我的首选解决方案是停止使用(即静态属性/方法),而是创建一个对象,交给所有需要此功能的组件。您可能还希望为此对象创建一个接口。这将允许您的单元测试使用模拟对象而不是真实对象。这将避免在运行单元测试时出现大量web浏览器 更糟糕的解决方案是只向所有公共方法添加lock语句
static object lockObj = new object();
public static MyPublicMethod(){
lock(lockObj){
// Logic
}
}
顺便说一句,这里没有POCO,POCO只是一个简单的类(普通的旧CLR对象)哦,我是说DTO?它也不是DTO,通常是一个只有属性的类,里面根本没有业务逻辑:)好吧,那么这个“公共静态IWebDriver驱动程序”叫什么呢?这只是一个静态属性。我已经更新了问题的主体。问题是,页面生成器类正在调用Driver属性,它需要驱动程序的值。我有下面的错误。System.Reflection.TargetInvocationException:调用的目标已引发异常。-->OpenQA.Selenium.StaleElementReferenceException:stale元素引用:元素未附加到页面文档(会话信息:chrome=81.0.4044.138)您可以添加公共静态IWebDriver GetDriver(string browserName)方法并从字典返回驱动程序。请参见,我正在阻止多次传递browserName,这就是为什么我创建了驱动程序静态属性。静态驱动程序属性在并行环境中没有帮助。您必须传递browsername并使用GetDriver方法,或者更改测试,以便BrowseFactory在InitBrowser方法上返回驱动程序的实例,并且您可以在特定于浏览器的测试用例中使用此引用