gpt4 book ai didi

java - JUnit并发访问synchronizedSet

转载 作者:行者123 更新时间:2023-11-30 02:47:14 25 4
gpt4 key购买 nike

我在服务器上运行 JUnit 测试时遇到问题。当我在我的机器上运行测试时,完全没有问题。当我在服务器上运行它时,我的所有服务器“有时”都会出现故障。这意味着有时 60% 的尝试会通过,而 40% 的测试会失败。

我正在使用 Mockito。我的测试从使用 MessageListener 模拟一些回复开始并将每个请求映射到响应,并在我正在使用的引擎盖下 Collections.synchronizedSet(new HashSet<>())这是线程安全的。(对我的synchronizedSet的每次修改都发生在 synchronized(mySynchronizedSet){....} 中)然后,我使用 RestAssurd获取特定 REST 端点的响应并断言一些值。

当测试失败并且我查看 Stacktrace 时,我发现我的一个映射(始终在同一个对象上)不起作用,并且我的集合中的该特定请求和响应之间没有映射,自然地,我获取null请求此端点时。

我正在使用 Jenkins 来自动编译和运行测试,并且我会在失败时获得堆栈跟踪或我的 Printlns,否则,没有可用的调试工具。

对我来说这听起来像是一个并发问题。我的意思是我的 Collection 似乎没有时间在 RestAssurd 之前做好准备对端点的请求。我已经测试了锁、 sleep 和另一个简单的 java 并发解决方案,但它们没有帮助,而且这个问题的概率特征使我陷入了死胡同。

每一个想法都会受到赞赏。

最佳答案

从您所说的来看,您似乎对 3 个具体情况下的工作原理存在误解。

第一

最明显的是,我什至为提及这一点而道歉,但我这样做的原因是因为我收集到你仍在学习(如果你没有仍在学习,我会进一步道歉!并且在同样的速度,你可能甚至没有用我阅读的方式暗示它,如果我读错了,很抱歉):你不是用 Jenkins 进行编译,而是用你的机器上有的任何 JDK 风格进行编译(无论是 Oracle,苹果、GCJ 等)。 Jenkins 是一个自动化工具,可以帮助您简化您希望定期运行的繁琐工作。我之所以提到这一点,是因为我知道现在的大学生在开课时都使用 IDE,无法区分编译器、运行时和 IDE。

其次

通过使用线程安全库,它不会自动使您所做的一切本质上都是线程安全的。考虑以下示例:

  final Map<Object, Object> foo = Collections.synchronizedMap(new HashMap <>());
final String bar = "bar";
foo.put(bar, new Object());
new Thread(new Runnable(){
@Override
public void run(){
foo.remove(bar);
}
}).start();
new Thread(new Runnable(){
@Override
public void run(){
if(foo.containsKey(bar)){
foo.get(bar).toString();
}
}
}).start();

无法保证第二个线程对 #get(Object) 的调用会在第一个线程对 #remove(Object) 的调用之前或之后发生。考虑一下

  1. 第二个线程可以调用#containsKey(Object)
  2. 然后第一个线程获得 CPU 时间并调用#remove(Object)
  3. 然后第二个线程现在拥有 CPU 时间并调用 #get(Object)

此时,get(Object) 的返回值为 null,并且调用 #toString() 将导致 NullPointerDereference。你说你正在使用Set,所以这个使用Map的例子主要是为了证明一点:仅仅因为你使用的是线程安全集合,并不会自动使你所做的一切都是线程安全的。我想你正在用你的集合做一些与这种行为相匹配的事情,但没有代码片段,我只能推测。

最后

您应该小心编写 JUnit 的方式。正确的 JUnit 测试就是所谓的“白盒”测试。换句话说,您知道测试中发生的所有事情,并且您正在显式测试仅在被测单元中发生的所有事情。被测试的单元只是您正在调用的方法 - 不是您的方法调用的方法,只有方法本身。这意味着您需要一个良好的模拟框架,并模拟被测单元可能调用的任何后续方法调用。一些不错的框架有JMockit、Mockito+PowerMock等。

这一点的重要性在于您的测试应该测试您的独立代码。如果您允许网络访问、磁盘访问等,那么您的测试可能会失败,并且它可能与您编写的代码无关,并且它会使测试完全无效。在你的情况下,你暗示网络访问,所以想象一下你的交换机/路由器/等存在一些吞吐量问题,或者你的NIC缓冲区已满并且无法足够快地处理你的程序正在尝试执行的操作。当然,失败是不好的,应该修复,但是应该在“黑盒”测试中进行测试。您应该编写测试,以便消除此类问题的出现,并且仅在被测单元的特定方法中测试您的代码,而不是其他任何东西。

编辑:我实际上发布了一个关于可能相关的白盒测试的单独讨论的答案:Is using a test entity manager a legitamate testing practice?

关于java - JUnit并发访问synchronizedSet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39834035/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com