[英]how to test method that starts new thread which may fail due to operating system
我有一个基于Spring Boot构建的应用程序。 有一个简单的控制器,带有创建新线程并启动它的方法。 但是,可运行程序会执行Unix命令(nc)(用于此过程的ProcessBuilder)。 因此,当我在Windows上运行它时,会从启动线程中获取异常。 确实不能运行unix程序。 现在,我想为此控制器编写一个测试,但是我想知道这是否可行和合理。 尽管我不知道如何完成操作,但我只是在考虑更改可运行任务的行为以进行测试。 感谢您提供的帮助和其他针对此案例的想法/解决方案。
控制器:
@Controller
public class TaskController {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(task-%d").build();
@RequestMapping(value = "/startTask")
public @ResponseBody ResponseEntity<String> startTask() {
Runnable runnable= new Task();
threadFactory.newThread(runnable).start();
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
任务:
public class Task implements Runnable {
@Override
public void run() {
// start unix process
}
}
应用类别:
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
整合测试:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest("server.port=0")
@DirtiesContext
public class ApplicationTest {
@Value("${local.server.port}")
private int port;
@Test
public void shouldStartTask() throws Exception {
// when
ResponseEntity<String> entity = new TestRestTemplate().getForEntity("http://localhost:" + this.port + "/startTask", String.class);
// then
assertThat(entity.getStatusCode()).isSameAs(HttpStatus.ACCEPTED);
}
}
如果从控制器逻辑中提取应用程序的处理逻辑(使用线程执行操作),并将处理逻辑放置在控制器委托的单独服务层中 ,则可能会更容易测试程序。 通过提供访问其当前状态而不只是执行操作的方法,将服务层设计为具有易于进行单元测试的API。 使用依赖注入将控制器连接到服务层。
因此,如下所示:
public interface Service
{
// Sets this.wasTaskStarted() == true
void startTask();
boolean wasTaskStarted();
void awaitCompletionOfTask();
}
@Controller
public class TaskController {
private final Service service;
@Autowired
public TaskController(Service service) {
this.service = service;
}
@RequestMapping(value = "/startTask")
public @ResponseBody ResponseEntity<String> startTask() {
service.startTask();
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
public ServiceImpl implements Service {
private final ThreadFactor threadFactory = new ....;
private Thread taskTread;
@Override
public synchronized void startTask() {
if (taskTread == null) {
taskTread = threadFactory.newThread(new Task());
taskTread.start();
notifyAll();
}
// else already started
}
@Override
public synchronized boolean wasTaskStarted() {
return taskTread != null;
}
@Override
public synchronized void awaitCompletionOfTask() {
while (taskTread == null) {
wait();
}
taskTread.join();
}
}
要测试您的控制器启动任务,只需在调用TaskController.startTask()
之后测试Service.wasTaskStarted()
是否为true
。
您还必须测试服务层:
public class ServiceImplTest
{
@Test
public void testStartTask() {
final ServiceImpl service = new ServiceImpl(....);
service.startTask();
assert(service.wasTastStarted());
}
@Test
public void testRunTask() {
final ServiceImpl service = new ServiceImpl(....);
service.startTask();
service.awaitCompletionOfTask();
// Add assertions here to test that the task did what it ought to do
}
}
谢谢你的建议。 您刚刚打开我的胸,我对设计进行了一些更改。 我从集成测试中辞职了。 从业务角度来看,我不需要检查任务是否已经开始甚至完成。 现在看起来如下:
控制器:
@Controller
public class TaskController {
private ThreadService threadService;
@Autowired
public TaskController (ThreadService threadService) {
this.threadService= threadService;
}
@RequestMapping(value = "/startTask")
public @ResponseBody ResponseEntity<String> startTask() {
// some conditions here which I would like to test
threadService.startNewThread(new Task());
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
任务:
public class Task implements Runnable {
@Override
public void run() {
// start unix process
}
}
线程服务:
@Component
public class ThreadService {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("task-%d").build();
public void startNewThread(Runnnable task) {
threadFactory.newThread(task).start();
}
}
然后我决定对我的控制器进行单元测试,并使用Mockito对ThreadService进行测试:
@RunWith(MockitoJUnitRunner.class)
public class TaskControllerTest {
@Mock
ThreadService threadService;
@InjectMocks
private TaskController objectUnderTest;
@Test
public void shouldStartTask() throws FileNotFoundException {
// when
ResponseEntity<String> response = objectUnderTest.startTask();
// then
assertThat(response.getStatusCode()).isSameAs(HttpStatus.ACCEPTED);
// more assertions
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.