如何使C#.net POCO线程安全?

如何使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

我是C#net的新手,目前正在从事一个Selenium C#项目,该项目具有BrowserFactory类,并将POCO命名为Driver

InitBrowser方法设置其值

当尝试在NUnitFramework中使用具有2个并行测试的并行测试运行测试时,肯定会出现错误,因为正如您所看到的,代码不是线程安全的,因此无法在并行测试环境中工作

代码如下:

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方法上返回驱动程序的实例,并且您可以在特定于浏览器的测试用例中使用此引用