【问题标题】:Cannot find classes in a package using Class.forName()使用 Class.forName() 在包中找不到类
【发布时间】:2018-06-14 15:16:02
【问题描述】:

使用 Java 我正在实现一个用于硒测试的页面工厂对象,该对象采用页面对象的名称并通过反射将其实例化以供 Cucumber 步骤定义使用。我遇到的问题是下面的代码找不到声明的类。包含此代码的对象PageFactory 和页面对象LoginPage 都驻留在一个名为pages 的包中。

 /**
 * This method take a string containing a Page Object class name (case-sensitive) and returns an instance of the Page Object.
 * This allows us to operate on pages without knowing they exist when we write step definitions.
 * @param choice String
 * @return Page Object cast as a Page
 */
public static Page getPage(String choice) {
    Page entity = null;

    try {
        entity = (Page) Class.forName(choice).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return entity;
}

我收到一个以java.lang.ClassNotFoundException: LoginPage 作为错误开头的堆栈跟踪。如果我将实体创建更改为以下代码,那么它可以工作。

        private static String packageName = "pages";
        entity = (Page) Class.forName(packageName + "." + choice).newInstance();

问题是我想组织我的页面。当我创建pages.mywebsite 并将LoginPage 放入其中时,PageFactory 将不知道在哪里可以找到该文件。

撇开我可以有两个命名空间pages.mywebsitepages.myotherwebsite 的问题不谈,它们都有一个LoginPage 对象,我怎样才能在不声明确切包的情况下找到我想要的文件,然后说“看看这个包和下面的类”?

【问题讨论】:

  • 我想,Page.forName(…) 应该是Class.forName(…)?除此之外,你希望谁来决定pages.mywebsite.LoginPagepages.myotherwebsite.LoginPage 是正确的班级?神奇的独角兽?
  • 编辑了上面的代码。我实际上创建了自己的 forName() 方法来尝试解决我的问题。但出于本次讨论的目的,我将重新使用默认值。至于谁来决定,我认为解决方案是根据加载的现有页面对象通过目录结构来完成 - 保持在同一级别。
  • 那么……existingPage.getClass().getPackage().getName()+'.'+choice 还是我错过了什么?
  • 没有现有的页面对象。我有 Page.forName() 因为我有一个我创建的静态 forName() 函数。我有一个循环引用问题。在知道包之前无法实例化页面对象,在创建页面对象之前无法知道包。这个想法是其他人将创建页面对象并将它们放在pages 包中的文件夹中,但我不知道它们或它们的文件夹结构。也许我应该以不同的方式来做这件事?
  • 在您之前的评论中,您说“我认为解决方案是根据已加载的现有页面对象通过目录结构来完成 - 保持在同一级别” ,所以我很惊讶你现在说没有现有的页面对象,如果没有现有的页面对象,那么“保持在同一级别”是什么意思?和什么一样?

标签: java selenium-webdriver reflection jvm cucumber


【解决方案1】:

您可以使用System.getProperty("java.class.path") 获取类路径,将其拆分为File.pathSeparator,然后使用FileVisitor 扫描结果。

【讨论】:

  • 我会试一试,然后告诉你进展如何。谢谢!
【解决方案2】:

这就是我解决问题的方法。我将反射移动到 findPageInPackage 方法中,并递归调用它以搜索目录。

完整代码在这里:https://github.com/dougnoel/sentinel/blob/master/src/main/java/com/dougnoel/sentinel/pages/PageFactory.java

/**
 * Returns a page object if it exists in the package searched.
 * @param pageName String the name of the page object class to instantiate
 * @param packageName String the name of the package to search
 * @return Page the page object if it exists, otherwise null
 */
private static Page findPageInPackage(String pageName, String packageName) {
    Page page = null;
    try {
        page = (Page) Class.forName(packageName + "." + pageName).newInstance();
    } catch (InstantiationException e) {
        log.trace("{}.{} Page Object creation failed.", packageName, pageName);
        log.trace("java.lang.InstantiationException: {}", e.getMessage());
    } catch (IllegalAccessException e) {
        log.trace("{}.{} Page Object creation failed.", packageName, pageName);
        log.trace("java.lang.IllegalAccessException: {}", e.getMessage());
    } catch (ClassNotFoundException e) {
        log.trace("{}.{} Page Object creation failed.", packageName, pageName);
        log.trace("java.lang.ClassNotFoundException: {}", e.getMessage());
    }

    return page;
}

/**
 * Returns the Page Object for the page name. This allows us to operate on pages
 * without knowing they exist when we write step definitions.
 * <p>
 * Searches any optional page object packages set with the pageObjectPackages system property, and 
 * then searches the defaultPackageName value.
 * <p>
 * <b>Example:</b>
 * <p>
 * <code>System.setProperty("pageObjectPackages", "pages.SharedComponent,pages.UserAdminTool");</code>
 * 
 * @param pageName String the name of the page in
 *                 <a href="https://en.wikipedia.org/wiki/Camel_case">Pascal
 *                 case</a>
 * @return Page the specific page object cast as a generic page object
 * @throws PageNotFoundException if page could not be built or retrieved.
 * @throws ConfigurationNotFoundException if the value is not found in the configuration file
 */
public static Page buildOrRetrievePage(String pageName) throws PageNotFoundException, ConfigurationNotFoundException {
    Page page = pages.get(pageName);
    final String errorMessage = "The page you want to test could not be built. At least one Page object package is required to run a test. Please add a pageObjectPackages property to your conf/sentinel.yml configuration file and try again.";
    if (page != null) {
        return page;
    } else {
        if (pageObjectPackagesList == null) {
            pageObjectPackagesList = ConfigurationManager.getPageObjectPackageList();
            if(pageObjectPackagesList == null) {
                throw new PageNotFoundException(errorMessage);
            }
        }

        for (String pageObjectPackage : pageObjectPackagesList) {
            log.trace("pageObjectPackage: " + pageObjectPackage);
            page = findPageInPackage(pageName, pageObjectPackage);
            if (page != null) {
                break; // If we have a page object, stop searching.
            }
        }
    }
    if(page == null) {
        throw new PageNotFoundException(errorMessage);
    }
    pages.put(pageName, page);
    return page;
}

【讨论】:

    猜你喜欢
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    相关资源
    最近更新 更多