Java 如何销毁静态对象?-测试静态字段时出现问题

Java 如何销毁静态对象?-测试静态字段时出现问题,java,testing,junit,static,Java,Testing,Junit,Static,我想测试这个类,不想将id设置为public或允许它从外部设置它 public class A { private int id; private static int prevId = 0; private static nextId(){ prevId++; } public A(){ id = nextId() } public int getId(){ return id;

我想测试这个类,不想将id设置为public或允许它从外部设置它

public class A {
    private int id;
    private static int prevId = 0;

    private static nextId(){
        prevId++;
    }

    public A(){
        id = nextId()
    }

    public int getId(){
       return id;
    }

    ...
}
如果我测试它并在设置中生成对象:

A foo;

@Before
public void setUp(){
    foo = new A();
}

@Test
public void test1(){
   assertEquals(1, foo.getId());
}

@Test
public void test2(){
    assertEquals(1, foo.getId());
}
测试失败,因为安装程序被调用了两次,因此 静态字段id为2

断言不等于 预期1 实际2

如上所述,我不希望id带有公共访问修饰符, 否则我可以自己设置为0

有没有一种方法可以破坏一个静态对象在 拆卸方法

@After
public void tearDown(){
    //TODO finalize static object A 
}

非常简单-如果不提供重置
prevId
值的方法,您就不能。可测试性降低是反对可变全局状态的(许多)论据之一

您可以提供一个工厂类来创建
a
实例,而不是像这样依赖可变全局状态:

class AFactory {
  private int nextId;

  A createA() {
    return new A(nextId++);
  }
}
您将需要在当前需要创建
a
实例的任何位置注入
a工厂的单个实例。现在,在您的测试中,您只需为每个测试用例创建一个新的
AFactory
,并且
nextId
值每次都将从零开始


或者,您可以通过创建某种唯一的值提供程序类来反转控件:

final class UniqueValueProvider {
  private int nextId;

  int nextId() {
    return nextId++;
  }
}
然后将其作为
a
的构造函数的参数:

class A {
  final int id;

  A(UniqueValueProvider uvp) {
    this.id = uvp.nextId();
  }
}
class ChildOfA extends A {
  ChildOfA(UniqueValueProvider uvp) {
    super(uvp);
  }
}
如果您想任意地将
子类化为A
,则它的扩展性更强:

class A {
  final int id;

  A(UniqueValueProvider uvp) {
    this.id = uvp.nextId();
  }
}
class ChildOfA extends A {
  ChildOfA(UniqueValueProvider uvp) {
    super(uvp);
  }
}
同样,在测试中,您只需创建一个新的
UniqueValueProvider
;您需要在创建
a
(或其子类之一)所需的任何地方注入一个实例


作为旁注:您还可以更改正在测试的内容。您可以创建两个实例,并断言ID不同于/大于另一个实例,而不是每次断言ID为1,以此类推:

Foo first = new Foo();
Foo second = new Foo();
assertNotEqual(first.getId(), second.getId());

如果它确实与您描述的测试场景有关,只需在
@Before
方法中使用反射重置静态成员变量的值即可。如果您使用的是Mockito,您可以使用他们的
白盒
助手在一行中实现这一点,如下所示:

import org.mockito.internal.util.reflection.Whitebox;

Foo foo = new Foo();

@Before
public void setup() {
    Whitebox.setInternalState(foo, "STATIC_MEMBER", 0);
}

@Test
public void staticMemberTest() {
     assertEquals(0, foo.next());
     assertEquals(1, foo.next());
 }

@Test
public void anotherStaticMemberTest() {
     assertEquals(0, foo.next());
     assertEquals(1, foo.next());
 }  

private static class Foo {
    private static int STATIC_MEMBER = 0;

    public int next() {
        return STATIC_MEMBER++;
    }
}

非常简单-如果不提供重置
prevId
值的方法,您就不能。可测试性降低是反对可变静态的(许多)论据之一。您可以向对象添加finalize方法,这可以降低prevId,但在测试方法结束时,并不是强制窗体垃圾收集器调用它。@ByeBye依赖于要执行的终结器是一条痛苦的捷径-甚至不应该将其作为选项提及。如果您需要在某个时间执行某些内容,请显式执行它。在createA()中,您可以使用的构造函数来设置id,但我希望避免从外部设置id。因此,应该是一个工厂的内部阶级吗?我想你可以;或者使
AFactory
成为
A
的内部类。或者您可以将
A
包的构造函数设置为私有。那么在工厂中创建A的子类怎么样?添加进一步的CreateSublassb()方法?添加了一个可能对任意子类更有效的替代方法。