JUnit 测试只有在我 运行 它们一个一个地通过时才通过。可能穿线不正确
JUnit tests pass only when I run them one by one. Probably incorrect threading
UPD:标记的答案解决了问题,请参阅它对小修复的评论。
首先请原谅我的乱七八糟的代码。这个问题让我抓狂,我尝试了很多东西来确定问题的根源,但都失败了。
我有一个 Spring 启动网络应用程序,它有 FMonitor 服务。它在应用程序运行时工作 运行ning 并在某个文件夹中出现扩展名为“.done”的文件时通知我。当我 运行 应用程序时,它可以完美运行(至少看起来是这样)。
我决定用测试来练习,扔了三个简单的。当我一次 运行 他们一个人时,他们就成功了。但是,当我 运行 整个 class 测试时,只有第一个通过,其他两个失败。
我的测试检查 System.out 并将其与所需的输出进行比较。这对我来说是最奇怪的事情。就是这样。
第一次测试通过,FMonitor 的输出与 assertEquals 完全一样。
找到测试 1
找到测试 2
找到测试3
第二次测试失败。由于某种原因,输出奇怪地翻了一番:
找到测试 1
找到测试 1
找到测试 3
找到测试3
然后第三个失败了。现在输出是三倍:
找到测试
找到测试
找到测试
我的猜测是我对线程做了一些完全错误的事情,所以 fm.monitor() 以某种方式捕获了所有事件和类似的东西。我很困惑。我在这里尝试了很多关于如何实现线程的事情,我不太擅长,但它仍然有效。我还认为 monitor() 的 @Async 注释可能会弄乱某些东西,但删除它并没有改变任何东西。请帮忙。
BunchOfTests
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);
}
}
配置
import org.srpingframework.context.annotation.Bean;
import org.srpingframework.context.annotation.Configuration;
import org.srpingframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
@EnableAsync
public class AConfiguration {
@Bean(name = "fMonitor")
public Executor asyncExecutor() { return Executors.newSingleThreadExecutor(); }
}
对于初学者,您应该在完成后完全停止 WatchService
。实现一个执行此操作的方法并使用 @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) {}
}
}
接下来不要使用 @SpringBootTest
它不会给您的测试增加任何复杂性和缓慢性。相反,只需自己实例化它并使用适当的执行程序来启动 monitor
方法。
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());
}
}
像这样的东西应该或多或少地做你想要的。
UPD:标记的答案解决了问题,请参阅它对小修复的评论。
首先请原谅我的乱七八糟的代码。这个问题让我抓狂,我尝试了很多东西来确定问题的根源,但都失败了。
我有一个 Spring 启动网络应用程序,它有 FMonitor 服务。它在应用程序运行时工作 运行ning 并在某个文件夹中出现扩展名为“.done”的文件时通知我。当我 运行 应用程序时,它可以完美运行(至少看起来是这样)。
我决定用测试来练习,扔了三个简单的。当我一次 运行 他们一个人时,他们就成功了。但是,当我 运行 整个 class 测试时,只有第一个通过,其他两个失败。
我的测试检查 System.out 并将其与所需的输出进行比较。这对我来说是最奇怪的事情。就是这样。
第一次测试通过,FMonitor 的输出与 assertEquals 完全一样。 找到测试 1 找到测试 2 找到测试3
第二次测试失败。由于某种原因,输出奇怪地翻了一番: 找到测试 1 找到测试 1 找到测试 3 找到测试3
然后第三个失败了。现在输出是三倍: 找到测试 找到测试 找到测试
我的猜测是我对线程做了一些完全错误的事情,所以 fm.monitor() 以某种方式捕获了所有事件和类似的东西。我很困惑。我在这里尝试了很多关于如何实现线程的事情,我不太擅长,但它仍然有效。我还认为 monitor() 的 @Async 注释可能会弄乱某些东西,但删除它并没有改变任何东西。请帮忙。
BunchOfTests
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);
}
}
配置
import org.srpingframework.context.annotation.Bean;
import org.srpingframework.context.annotation.Configuration;
import org.srpingframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
@EnableAsync
public class AConfiguration {
@Bean(name = "fMonitor")
public Executor asyncExecutor() { return Executors.newSingleThreadExecutor(); }
}
对于初学者,您应该在完成后完全停止 WatchService
。实现一个执行此操作的方法并使用 @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) {}
}
}
接下来不要使用 @SpringBootTest
它不会给您的测试增加任何复杂性和缓慢性。相反,只需自己实例化它并使用适当的执行程序来启动 monitor
方法。
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());
}
}
像这样的东西应该或多或少地做你想要的。