使用 IsAssignableFrom 返回 class 接口类型时序列不包含匹配元素错误
Sequence contains no matching element error when returning class Type of Interface using IsAssignableFrom
这是我围绕实施 UI 测试解决方案创建的另一个 post 的后续问题,该解决方案可以根据接口切换哪些 class 来执行代码。整个目标是在相同版本的应用程序(Web 与 WPF)上重新使用测试代码。
代码编译正常,但在测试 运行 之后,它在 GetPageModelType 方法调用中爆炸。下面是我的实现与链接 post 几乎相同,有一些小的调整来抽象一些在 TestClassBase
上创建的页面对象
接口和相应的页面对象classes
public interface ILogin
{
void Login(string username, string password);
}
public class WebLogin : ILogin
{
private readonly IWebDriver driver;
public WebLogin(IWebDriver driver)
{
this.driver = driver;
}
public void Login(string username, string password)
{
Console.WriteLine("Web Success!");
}
}
public class WPFLogin : ILogin
{
private readonly WindowsDriver<WindowsElement> session;
public WPFLogin(WindowsDriver<WindowsElement> session)
{
this.session = session;
}
public void Login(string username, string password)
{
Console.WriteLine("WPF Success!");
}
}
页面对象工厂classes
public interface IPageModelFactory
{
ILogin CreateLogin();
}
public class WebPageModelFactory : IPageModelFactory
{
private readonly IWebDriver driver;
public WebPageModelFactory(IWebDriver driver)
{
this.driver = driver;
}
public ILogin CreateLogin()
{
return new WebLogin(driver);
}
}
public class WPFPageModelFactory : IPageModelFactory
{
private readonly WindowsDriver<WindowsElement> session;
public WPFPageModelFactory(WindowsDriver<WindowsElement> session)
{
this.session = session;
}
public ILogin CreateLogin()
{
return new WPFLogin(session);
}
}
public class PageModelFactory
{
private readonly object client;
public PageModelFactory(object client)
{
this.client = client;
}
// Create Page Objects
public ILogin CreateLoginPage()
{
var pageModelType = GetPageModelType<ILogin>();
var constructor = pageModelType.GetConstructor(new Type[] { client.GetType() });
return (ILogin)constructor.Invoke(new object[] { client });
}
private Type GetPageModelType<TPageModelInterface>()
{
return client.GetType().Assembly.GetTypes().Single(type => type.IsClass && typeof(TPageModelInterface).IsAssignableFrom(type));
}
}
TestClassBase - 用于测试的基础 class,简化了测试脚本
[TestFixture]
public class TestClassBase
{
// WinAppDriver variables
private static string WinAppDriverExe = "C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe";
private string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
// Sessions
public WindowsDriver<WindowsElement> session;
public IWebDriver driver;
// Declare Page Objects
public ILogin login = null;
[SetUp]
public void SetUp()
{
if (GlobalData.targetHost.Equals("WPF"))
{
// Capabilities
AppiumOptions appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("app", GetExeFile());
appCapabilities.AddAdditionalCapability("appWorkingDir", GetWorkingDirectory());
// Create Session
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities, TimeSpan.FromMinutes(3));
session.Manage().Window.Maximize();
// Pass session to page objects
PageModelFactory wpfPages = new PageModelFactory(session);
login = wpfPages.CreateLoginPage();
} else if (GlobalData.targetHost.Equals("Web"))
{
}
}
[TearDown]
public void TearDown()
{
// Clean up code...
}
}
登录测试
public class LoginTests : TestClassBase
{
[Test]
public void Login()
{
// Login
login.Login("", "");
}
}
上面没有显示的是我的 GlobalData.cs class,它只包含一堆在测试中使用的硬编码变量。在针对 WPF 主机进行测试时,我将 targetHost 变量设置为“WPF”。 StartUp 代码确实按预期启动了应用程序,当我们在 PageModelFactory.CreateLoginPage();
上调用 GetPageModelType 时它失败了
我在对您的原始问题的回答中没有看到这一点。 “客户端”所在的程序集和页面模型所在的程序集是不同的。这意味着 PageModelFactory 将需要第二个构造函数参数来知道在初始化新页面模型时要搜索哪个程序集:
public class PageModelFactory
{
private readonly object client;
private Assembly Assembly => GetType().Assembly;
public PageModelFactory(object client)
{
this.client = client;
}
// Create Page Objects
public ILogin CreateLoginPage()
{
var pageModelTypes = GetPageModelTypes<ILogin>();
var constructorSignature = new Type[] { client.GetType() };
foreach (var type in pageModelTypes)
{
var constructor = type.GetConstructor(constructorSignature);
if (constructor != null)
return (ILogin)constructor.Invoke(new object[] { client });
}
throw new InvalidOperationException($"No class found implementing ILogin with a constructor that accepts {client.GetType().FullName} as an argument in assembly {Assembly.Name}");
}
private IEnumerable<Type> GetPageModelTypes<TPageModelInterface>()
{
return Assembly.GetTypes()
.Where(type => type.IsClass
&& typeof(TPageModelInterface).IsAssignableFrom(type));
}
}
这是我围绕实施 UI 测试解决方案创建的另一个 post 的后续问题,该解决方案可以根据接口切换哪些 class 来执行代码。整个目标是在相同版本的应用程序(Web 与 WPF)上重新使用测试代码。
代码编译正常,但在测试 运行 之后,它在 GetPageModelType 方法调用中爆炸。下面是我的实现与链接 post 几乎相同,有一些小的调整来抽象一些在 TestClassBase
上创建的页面对象接口和相应的页面对象classes
public interface ILogin
{
void Login(string username, string password);
}
public class WebLogin : ILogin
{
private readonly IWebDriver driver;
public WebLogin(IWebDriver driver)
{
this.driver = driver;
}
public void Login(string username, string password)
{
Console.WriteLine("Web Success!");
}
}
public class WPFLogin : ILogin
{
private readonly WindowsDriver<WindowsElement> session;
public WPFLogin(WindowsDriver<WindowsElement> session)
{
this.session = session;
}
public void Login(string username, string password)
{
Console.WriteLine("WPF Success!");
}
}
页面对象工厂classes
public interface IPageModelFactory
{
ILogin CreateLogin();
}
public class WebPageModelFactory : IPageModelFactory
{
private readonly IWebDriver driver;
public WebPageModelFactory(IWebDriver driver)
{
this.driver = driver;
}
public ILogin CreateLogin()
{
return new WebLogin(driver);
}
}
public class WPFPageModelFactory : IPageModelFactory
{
private readonly WindowsDriver<WindowsElement> session;
public WPFPageModelFactory(WindowsDriver<WindowsElement> session)
{
this.session = session;
}
public ILogin CreateLogin()
{
return new WPFLogin(session);
}
}
public class PageModelFactory
{
private readonly object client;
public PageModelFactory(object client)
{
this.client = client;
}
// Create Page Objects
public ILogin CreateLoginPage()
{
var pageModelType = GetPageModelType<ILogin>();
var constructor = pageModelType.GetConstructor(new Type[] { client.GetType() });
return (ILogin)constructor.Invoke(new object[] { client });
}
private Type GetPageModelType<TPageModelInterface>()
{
return client.GetType().Assembly.GetTypes().Single(type => type.IsClass && typeof(TPageModelInterface).IsAssignableFrom(type));
}
}
TestClassBase - 用于测试的基础 class,简化了测试脚本
[TestFixture]
public class TestClassBase
{
// WinAppDriver variables
private static string WinAppDriverExe = "C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe";
private string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
// Sessions
public WindowsDriver<WindowsElement> session;
public IWebDriver driver;
// Declare Page Objects
public ILogin login = null;
[SetUp]
public void SetUp()
{
if (GlobalData.targetHost.Equals("WPF"))
{
// Capabilities
AppiumOptions appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("app", GetExeFile());
appCapabilities.AddAdditionalCapability("appWorkingDir", GetWorkingDirectory());
// Create Session
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities, TimeSpan.FromMinutes(3));
session.Manage().Window.Maximize();
// Pass session to page objects
PageModelFactory wpfPages = new PageModelFactory(session);
login = wpfPages.CreateLoginPage();
} else if (GlobalData.targetHost.Equals("Web"))
{
}
}
[TearDown]
public void TearDown()
{
// Clean up code...
}
}
登录测试
public class LoginTests : TestClassBase
{
[Test]
public void Login()
{
// Login
login.Login("", "");
}
}
上面没有显示的是我的 GlobalData.cs class,它只包含一堆在测试中使用的硬编码变量。在针对 WPF 主机进行测试时,我将 targetHost 变量设置为“WPF”。 StartUp 代码确实按预期启动了应用程序,当我们在 PageModelFactory.CreateLoginPage();
上调用 GetPageModelType 时它失败了我在对您的原始问题的回答中没有看到这一点。 “客户端”所在的程序集和页面模型所在的程序集是不同的。这意味着 PageModelFactory 将需要第二个构造函数参数来知道在初始化新页面模型时要搜索哪个程序集:
public class PageModelFactory
{
private readonly object client;
private Assembly Assembly => GetType().Assembly;
public PageModelFactory(object client)
{
this.client = client;
}
// Create Page Objects
public ILogin CreateLoginPage()
{
var pageModelTypes = GetPageModelTypes<ILogin>();
var constructorSignature = new Type[] { client.GetType() };
foreach (var type in pageModelTypes)
{
var constructor = type.GetConstructor(constructorSignature);
if (constructor != null)
return (ILogin)constructor.Invoke(new object[] { client });
}
throw new InvalidOperationException($"No class found implementing ILogin with a constructor that accepts {client.GetType().FullName} as an argument in assembly {Assembly.Name}");
}
private IEnumerable<Type> GetPageModelTypes<TPageModelInterface>()
{
return Assembly.GetTypes()
.Where(type => type.IsClass
&& typeof(TPageModelInterface).IsAssignableFrom(type));
}
}