Java JUnit测试只有在我逐个运行时才通过。线程可能不正确
UPD:有标记的答案解决了这个问题,请参阅它的注释以获得小的修复 首先,请原谅我对代码的残杀。这个问题让我发疯,我尝试了很多方法来确定问题的根源,但是失败了 我有一个SpringBootWeb应用程序,它有FMonitor服务。当某个文件夹中出现扩展名为“.done”的文件时,它会在应用程序运行时工作并通知我。当我运行这个应用程序的时候,它工作得非常完美(至少看起来是这样) 我决定练习测试,并抛出了三个简单的测试。当我一次一个地运行它们时,它们工作得非常成功。然而,当我运行整个类的测试时,只有前一个通过,其他两个失败 我的测试检查系统,并将其与所需的输出进行比较。这是我最奇怪的事情。事情就是这样 第一次测试通过后,FMMonitor的输出与assertEquals中的完全相同。 找到test1 找到测试2 找到测试3 第二次测试失败。由于某些原因,产量奇怪地翻了一番: 找到test1 找到test1 找到测试3 找到测试3 然后第三个失败了。现在产量是原来的三倍: 发现试验 发现试验 发现试验 我的猜测是,我对线程做了一些完全错误的操作,因此fm.monitor()以某种方式捕获了所有事件以及类似的内容。我很困惑。我在这里尝试了很多关于如何实现线程的方法,我不太擅长,但它仍然可以正常工作。我还认为@Async annotation for monitor()可能会搞乱一些事情,但删除它并没有改变任何事情。请帮忙 BunchOfTestsJava JUnit测试只有在我逐个运行时才通过。线程可能不正确,java,spring,multithreading,spring-boot,junit,Java,Spring,Multithreading,Spring Boot,Junit,UPD:有标记的答案解决了这个问题,请参阅它的注释以获得小的修复 首先,请原谅我对代码的残杀。这个问题让我发疯,我尝试了很多方法来确定问题的根源,但是失败了 我有一个SpringBootWeb应用程序,它有FMonitor服务。当某个文件夹中出现扩展名为“.done”的文件时,它会在应用程序运行时工作并通知我。当我运行这个应用程序的时候,它工作得非常完美(至少看起来是这样) 我决定练习测试,并抛出了三个简单的测试。当我一次一个地运行它们时,它们工作得非常成功。然而,当我运行整个类的测试时,只有前
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.*;
import java.util.concurrent.Executor;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FMonitor.class)
public class BunchOfTests {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private Executor executor;
@Autowired
FMonitor fm;
@Test
public void test1() throws InterruptedException, IOException {
Runnable task = () -> {
System.setOut(new PrintStream(outContent));
fm.monitor();
System.setOut(System.out);
};
executor = (runnable) -> new Thread(runnable).start();
executor.executeTask(task);
Thread.sleep(3000);
File file1 = new File("C:\\dir\\test1.done");
File file2 = new File("C:\\dir\\test2.done");
File file3 = new File("C:\\dir\\test3.done");
file1.createNewFile();
file2.createNewFile();
file3.createNewFile();
Thread.sleep(3000);
file1.delete();
file2.delete();
file3.delete();
Assert.assertEquals("Found test1\r\n" + "Found test2\r\n" + "Found test3\r\n", outContent);
}
@Test
public void test2() throws InterruptedException, IOException {
Runnable task = () -> {
System.setOut(new PrintStream(outContent));
fm.monitor();
System.setOut(System.out);
};
executor = (runnable) -> new Thread(runnable).start();
executor.executeTask(task);
Thread.sleep(3000);
File file1 = new File("C:\\dir\\test1.done");
File file2 = new File("C:\\dir\\test2.txt");
File file3 = new File("C:\\dir\\test3.done");
file1.createNewFile();
file2.createNewFile();
file3.createNewFile();
Thread.sleep(3000);
file1.delete();
file2.delete();
file3.delete();
Assert.assertEquals("Found test1\r\n" + "Found test3\r\n", outContent);
}
@Test
public void test3() throws InterruptedException, IOException {
Runnable task = () -> {
System.setOut(new PrintStream(outContent));
fm.monitor();
System.setOut(System.out);
};
executor = (runnable) -> new Thread(runnable).start();
executor.executeTask(task);
Thread.sleep(3000);
File file = new File("C:\\dir\\test.done");
file.createNewFile();
Thread.sleep(3000);
file.delete();
Assert.assertEquals("Found test\r\n", outContent);
}
}
FMonitor
import org.springframework.stereotype.Service;
import org.srpingframework.scheduling.annotation.Async;
import java.io.IOException;
import java.nio.file.*;
@Service
public class FMonitor {
@Async("fMonitor")
public void monitor() {
Path path = Paths.get("C:\\dir");
try {
WatchService watchService = FileSystems.getDefault.newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event: key.pollEvents()) {
String filename = event.context().toString();
if (filename.endsWith(".done")) {
processFile(filename.substring(0, filename.lastIndexOf('.')));
}
}
key.reset();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private void processFile(String filename) {
System.out.println("Found " + filename);
}
}
首先,您应该在完成后完全停止
监视服务
。实现一个这样做的方法,并使用@PreDestroy
进行注释
@Service
public class FMonitor {
private final WatchService watchService = FileSystems.getDefault.newWatchService();
@Async("fMonitor")
public void monitor() {
Path path = Paths.get("C:\\dir");
try {
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event: key.pollEvents()) {
String filename = event.context().toString();
if (filename.endsWith(".done")) {
processFile(filename.substring(0, filename.lastIndexOf('.')));
}
}
key.reset();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private void processFile(String filename) {
System.out.println("Found " + filename);
}
@PreDestroy
public void shutdown() {
try {
watchService.close();
} catch (IOException ex) {}
}
}
类似这样的操作或多或少应该满足您的需要。使用适当的执行器,在测试结束后关闭它,以便停止线程。@M.Deinum我尝试过,没有帮助您尝试过什么?我也不太明白你的测试。为什么您在执行器中执行任务,而Spring Boot已经在线程中执行该任务。。。。每次调用
monitor
时,它都会启动一个新的监视器。将一个添加到任务列表中,因为只有一个应用程序。因此,您将完成多个任务,每个测试一个任务。因此,第一个执行1个任务,第二个执行2个任务,第三个执行3个任务。您的测试中也不需要执行器。@M.Deinum我尝试过使用“带关机的正确执行器”。执行人服务。如果您知道如何正确地执行,请随意分享。如前所述,问题是您正在对spring管理的bean调用一个方法。每次调用monitor
时,它都会安排一个新任务。每个测试添加一个。你不需要遗嘱执行人。我认为您的FMonitor
稍有错误。您应该将WatchService
存储为实例变量,并添加一个用@PreDestroy
注释的方法。测试完成后,调用此方法清理观察者。非常感谢。这有帮助。有一个拼写错误,您在测试类的@After中构造do时忘记了do的“do{/code/}”。同样对于assertEquals,我将简单的“output”改为“output.toString()”。如果有人试图运行你的示例,那么没有遗漏的操作。它只是一个空的while循环,将在执行器终止时等待。
@Service
public class FMonitor {
private final WatchService watchService = FileSystems.getDefault.newWatchService();
@Async("fMonitor")
public void monitor() {
Path path = Paths.get("C:\\dir");
try {
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event: key.pollEvents()) {
String filename = event.context().toString();
if (filename.endsWith(".done")) {
processFile(filename.substring(0, filename.lastIndexOf('.')));
}
}
key.reset();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private void processFile(String filename) {
System.out.println("Found " + filename);
}
@PreDestroy
public void shutdown() {
try {
watchService.close();
} catch (IOException ex) {}
}
}
public class BunchOfTests {
@Rule
public OutputCaptureRule output = new OutputCaptureRule();
private ExecutorService executor = Executors.newSingleThreadExecutor();
private final FMonitor fm = new FMonitor();
@After
public void cleanUp() throws Exception {
fm.shutdown();
executor.shutdown();
while (!executor.awaitTermination(100, TimeUnit.MICROSECONDS));
}
@Test
public void test1() throws InterruptedException, IOException {
executor.submit(() -> fm.monitor());
Thread.sleep(3000);
File file1 = new File("C:\\dir\\test1.done");
File file2 = new File("C:\\dir\\test2.done");
File file3 = new File("C:\\dir\\test3.done");
file1.createNewFile();
file2.createNewFile();
file3.createNewFile();
Thread.sleep(3000);
file1.delete();
file2.delete();
file3.delete();
Assert.assertEquals("Found test1\r\n" + "Found test2\r\n" + "Found test3\r\n", output.toString());
}
@Test
public void test2() throws InterruptedException, IOException {
executor.submit(() -> fm.monitor());
Thread.sleep(3000);
File file1 = new File("C:\\dir\\test1.done");
File file2 = new File("C:\\dir\\test2.txt");
File file3 = new File("C:\\dir\\test3.done");
file1.createNewFile();
file2.createNewFile();
file3.createNewFile();
Thread.sleep(3000);
file1.delete();
file2.delete();
file3.delete();
Assert.assertEquals("Found test1\r\n" + "Found test3\r\n", output.toString());
}
@Test
public void test3() throws InterruptedException, IOException {
executor.submit(() -> fm.monitor());
Thread.sleep(3000);
File file = new File("C:\\dir\\test.done");
file.createNewFile();
Thread.sleep(3000);
file.delete();
Assert.assertEquals("Found test\r\n", output.toString());
}
}