[英]Is it possible to intercept the get method of a property when said property is marked with an attribute?
我正在使用Selenium
運行自動化測試,並使用Autofac
在運行時管理依賴注入。
Selenium 使用WebDriver
來控制瀏覽器,通常通過接口IWebDriver
。 要在頁面中查找元素,可以使用FindElement
方法,該方法將返回帶有接口 IWebElement 的IWebElement
。 要查找多個元素,我們可以使用FindElements
依次返回ReadOnlyCollection<IWebElement>
web 自動化中最常用的模式是Page Object Model
,它建立:
頁面 object 是一個面向對象的 class,用作 AUT 頁面的接口。 然后,測試在需要與該頁面的 UI 交互時使用此頁面 object class 的方法。 好處是如果頁面的 UI 發生變化,測試本身不需要更改,只需要更改頁面 object 中的代碼。 隨后,支持該新 UI 的所有更改都位於一個位置。
因此,這方面的一個例子可能是:
public class SomePage
{
private IWebDriver driver;
private IWebElement UsernameElement => driver.FindElement(By.Id("username"));
private IWebElement PasswordElement => driver.FindElement(By.Id("password"));
private IWebElement LoginButton => driver.FindElement(By.Id("login"));
public SomePage(IWebDriver driver)
{
this.driver = driver;
}
public Input_Username(string username) => UsernameElement.SendKeys(username);
public Input_Password(string password) => PasswordElement.SendKeys(password);
public Click_Login() => LoginButton.Click();
}
最近我一直在努力解決一個問題:如果網頁的某個元素是不可變的(意味着一旦在 DOM 中設置了它就永遠不會改變),我想在第一次訪問時保存該元素的值並以后每次我們需要該元素時都返回它。
遵循上一個示例的第一種方法可能是:
public class SomePage
{
...
private IWebElement usernameBackingField;
private IWebElement UsernameElement
{
get
{
if (usernameBackingField == null)
usernameBackingField = driver.FindElement(By.Id("username"));
return usernameBackingField;
}
}
...
}
這樣,我們第一次使用屬性UsernameElement
時,我們將值保存在usernameBackingField
中,每次我們需要這個元素時,我們都會保存它。
如果元素特別難創建,我們可以使用Lazy<IWebElement>
來保存值,然后再使用Value
:
public class SomePage
{
...
private Lazy<IWebElement> lazyUsername;
private IWebElement UsernameElement
{
get
{
if (lazyUsername == null || lazyUsername.IsValueCreated == false)
lazyUsername = new Lazy<IWebElement>(() => driver.FindElement(By.Id("username")), LazyThreadSafetyMode.PublicationOnly);
return lazyUsername.Value;
}
}
...
}
此示例按預期工作,但為每個元素執行此操作很痛苦。
知道我需要什么,即:攔截屬性的get method
,將其用作Lazy<IWebElement>
實例化的Func<IWebElement>
,然后在每次訪問屬性時返回 Lazy.Value ,這就是 Autofac可以幫助我實現如下代碼嗎?
[Cacheable]
private IWebElement UsernameElement => driver.FindElement(By.Id("username"));
謝謝你的時間。
Autofac是一個依賴注入工具,這意味着它只會幫助獲取實例及其所有依賴圖。 它無助於攔截方法或屬性。
您需要的是一個面向方面的編程工具。 此類工具將有助於攔截方法等。 .net 最受歡迎的一種是城堡動態代理
DynamicProxy 將自動為您的類型注入代理,並在需要時注入IInterceptor
。
假設您有以下 class
public class Test1
{
[Cacheable]
protected virtual String UserName => "test";
}
DynamicProxy 將自動創建從Test1
派生的 class 並覆蓋UserName
屬性以注入這些 IInterceptor。 這就是為什么屬性應該是虛擬的原因。
然后,您將使用動態代理提供的類型,而不是使用Test1
。
獲取代理類型的方法有很多,其中一種解決方案是:
ProxyGenerator generator = new ProxyGenerator();
IInterceptor interceptor = new CacheableInterceptor();
Test1 test = new Test1();
Test1 proxy = generator.CreateClassProxyWithTarget<Test1>(test, interceptor);
在這種情況下,每次您使用proxy
執行某些操作時,都會調用CacheableInterceptor
。
CacheableInterceptor
的一個非常簡單的實現可能是
public class CacheableInterceptor : IInterceptor
{
private readonly Dictionary<String, String> _cache = new Dictionary<String, String>();
public void Intercept(IInvocation invocation)
{
// This code is a sample and should not be used in production
// performance optimization are available and is not thread safe
var pi = invocation.Method
.DeclaringType
.GetProperties()
.Where(p => p.GetCustomAttributes<CacheableAttribute>().Any())
.FirstOrDefault(p => p.GetGetMethod() == invocation.Method);
if (pi != null)
{
if (this._cache.TryGetValue(pi.Name, out var value))
{
invocation.ReturnValue = value;
}
else
{
invocation.Proceed();
value = (String)invocation.ReturnValue;
this._cache.Add(pi.Name, value);
}
}
}
}
當您完全了解動態代理的工作原理時。 我建議看看 Autofac 是如何工作的(沒有攔截等),然后將Autofac與DynamicProxy結合使用以使奇跡發生:autofac 可以為您創建代理和攔截器:請參閱Autofac - 類型攔截器
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.