使用 JUnit 测试的不同 Singleton 实例

发布于 2024-08-19 16:07:15 字数 82 浏览 3 评论 0原文

我有一个成功通过测试的独立单例。但通过一组测试,这会失败,因为一旦定义了单例,它就不允许重置实例。

关于如何解决这个问题有什么想法吗?

I have a standalone singleton which successfully passes the test. But with a group of tests this fails since once a singleton is defined it does not allow to reset the instance.

Any ideas about how to go about this?

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

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

发布评论

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

评论(8

幽梦紫曦~ 2024-08-26 16:07:15

我假设您的单例类中有一个私有静态字段来存储初始化的实例。

如果您不想修改代码,您可以定义一个在每次测试后运行的拆卸方法,并在该方法中通过反射将此静态字段设置为 null,如 此处

I assume you have a private static field within your singleton class to store the initialized instance.

If you do not want to modify your code, you can define a teardown method which run after every test, and in this method you set this static field to null via reflection as seen here.

怎会甘心 2024-08-26 16:07:15

不要使用单例。

具体来说,单例和全局变量之间的唯一区别是单例尝试强制执行单个实例(例如,通过将构造函数设为私有)。

相反,将构造函数公开并使用新实例编写测试。在您的实际程序中,使用 getInstance() 来获取规范的全局实例(或使用 IOC 容器)。

请记住单身人士是病态的骗子

如果您仍然对单例的想法感到太舒服,那么您可以添加一个公共(和静态)工厂方法来以不会意外使用的方式创建实例,而不是将构造函数公开,例如:

public static MyClass TEST_CreateInstance() {
  return new MyClass();
}

Don't use a singleton.

Specifically, the only difference between a singleton and a global variable is that the singleton tries to enforce a single instance (by making the constructor private, for example).

Instead, make the constructor public and write tests using new instances. In your actual program, use getInstance() to get the canonical global instance (or use an IOC container).

And remember that singletons are pathological liars.

If you're still too comfortable with the idea of a Singleton, instead of making the constructor public you can add a public (and static) factory method to create instances in a way that can't be used by accident, e.g.:

public static MyClass TEST_CreateInstance() {
  return new MyClass();
}
小耗子 2024-08-26 16:07:15

Spring provides the DirtiesContext annotation for this particular use case where you need new instances of the singleton beans for each testcase. It basically creates a new application context for each testcase/testclass which has this annotation applied.

沉睡月亮 2024-08-26 16:07:15

您可以添加一个方法来销毁单例,例如 destroyMe();您可以在其中取消初始化所有内容并将单例的实例设置为 null。

  public void destroyMe(){
   this.instance = null;
   //-- other stuff to turn it off.
}

不过,我会留下同步问题;)

但是为什么你需要为每个测试重新初始化你的单例?它不应该根据单例的概念而有所不同。

You can add a method to destroy the singleton, for example destroyMe(); where you deinitialize everything and set the instance of the singleton to null.

  public void destroyMe(){
   this.instance = null;
   //-- other stuff to turn it off.
}

I will leave synchronization problems though ;)

But why do you need to re-initialize your singleton for each test? It should not differ based on the concept of the singleton.

掩耳倾听 2024-08-26 16:07:15

强烈建议放弃单例作为设计模式,并使用单例作为范围(依赖注入)。这只会让你的问题消失。

但是假设您陷入了单例的世界,那么您有一些选择,具体取决于您是在测试单例还是依赖项。

如果您正在测试依赖项,则可以使用 PowerMockJMockIt。请参阅我关于模拟 Runtime.getRuntime 的上一篇文章,了解如何进行此操作的说明。

如果您正在测试单例,那么您需要放宽构造规则,或者为单例提供“重置”方法。

I highly recommend moving away from Singletons as a design pattern, and using Singleton as a scope (Dependency Injection). This would simply make your problem go away.

But assuming you are stuck in the world of Singletons, then you have a few options depending on if you are testing the Singleton or the dependency.

If you are testing the dependant item then you can mock the Singleton using PowerMock and JMockIt. See my previous post about mocking Runtime.getRuntime for instructions on how to go about this.

If you are testing the Singleton then you need to relax the rules on construction, or give the Singleton a "Reset" method.

伊面 2024-08-26 16:07:15

通常要小心单例,大多数情况下它们是邪恶的、糟糕的设计,并且往往代表大而令人讨厌的全局变量(这不利于维护)。

仍然要首先进行测试,您可以这样做:


static setInstance(...){ //package visibility or in difficult cases you have to use public
  instance = ...;
}

正如所说,这更像是一种解决方法。因此,先进行测试,然后从单例模式进行重构。

generally beware of singletons, most often they are evil, bad design and tend to represent big yucky global variables (which is bad for maintenance).

still to get tests in place first you can do:


static setInstance(...){ //package visibility or in difficult cases you have to use public
  instance = ...;
}

as said this is more a workaround. so get first tests place, but then refactor away from singleton pattern.

萌吟 2024-08-26 16:07:15

单例实例需要通过测试本身传递给 SUT - 这样您就可以为每个测试创建单例(并销毁)。采用 IoC 和模拟框架(如 Mockito)将使这种方法变得几乎微不足道。

Singleton instance needs to be passed to SUT by test itself - that way you create singleton (and destroy) for each test. Adopting IoC and mocking framework, like Mockito, would render this approach almost trivial.

你げ笑在眉眼 2024-08-26 16:07:15

参加聚会已经很晚了,但对于任何寻求答案的人来说,以防万一您不想/无法修改代码。

@BeforeEach
public void setup() {
    
    object = Singleton.getInstance();
}

@AfterEach
public void after() {
    // cleaning the singleton instance
    ReflectionTestUtils.setField(object , "internal_object_name", null);
}

你的 Singleton 类应该是这样的:

public final class Singleton {

private static Singleton internal_object_name;

private Singleton (){}
public static Singleton getInstance() {
    if (object == null)
         return new Singleton();
    else
         return internal_object_name;
 }

Very late to the party here, but for anyone looking for an answer, in case you don't want / cannot modify the code.

@BeforeEach
public void setup() {
    
    object = Singleton.getInstance();
}

@AfterEach
public void after() {
    // cleaning the singleton instance
    ReflectionTestUtils.setField(object , "internal_object_name", null);
}

your Singleton class should be something like this:

public final class Singleton {

private static Singleton internal_object_name;

private Singleton (){}
public static Singleton getInstance() {
    if (object == null)
         return new Singleton();
    else
         return internal_object_name;
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文