【问题标题】:How can I make the C# .net POCO thread safe?如何使 C# .net POCO 线程安全?
【发布时间】:2020-08-30 16:56:42
【问题描述】:

我是 C# .net 的新手,我目前正在开发一个 Selenium C# 项目,该项目具有 BrowserFactory 类并具有名为 Driver 的 POCO。

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

页面生成器类正在调用 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>(); }
        }
    }
}

【问题讨论】:

  • 对了,你这里没有POCO,POCO只是一个简单的类(Plain Old CLR Object)
  • 哦,我是说 DTO?
  • 好吧,它也不是 DTO,它通常是一个只有属性而根本没有业务逻辑的类 :)
  • 好的,那么这个 '''public static IWebDriver Driver''' 叫什么?
  • 这只是一个静态属性。

标签: c# .net selenium .net-core


【解决方案1】:

我的首选解决方案是停止使用global fields(即静态属性/方法),而是创建一个对象,该对象将交给所有需要此功能的组件。您可能还想为这个对象创建一个接口。这将允许您的单元测试使用模拟对象而不是真实对象。这样可以避免在运行单元测试时弹出一堆网络浏览器。

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

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

【讨论】:

    【解决方案2】:

    我认为最好的方法是使用 Lazy 类和 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"));
    

    【讨论】:

    • 我已经更新了问题的正文。问题是页面生成器类正在调用 Driver 属性,它需要 Driver 的值。
    • 所以当我尝试并行运行时。我有以下错误。 System.Reflection.TargetInvocationException :调用目标引发了异常。 ----> OpenQA.Selenium.StaleElementReferenceException : stale element reference: element is not attach to the page document (Session info: chrome=81.0.4044.138)
    • 您可以添加 public static IWebDriver GetDriver(string browserName) 方法并从字典中返回驱动程序
    • 我明白了,我正在阻止多次传递浏览器名称,这就是我创建 Drivers 静态属性的原因。
    • 静态驱动程序属性在并行环境中没有帮助。您必须传递浏览器名称并使用 GetDriver 方法,或者更改测试,以便 BrowseFactory 在 InitBrowser 方法上返回驱动程序的实例,您可以在浏览器特定的测试用例中使用此引用
    猜你喜欢
    • 2011-08-02
    • 2012-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    相关资源
    最近更新 更多