如何在 Java 中访问私有类成员?

发布于 2024-08-02 00:05:11 字数 338 浏览 2 评论 0原文

我有包含私有字段的数据模型类,这些字段是只读的(通过 getter 函数)。 这些字段是由我的 JPA 持久性提供程序 (eclipselink) 在正常操作期间使用数据库的内容设置的。 对于单元测试,我想将它们设置为来自持久层模型的假值。 我怎样才能做到这一点? eclipselink 是如何设置这些值的呢?

简化示例:

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public Integer ixGet()
    {
        return this._ix;
    }
}

I have data model classes that contain private fields which are meant to be read-only (via a getter function). These fields are set by my JPA persistence provider (eclipselink) during normal operation, using the contents of the database. For unit tests, I want to set them to fake values from a mockup of the persistence layer. How can I do that? How does eclipselink set these values, anyway?

Simplified example:

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public Integer ixGet()
    {
        return this._ix;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

冬天的雪花 2024-08-09 00:05:12

我过去使用过的一些方法:

  • 让 _ix 受保护,创建一个子类,在其中实现 setter
  • 创建一个构造函数,将 _ix 的值作为参数
  • 使用反射

Some methods I've used in the past:

  • Make _ix protected, create a subclass where you implement a setter
  • Make a constructor taking the value for _ix as a parameter
  • Use reflection
沫离伤花 2024-08-09 00:05:12

如果您真的不想公开,另一种选择是创建一个子类进行测试,并在那里提供公共访问。

您有几个选择:

  • 创建存根来替换实体(首先提取接口)
  • 使用反射
  • 添加公共设置器以进行测试
  • 将测试保留在包内并使用默认范围

对于一堆有用的技术,请查看 Michael Feather 的书,有效地使用旧代码

Another option, if you really hate to make things public, is to create a subclass for testing, and provide public access there.

You have a few options:

  • Create stubs to replace your entity (extract an interface first)
  • Use Reflection
  • Add a public setter for testing
  • Keep your tests within the package and use a default scope

For a bunch of useful techniques, have a look at Michael Feather's book, Working Effectively With Legacy Code

尴尬癌患者 2024-08-09 00:05:12

您可以为只读变量添加带参数的构造函数。 不要忘记添加默认(零参数)构造函数。

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public MyEntity(Integer ix) {
        _ix = ix;
    }

    public MyEntity() {
        /*
         * Default constructor
         */
    }

    public Integer ixGet()
    {
        return this._ix;
    }
}

You can add constructor with parameter for your read-only variable. Don't forget to add a default (zero parameter) constructor.

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public MyEntity(Integer ix) {
        _ix = ix;
    }

    public MyEntity() {
        /*
         * Default constructor
         */
    }

    public Integer ixGet()
    {
        return this._ix;
    }
}
黎夕旧梦 2024-08-09 00:05:12

构造函数是我认为最好的方式。 如果这个实体必须是真正只读的(根本不允许在生产代码中创建新实例),您可以创建具有包访问权限的构造函数并仅在测试中使用它。 并且有可能即使您将默认构造函数设置为私有或具有包访问权限,您的持久性提供程序仍然能够使用此类实体,但不确定 - 请检查 eclipselink 文档。

The constructor is a best way I think. If this entity has to be really readonly (not allowed to create new instances in production code at all) you can make constructor with package access and use it only within the tests. And there is a possibility that even if you make your default constructor private or with package access, your persistance provider still be able to work with such entity, but not sure though - check with eclipselink docs.

坚持沉默 2024-08-09 00:05:11

您可以模拟实体本身,提供您自己的 getter 实现吗?

您可以在模拟持久层中创建匿名扩展:

MyEntity x = new MyEntity() {
    public Integer ixGet() { return new Integer(88); }
};

Can you just Mock the Entity itself, providing your own implemenations of the getters?

You could create an anonymous extension in your mock persistence layer:

MyEntity x = new MyEntity() {
    public Integer ixGet() { return new Integer(88); }
};
℉絮湮 2024-08-09 00:05:11

您需要使用反射 API。 使用 Class.getField() 获取该字段,然后对该字段调用 setAccessable(true) 以便您可以写入该字段,即使它是私有的,最后您可以对其调用 set() 写入新值。

例如:

public class A {
    private int i;
}

您希望将字段“i”设置为 3,即使它是私有的:

void forceSetInt(Object o, String fieldName, int value) {
    Class<?> clazz = o.getClass();
    Field field = clazz.getDeclaredField(fieldName);
    field.setAccessible(true);
    field.set(o, value);
}

您需要处理许多异常。

You need to use the Reflection API. Use Class.getField() to get the field, then call setAccessable(true) on that field so that you may write to it, even though it is private, and finally you may call set() on it to write a new value.

For example:

public class A {
    private int i;
}

You want to set the field 'i' to 3, even though it is private:

void forceSetInt(Object o, String fieldName, int value) {
    Class<?> clazz = o.getClass();
    Field field = clazz.getDeclaredField(fieldName);
    field.setAccessible(true);
    field.set(o, value);
}

There are a number of exceptions that you will need to handle.

风追烟花雨 2024-08-09 00:05:11

您可以使用 Mockito 等测试库以读写模式访问对象内部状态。 例如,使用 Mockito 时:

//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write 
Whitebox.setInternalState(myEntity,"_ix", 123) 

You can use a test library like Mockito to access objects internal state in read and write mode. For example with Mockito use:

//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write 
Whitebox.setInternalState(myEntity,"_ix", 123) 
凡间太子 2024-08-09 00:05:11

您可以使用 powermock 之类的模拟框架来绕过封装。 在 powermock 中,您可以使用 Whitebox.setInternalState(..) 设置私有成员。

一种侵入性较小的方法是模拟 getter 方法。 这是否可行取决于内部状态的其他因素,但如果足够,那么它就是更干净的解决方案。

You can use a mocking framework like powermock to by pass encapsulation. In powermock you'd use Whitebox.setInternalState(..) to set a private member.

A less invasive method would be to mock the getter method. Whether this is feasible would depend on what else depends on the internal state but if it is enough, it's the cleaner solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文