[英]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.