簡體   English   中英

如何測試啟動可能由於操作系統而失敗的新線程的方法

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM