繁体   English   中英

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

[英]How can I make the C# .net POCO thread safe?

I am new in C# .net and I'm currently working on a Selenium C# Project that have the BrowserFactory class and have POCO named Driver.

InitBrowser 方法设置它的值。

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

下面是代码:

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();
            }
        }
    } }

页面生成器 class 正在调用 Driver 属性。

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>(); }
        }
    }
}

我认为最好的方法是使用 Lazy class 和 ConcurrentDictionary。

但是拥有 Driver 属性的原因是什么? 如果它并行运行,则您只有最后加载的驱动程序。 因此,对于所有测试,您将使用一个驱动程序。 我认为你应该只使用 ConcurrentDictionary 并这样做

更新以更好地适应需求

我会使用的代码

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"));
}

然后像这样创建页面

var page = new HomePage(BrowserFactory.GetDriver("chrome"));

我首选的解决方案是停止使用全局字段(即 static 属性/方法),而是创建一个 object 来处理所有需要此功能的组件。 您可能还想为此 object 创建一个接口。 这将允许您的单元测试使用模拟 object 而不是真实的东西。 这将避免在您运行单元测试时弹出一堆网络浏览器。

更糟糕的解决方案是在所有公共方法中添加锁定语句,

static object lockObj = new object();
public static MyPublicMethod(){
    lock(lockObj){
         // Logic
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM