TestNG 是否保证 @BeforeSuite 方法在 @BeforeTest 方法之前执行?
背景:我的目标是编写一个运行独立的 TestNG-Selenium 系统(没有 Maven 或 Ant 插件的字符串;只有 Java)。它必须允许测试用例接受包括浏览器和域 URL 在内的参数。当 TestRunner 实例化这些测试用例时,浏览器和域用于获取 Selenium 对象来执行其测试。
问题:在尝试获取 Selenium 对象(在 @BeforeTest
中)之前,每个套件只有一个测试类成功获取域参数(在 @BeforeSuite
方法中)。不接收域的测试类具有 null
selenium 对象,因为它无法实例化。
代码:XmlClass 均包含在其自己的 XmlTest 中,并且所有三个类都位于单个 XmlSuite 中。该套件按 TestClass1、TestClass2、TestClass3 的顺序包含。测试类本身是 2 层抽象基类的子类,其中包括初始化注入变量并随后获取 Selenium 实例的功能。这样做的目的是测试一个或多个 应用程序(在多个域上)具有尽可能少的重复代码(即:Selenium 实例化位于根基类中,因为它对所有测试都是通用的)。详细请参阅以下方法。
// Top-most custom base class
abstract public class WebAppTestBase extends SeleneseTestBase
{
private static Logger logger = Logger.getLogger(WebAppTestBase.class);
protected static Selenium selenium = null;
protected String domain = null;
protected String browser = null;
@BeforeTest(alwaysRun = true)
@Parameters({ "selenium.browser" })
public void setupTest(String browser)
{
this.browser = browser;
logger.debug(this.getClass().getName()
+ " acquiring Selenium instance ('" + this.browser + " : " + domain + "').");
selenium = new DefaultSelenium("localhost", 4444, browser, domain);
selenium.start();
}
}
// Second level base class.
public abstract class App1TestBase extends WebAppTestBase
{
@BeforeSuite(alwaysRun = true)
@Parameters({"app1.domain" })
public void setupSelenium(String domain)
{
// This should execute for each test case prior to instantiating any Selenium objects in @BeforeTest
logger.debug(this.getClass().getName() + " starting selenium on domain '" + domain+ "'.");
this.domain = domain;
}
}
// Leaf level test class
public class TestClass1 extends App1TestBase
{
@Test
public void validateFunctionality() throws Exception
{
// Code for tests go here...
}
}
// Leaf level test class
public class TestClass2 extends App1TestBase
{
@Test
public void validateFunctionality() throws Exception
{
selenium.isElementPresent( ...
// Rest of code for tests go here...
// ....
}
}
// Leaf level test class
public class TestClass3 extends App1TestBase
{
@Test
public void validateFunctionality() throws Exception
{
// Code for tests go here...
}
}
输出:TestCase3 运行正确。 TestCase1 和 TestCase2 失败。生成堆栈跟踪...
10:08:23 [DEBUG RunTestCommand.java:63] - Running Tests.
10:08:23 [Parser] Running:
Command line suite
Command line suite
[DEBUG App1TestBase.java:49] - TestClass3 starting selenium on domain 'http://localhost:8080'.
10:08:24 [DEBUG WebAppTestBase.java:46] - TestClass2 acquiring Selenium instance ('*firefox : null').
10:08:24 [ERROR SeleniumCoreCommand.java:40] - Exception running 'isElementPresent 'command on session null
10:08:24 java.lang.NullPointerException: sessionId should not be null; has this session been started yet?
at org.openqa.selenium.server.FrameGroupCommandQueueSet.getQueueSet(FrameGroupCommandQueueSet.java:216)
at org.openqa.selenium.server.commands.SeleniumCoreCommand.execute(SeleniumCoreCommand.java:34)
at org.openqa.selenium.server.SeleniumDriverResourceHandler.doCommand(SeleniumDriverResourceHandler.java:562)
at org.openqa.selenium.server.SeleniumDriverResourceHandler.handleCommandRequest(SeleniumDriverResourceHandler.java:370)
at org.openqa.selenium.server.SeleniumDriverResourceHandler.handle(SeleniumDriverResourceHandler.java:129)
at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1530)
at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1482)
at org.openqa.jetty.http.HttpServer.service(HttpServer.java:909)
at org.openqa.jetty.http.HttpConnection.service(HttpConnection.java:820)
at org.openqa.jetty.http.HttpConnection.handleNext(HttpConnection.java:986)
at org.openqa.jetty.http.HttpConnection.handle(HttpConnection.java:837)
at org.openqa.jetty.http.SocketListener.handleConnection(SocketListener.java:245)
at org.openqa.jetty.util.ThreadedServer.handle(ThreadedServer.java:357)
at org.openqa.jetty.util.ThreadPool$PoolThread.run(ThreadPool.java:534)
感谢您提供有关此问题的任何信息。
BACKGROUND: My goal is to code a TestNG-Selenium system that runs self-contained (no strings to Maven or Ant plugins; just Java). It must allow for test cases to accept parameters including the browser and the domain URL. When the TestRunner
instantiates these test cases, the browser and domain are used to get a Selenium object to perform its testing.
PROBLEM: Only one test class per suite succeeds in getting the domain parameter (in a @BeforeSuite
method) before attempting to get a Selenium object (in a @BeforeTest
). The test classes that do not receive the domain have a null
selenium object b/c it can't be instantiated.
CODE: The XmlClasses are each contained within their own XmlTest and all three are in a single XmlSuite. The suite contains the in the order of TestClass1, TestClass2, then TestClass3. The test classes themselves are subclasses of 2 layers of abstract base classes that includes functionality to initialize injected variables and subsequently get an instance of Selenium. The purpose for this is to test one or multiple
applications (on multiple domains) with as little repeated code as possible (ie: Selenium instantiation is in the root base class because it's common to all tests). See the methods below for details.
// Top-most custom base class
abstract public class WebAppTestBase extends SeleneseTestBase
{
private static Logger logger = Logger.getLogger(WebAppTestBase.class);
protected static Selenium selenium = null;
protected String domain = null;
protected String browser = null;
@BeforeTest(alwaysRun = true)
@Parameters({ "selenium.browser" })
public void setupTest(String browser)
{
this.browser = browser;
logger.debug(this.getClass().getName()
+ " acquiring Selenium instance ('" + this.browser + " : " + domain + "').");
selenium = new DefaultSelenium("localhost", 4444, browser, domain);
selenium.start();
}
}
// Second level base class.
public abstract class App1TestBase extends WebAppTestBase
{
@BeforeSuite(alwaysRun = true)
@Parameters({"app1.domain" })
public void setupSelenium(String domain)
{
// This should execute for each test case prior to instantiating any Selenium objects in @BeforeTest
logger.debug(this.getClass().getName() + " starting selenium on domain '" + domain+ "'.");
this.domain = domain;
}
}
// Leaf level test class
public class TestClass1 extends App1TestBase
{
@Test
public void validateFunctionality() throws Exception
{
// Code for tests go here...
}
}
// Leaf level test class
public class TestClass2 extends App1TestBase
{
@Test
public void validateFunctionality() throws Exception
{
selenium.isElementPresent( ...
// Rest of code for tests go here...
// ....
}
}
// Leaf level test class
public class TestClass3 extends App1TestBase
{
@Test
public void validateFunctionality() throws Exception
{
// Code for tests go here...
}
}
OUTPUT: TestCase3 runs correctly. TestCase1 and TestCase2 fails. Stack trace gets generated...
10:08:23 [DEBUG RunTestCommand.java:63] - Running Tests.
10:08:23 [Parser] Running:
Command line suite
Command line suite
[DEBUG App1TestBase.java:49] - TestClass3 starting selenium on domain 'http://localhost:8080'.
10:08:24 [DEBUG WebAppTestBase.java:46] - TestClass2 acquiring Selenium instance ('*firefox : null').
10:08:24 [ERROR SeleniumCoreCommand.java:40] - Exception running 'isElementPresent 'command on session null
10:08:24 java.lang.NullPointerException: sessionId should not be null; has this session been started yet?
at org.openqa.selenium.server.FrameGroupCommandQueueSet.getQueueSet(FrameGroupCommandQueueSet.java:216)
at org.openqa.selenium.server.commands.SeleniumCoreCommand.execute(SeleniumCoreCommand.java:34)
at org.openqa.selenium.server.SeleniumDriverResourceHandler.doCommand(SeleniumDriverResourceHandler.java:562)
at org.openqa.selenium.server.SeleniumDriverResourceHandler.handleCommandRequest(SeleniumDriverResourceHandler.java:370)
at org.openqa.selenium.server.SeleniumDriverResourceHandler.handle(SeleniumDriverResourceHandler.java:129)
at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1530)
at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1482)
at org.openqa.jetty.http.HttpServer.service(HttpServer.java:909)
at org.openqa.jetty.http.HttpConnection.service(HttpConnection.java:820)
at org.openqa.jetty.http.HttpConnection.handleNext(HttpConnection.java:986)
at org.openqa.jetty.http.HttpConnection.handle(HttpConnection.java:837)
at org.openqa.jetty.http.SocketListener.handleConnection(SocketListener.java:245)
at org.openqa.jetty.util.ThreadedServer.handle(ThreadedServer.java:357)
at org.openqa.jetty.util.ThreadPool$PoolThread.run(ThreadPool.java:534)
I appreciate any information you may have on this issue.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为问题在于您的 @BeforeSuite 方法正在为字段分配值,但您有三个不同的实例,因此其他两个永远不会初始化。
请记住,
@BeforeSuite
只运行一次,无论它属于哪个类。因此,@Before/AfterSuite
方法通常在整个测试环境之外的类上定义。这些方法实际上应该是静态的,但我决定不强制执行此要求,因为它有时不切实际。我认为解决问题的更好方法是将域字段视为每个测试将从 Guice 或其他依赖项注入框架接收的注入资源。
I think the problem is that your
@BeforeSuite
method is assigning a value to a field, but you have three different instances, so the other two never get initialized.Remember that
@BeforeSuite
is only run once, regardless of what class it belongs to. As such,@Before/AfterSuite
methods are usually defined on classes that are outside the entire test environment. These methods should really bestatic
but I decided not to enforce this requirement because it's sometimes impractical.I think a better way to approach your problem is to look at your domain field as an injected resource that each of your test will receive from Guice or other dependency injection framework.