Java: Làm thế nào để kiểm tra các phương thức gọi System.exit ()?


198

Tôi đã có một vài phương pháp nên gọi System.exit()một số đầu vào nhất định. Thật không may, kiểm tra những trường hợp này khiến JUnit chấm dứt! Việc đặt các cuộc gọi phương thức trong một System.exit()luồng mới dường như không có ích gì, vì chấm dứt JVM, không chỉ là luồng hiện tại. Có bất kỳ mô hình phổ biến để đối phó với điều này? Ví dụ, tôi có thể đăng ký sơ khai System.exit()không?

[EDIT] Lớp trong câu hỏi thực sự là một công cụ dòng lệnh mà tôi đang cố kiểm tra bên trong JUnit. Có lẽ JUnit đơn giản không phải là công cụ phù hợp cho công việc? Đề xuất cho các công cụ kiểm tra hồi quy bổ sung được hoan nghênh (tốt nhất là một cái gì đó tích hợp tốt với JUnit và EclEmma).


2
Tôi tò mò về lý do tại sao một hàm sẽ gọi System.exit () ...
Thomas Owens

2
Nếu bạn đang gọi một chức năng thoát khỏi ứng dụng. Ví dụ: nếu người dùng cố gắng thực hiện một tác vụ mà họ không được phép thực hiện nhiều hơn x lần liên tiếp, bạn buộc họ ra khỏi ứng dụng.
Elie

5
Tôi vẫn nghĩ rằng trong trường hợp đó, nên có một cách tốt hơn để trở về từ ứng dụng hơn là System.exit ().
Thomas Owens

27
Nếu bạn đang thử nghiệm main (), thì việc gọi System.exit () là hoàn hảo. Chúng tôi có một yêu cầu là do lỗi, một quy trình hàng loạt phải thoát với 1 và thoát thành công với 0.
Matthew Farwell

5
Tôi không đồng ý với tất cả những người nói System.exit()là xấu - chương trình của bạn sẽ thất bại nhanh chóng. Ném một ngoại lệ chỉ đơn thuần là kéo dài một ứng dụng ở trạng thái không hợp lệ trong các tình huống mà nhà phát triển muốn thoát và sẽ đưa ra các lỗi giả.
Sridhar Sarnobat

Câu trả lời:


216

Thật vậy, Derkeiler.com gợi ý:

  • Tại sao System.exit()?

Thay vì chấm dứt với System.exit (anythingValue), tại sao không ném một ngoại lệ không được kiểm soát? Trong sử dụng bình thường, nó sẽ trôi dạt đến công cụ bắt cuối cùng của JVM và tắt tập lệnh của bạn (trừ khi bạn quyết định bắt nó ở đâu đó trên đường đi, điều này có thể hữu ích vào một ngày nào đó).

Trong kịch bản JUnit, khung công tác JUnit sẽ bắt gặp, nó sẽ báo cáo rằng thử nghiệm tương tự như vậy đã thất bại và chuyển trơn tru sang kế tiếp.

  • Ngăn chặn System.exit()để thực sự thoát JVM:

Hãy thử sửa đổi TestCase để chạy với trình quản lý bảo mật ngăn việc gọi System.exit, sau đó bắt SecurityException.

public class NoExitTestCase extends TestCase 
{

    protected static class ExitException extends SecurityException 
    {
        public final int status;
        public ExitException(int status) 
        {
            super("There is no escape!");
            this.status = status;
        }
    }

    private static class NoExitSecurityManager extends SecurityManager 
    {
        @Override
        public void checkPermission(Permission perm) 
        {
            // allow anything.
        }
        @Override
        public void checkPermission(Permission perm, Object context) 
        {
            // allow anything.
        }
        @Override
        public void checkExit(int status) 
        {
            super.checkExit(status);
            throw new ExitException(status);
        }
    }

    @Override
    protected void setUp() throws Exception 
    {
        super.setUp();
        System.setSecurityManager(new NoExitSecurityManager());
    }

    @Override
    protected void tearDown() throws Exception 
    {
        System.setSecurityManager(null); // or save and restore original
        super.tearDown();
    }

    public void testNoExit() throws Exception 
    {
        System.out.println("Printing works");
    }

    public void testExit() throws Exception 
    {
        try 
        {
            System.exit(42);
        } catch (ExitException e) 
        {
            assertEquals("Exit status", 42, e.status);
        }
    }
}

Cập nhật tháng 12 năm 2012:

Sẽ đề xuất trong các nhận xét bằng cách sử dụng Quy tắc hệ thống , một tập hợp các quy tắc JUnit (4.9+) để kiểm tra mã sử dụng java.lang.System.
Điều này ban đầu được đề cập bởi Stefan Birkner trong câu trả lời của ông vào tháng 12 năm 2011.

System.exit(…)

Sử dụng ExpectedSystemExitquy tắc để xác minh rằng System.exit(…)được gọi.
Bạn cũng có thể xác minh trạng thái thoát.

Ví dụ:

public void MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void noSystemExit() {
        //passes
    }

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        System.exit(0);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        System.exit(0);
    }
}

4
Không thích câu trả lời đầu tiên, nhưng câu trả lời thứ hai khá tuyệt - tôi đã không nhắn tin với các nhà quản lý bảo mật và cho rằng chúng phức tạp hơn thế. Tuy nhiên, làm thế nào để bạn kiểm tra trình quản lý bảo mật / cơ chế kiểm tra.
Bill K

6
Hãy chắc chắn rằng việc phá bỏ được thực thi đúng cách, nếu không, thử nghiệm của bạn sẽ thất bại trong một trình chạy như Eclipse vì ứng dụng JUnit không thể thoát! :)
MetroidFan2002

6
Tôi không thích giải pháp sử dụng trình quản lý bảo mật. Có vẻ như một hack cho tôi chỉ để kiểm tra nó.
Nicolai Reuschling

6
Nếu bạn đang sử dụng Junit 4.7 trở lên, hãy để thư viện xử lý các cuộc gọi System.exit của bạn. Quy tắc hệ thống - stefanbirkner.github.com/system-rules
Sẽ

6
"Thay vì chấm dứt với System.exit (anythingValue), tại sao không ném một ngoại lệ không được kiểm soát?" - bởi vì tôi đang sử dụng khung xử lý đối số dòng lệnh gọi System.exitbất cứ khi nào một đối số dòng lệnh không hợp lệ được cung cấp.
Adam Parkin

106

Các quy tắc hệ thống thư viện có một quy tắc JUnit được gọi là ExpectedSystemExit. Với quy tắc này, bạn có thể kiểm tra mã, gọi System.exit (...):

public void MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        //the code under test, which calls System.exit(...);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        //the code under test, which calls System.exit(0);
    }
}

Tiết lộ đầy đủ: Tôi là tác giả của thư viện đó.


3
Hoàn hảo. Thanh lịch. Không phải thay đổi một đoạn mã gốc của tôi hoặc chơi xung quanh với trình quản lý bảo mật. Đây phải là câu trả lời hàng đầu!
Sẽ

2
Đây phải là câu trả lời hàng đầu. Thay vì tranh luận bất tận về việc sử dụng System.exit đúng cách, hãy đi thẳng vào vấn đề. Ngoài ra, một giải pháp linh hoạt tuân thủ chính JUnit vì vậy chúng tôi không cần phải phát minh lại bánh xe hoặc gây rối với người quản lý bảo mật.
L. Holanda

@LeoHolanda Không phải giải pháp này chịu cùng một "vấn đề" mà bạn đã sử dụng để biện minh cho một câu trả lời về câu trả lời của tôi sao? Ngoài ra, những gì về người dùng TestNG?
Rogério

2
@LeoHolanda Bạn nói đúng; Nhìn vào việc thực thi quy tắc, bây giờ tôi thấy nó sử dụng trình quản lý bảo mật tùy chỉnh, sẽ đưa ra một ngoại lệ khi cuộc gọi kiểm tra "thoát hệ thống" xảy ra, do đó kết thúc thử nghiệm. Bài kiểm tra ví dụ được thêm vào trong câu trả lời của tôi đáp ứng cả hai yêu cầu (xác minh đúng với System.exit được gọi / không được gọi), BTW. Việc sử dụng ExpectedSystemRulelà tốt đẹp, tất nhiên; vấn đề là nó đòi hỏi một thư viện bên thứ 3 bổ sung cung cấp rất ít về tính hữu dụng trong thế giới thực và đặc thù của JUnit.
Rogério

2
exit.checkAssertionAfterwards().
Stefan Birkner

31

Làm thế nào về việc tiêm một "Trình quản lý thoát" vào Phương thức này:

public interface ExitManager {
    void exit(int exitCode);
}

public class ExitManagerImpl implements ExitManager {
    public void exit(int exitCode) {
        System.exit(exitCode);
    }
}

public class ExitManagerMock implements ExitManager {
    public bool exitWasCalled;
    public int exitCode;
    public void exit(int exitCode) {
        exitWasCalled = true;
        this.exitCode = exitCode;
    }
}

public class MethodsCallExit {
    public void CallsExit(ExitManager exitManager) {
        // whatever
        if (foo) {
            exitManager.exit(42);
        }
        // whatever
    }
}

Mã sản xuất sử dụng ExitManagerImpl và mã kiểm tra sử dụng ExitManagerMock và có thể kiểm tra xem exit () có được gọi hay không và mã thoát nào.


1
+1 Giải pháp tốt đẹp. Cũng dễ thực hiện nếu bạn đang sử dụng Spring khi ExitManager trở thành Thành phần đơn giản. Chỉ cần lưu ý rằng bạn cần đảm bảo rằng mã của bạn không tiếp tục thực thi sau lệnh gọi exitManager.exit (). Khi mã của bạn đang được kiểm tra với một Trình quản lý giả, mã sẽ không thực sự thoát sau khi gọi đến exitManager.exit.
Joman68

31

Bạn thực sự có thể giả định hoặc bỏ qua System.exitphương thức này, trong một bài kiểm tra JUnit.

Ví dụ: sử dụng JMockit bạn có thể viết (cũng có những cách khác):

@Test
public void mockSystemExit(@Mocked("exit") System mockSystem)
{
    // Called by code under test:
    System.exit(); // will not exit the program
}


EDIT: Thử nghiệm thay thế (sử dụng API JMockit mới nhất) không cho phép bất kỳ mã nào chạy sau khi gọi đến System.exit(n):

@Test(expected = EOFException.class)
public void checkingForSystemExitWhileNotAllowingCodeToContinueToRun() {
    new Expectations(System.class) {{ System.exit(anyInt); result = new EOFException(); }};

    // From the code under test:
    System.exit(1);
    System.out.println("This will never run (and not exit either)");
}

2
Bỏ phiếu lý do: Vấn đề với giải pháp này là nếu System.exit không phải là dòng cuối cùng trong mã (tức là bên trong nếu có điều kiện), mã sẽ tiếp tục chạy.
L. Holanda

@LeoHolanda Đã thêm phiên bản thử nghiệm ngăn mã chạy sau exitcuộc gọi (không thực sự là vấn đề, IMO).
Rogério

Có thể thích hợp hơn để thay thế System.out.println () bằng một tuyên bố khẳng định?
dùng515655

"@Mocked (" exit ")" dường như không hoạt động nữa với JMockit 1.43: "Giá trị thuộc tính không được xác định cho loại chú thích Mocked" Các tài liệu: "Có ba chú thích giả khác nhau mà chúng ta có thể sử dụng khi khai báo các trường giả và tham số: @Mocked, sẽ mô phỏng tất cả các phương thức và hàm tạo trên tất cả các phiên bản hiện tại và tương lai của một lớp bị giả (trong thời gian thử nghiệm sử dụng nó); " jmockit.github.io/tutorial/Mocking.html#mocked
Thorsten Schöning

Bạn đã thực sự thử đề nghị của bạn? Tôi nhận được: "java.lang.IllegalArgumentException: Class java.lang.System không Runtimebị chế giễu " có thể bị chế giễu, nhưng System.exitít nhất không dừng lại trong kịch bản của tôi khi chạy Tests in Gradle.
Thorsten Schöning

20

Một mẹo mà chúng tôi đã sử dụng trong cơ sở mã của mình là để lệnh gọi System.exit () được gói gọn trong một hàm Runnable, phương thức được đề cập theo mặc định được sử dụng. Để kiểm tra đơn vị, chúng tôi đặt một giả lập Runnable khác nhau. Một cái gì đó như thế này:

private static final Runnable DEFAULT_ACTION = new Runnable(){
  public void run(){
    System.exit(0);
  }
};

public void foo(){ 
  this.foo(DEFAULT_ACTION);
}

/* package-visible only for unit testing */
void foo(Runnable action){   
  // ...some stuff...   
  action.run(); 
}

... và phương pháp kiểm tra JUnit ...

public void testFoo(){   
  final AtomicBoolean actionWasCalled = new AtomicBoolean(false);   
  fooObject.foo(new Runnable(){
    public void run(){
      actionWasCalled.set(true);
    }   
  });   
  assertTrue(actionWasCalled.get()); 
}

Đây có phải là những gì họ gọi là tiêm phụ thuộc?
Thomas Ahle

2
Ví dụ này như được viết là một kiểu tiêm phụ thuộc nửa nướng - phần phụ thuộc được truyền cho phương thức foo có thể nhìn thấy gói (bằng phương thức foo công khai hoặc kiểm tra đơn vị), nhưng lớp chính vẫn mã hóa việc triển khai Runnable mặc định.
Scott Bale

6

Tạo một lớp có thể mô phỏng bao bọc System.exit ()

Tôi đồng ý với EricSchaefer . Nhưng nếu bạn sử dụng một khung mô phỏng tốt như Mockito, một lớp cụ thể đơn giản là đủ, không cần giao diện và hai triển khai.

Dừng thực thi kiểm tra trên System.exit ()

Vấn đề:

// do thing1
if(someCondition) {
    System.exit(1);
}
// do thing2
System.exit(0)

Một sự chế giễu Sytem.exit()sẽ không chấm dứt thực thi. Điều này là xấu nếu bạn muốn kiểm tra mà thing2không được thực thi.

Giải pháp:

Bạn nên cấu trúc lại mã này theo đề xuất của martin :

// do thing1
if(someCondition) {
    return 1;
}
// do thing2
return 0;

Và làm System.exit(status)trong chức năng gọi. Điều này buộc bạn phải có tất cả của bạn System.exit()ở một nơi trong hoặc gần main(). Điều này sạch hơn gọi System.exit()sâu bên trong logic của bạn.

Vỏ bánh:

public class SystemExit {

    public void exit(int status) {
        System.exit(status);
    }
}

Chủ yếu:

public class Main {

    private final SystemExit systemExit;


    Main(SystemExit systemExit) {
        this.systemExit = systemExit;
    }


    public static void main(String[] args) {
        SystemExit aSystemExit = new SystemExit();
        Main main = new Main(aSystemExit);

        main.executeAndExit(args);
    }


    void executeAndExit(String[] args) {
        int status = execute(args);
        systemExit.exit(status);
    }


    private int execute(String[] args) {
        System.out.println("First argument:");
        if (args.length == 0) {
            return 1;
        }
        System.out.println(args[0]);
        return 0;
    }
}

Kiểm tra:

public class MainTest {

    private Main       main;

    private SystemExit systemExit;


    @Before
    public void setUp() {
        systemExit = mock(SystemExit.class);
        main = new Main(systemExit);
    }


    @Test
    public void executeCallsSystemExit() {
        String[] emptyArgs = {};

        // test
        main.executeAndExit(emptyArgs);

        verify(systemExit).exit(1);
    }
}

5

Tôi thích một số câu trả lời đã được đưa ra nhưng tôi muốn chứng minh một kỹ thuật khác thường hữu ích khi kiểm tra mã kế thừa. Cho mã như:

public class Foo {
  public void bar(int i) {
    if (i < 0) {
      System.exit(i);
    }
  }
}

Bạn có thể thực hiện tái cấu trúc an toàn để tạo phương thức kết thúc lệnh gọi System.exit:

public class Foo {
  public void bar(int i) {
    if (i < 0) {
      exit(i);
    }
  }

  void exit(int i) {
    System.exit(i);
  }
}

Sau đó, bạn có thể tạo giả cho bài kiểm tra của mình ghi đè thoát:

public class TestFoo extends TestCase {

  public void testShouldExitWithNegativeNumbers() {
    TestFoo foo = new TestFoo();
    foo.bar(-1);
    assertTrue(foo.exitCalled);
    assertEquals(-1, foo.exitValue);
  }

  private class TestFoo extends Foo {
    boolean exitCalled;
    int exitValue;
    void exit(int i) {
      exitCalled = true;
      exitValue = i;
    }
}

Đây là một kỹ thuật chung để thay thế hành vi cho các trường hợp thử nghiệm và tôi sử dụng nó mọi lúc khi tái cấu trúc mã kế thừa. Nó không thường là nơi tôi sẽ rời khỏi mọi thứ, mà là một bước trung gian để có được mã hiện có đang được thử nghiệm.


2
Công cụ này không dừng luồng conrol khi lối ra () đã được gọi. Sử dụng một ngoại lệ thay thế.
Andrea Francia

3

Nhìn nhanh vào api, cho thấy System.exit có thể ném một ngoại lệ đặc biệt. nếu một nhân viên an ninh cấm tắt máy vm. Có lẽ một giải pháp sẽ là cài đặt một người quản lý như vậy.


3

Bạn có thể sử dụng java SecurityManager để ngăn luồng hiện tại tắt máy ảo Java. Các mã sau đây nên làm những gì bạn muốn:

SecurityManager securityManager = new SecurityManager() {
    public void checkPermission(Permission permission) {
        if ("exitVM".equals(permission.getName())) {
            throw new SecurityException("System.exit attempted and blocked.");
        }
    }
};
System.setSecurityManager(securityManager);

Hừm. Các tài liệu System.exit nói cụ thể rằng checkExit (int) sẽ được gọi, không phải checkPermission với name = "exitVM". Tôi tự hỏi nếu tôi nên ghi đè cả hai?
Chris Conway

Tên quyền thực sự có vẻ là exitVM. (Mã trạng thái), tức là exitVM.0 - ít nhất là trong thử nghiệm gần đây của tôi về OSX.
Đánh dấu Derricutt

3

Để câu trả lời của VonC chạy trên JUnit 4, tôi đã sửa đổi mã như sau

protected static class ExitException extends SecurityException {
    private static final long serialVersionUID = -1982617086752946683L;
    public final int status;

    public ExitException(int status) {
        super("There is no escape!");
        this.status = status;
    }
}

private static class NoExitSecurityManager extends SecurityManager {
    @Override
    public void checkPermission(Permission perm) {
        // allow anything.
    }

    @Override
    public void checkPermission(Permission perm, Object context) {
        // allow anything.
    }

    @Override
    public void checkExit(int status) {
        super.checkExit(status);
        throw new ExitException(status);
    }
}

private SecurityManager securityManager;

@Before
public void setUp() {
    securityManager = System.getSecurityManager();
    System.setSecurityManager(new NoExitSecurityManager());
}

@After
public void tearDown() {
    System.setSecurityManager(securityManager);
}

3

Bạn có thể kiểm tra System.exit (..) bằng cách thay thế phiên bản Runtime. Ví dụ: với TestNG + Mockito:

public class ConsoleTest {
    /** Original runtime. */
    private Runtime originalRuntime;

    /** Mocked runtime. */
    private Runtime spyRuntime;

    @BeforeMethod
    public void setUp() {
        originalRuntime = Runtime.getRuntime();
        spyRuntime = spy(originalRuntime);

        // Replace original runtime with a spy (via reflection).
        Utils.setField(Runtime.class, "currentRuntime", spyRuntime);
    }

    @AfterMethod
    public void tearDown() {
        // Recover original runtime.
        Utils.setField(Runtime.class, "currentRuntime", originalRuntime);
    }

    @Test
    public void testSystemExit() {
        // Or anything you want as an answer.
        doNothing().when(spyRuntime).exit(anyInt());

        System.exit(1);

        verify(spyRuntime).exit(1);
    }
}

2

Có những môi trường mà mã thoát được trả về được sử dụng bởi chương trình gọi (chẳng hạn như ERRORLEVEL trong MS Batch). Chúng tôi có các thử nghiệm xung quanh các phương thức chính thực hiện điều này trong mã của chúng tôi và cách tiếp cận của chúng tôi là sử dụng một ghi đè SecurityManager tương tự như được sử dụng trong các thử nghiệm khác ở đây.

Đêm qua tôi đã tập hợp một JAR nhỏ bằng cách sử dụng các chú thích Junit @Rule để ẩn mã trình quản lý bảo mật, cũng như thêm các kỳ vọng dựa trên mã trả về dự kiến. http://code.google.com.vn/p/junitsystemrules/


2

Hầu hết các giải pháp sẽ

  • chấm dứt thử nghiệm (phương thức, không phải toàn bộ hoạt động) thời điểm System.exit()được gọi
  • bỏ qua một cài đặt đã SecurityManager
  • Đôi khi khá cụ thể cho một khung kiểm tra
  • hạn chế sử dụng tối đa một lần cho mỗi trường hợp thử nghiệm

Vì vậy, hầu hết các giải pháp không phù hợp cho các tình huống:

  • Việc xác minh tác dụng phụ sẽ được thực hiện sau cuộc gọi đến System.exit()
  • Một người quản lý bảo mật hiện có là một phần của thử nghiệm.
  • Một khung kiểm tra khác nhau được sử dụng.
  • Bạn muốn có nhiều xác minh trong một trường hợp thử nghiệm. Điều này có thể không được khuyến khích, nhưng đôi khi có thể rất thuận tiện, đặc biệt là kết hợp với assertAll(), ví dụ.

Tôi không hài lòng với các hạn chế được áp đặt bởi các giải pháp hiện có trong các câu trả lời khác, và do đó tự mình nghĩ ra một cái gì đó.

Lớp sau đây cung cấp một phương thức assertExits(int expectedStatus, Executable executable)xác nhận System.exit()được gọi với một statusgiá trị được chỉ định và kiểm tra có thể tiếp tục sau nó. Nó hoạt động tương tự như JUnit 5 assertThrows. Nó cũng tôn trọng một người quản lý an ninh hiện có.

Có một vấn đề còn lại: Khi mã được kiểm tra cài đặt trình quản lý bảo mật mới thay thế hoàn toàn trình quản lý bảo mật do thử nghiệm đặt. Tất cả các SecurityManagergiải pháp dựa trên cơ sở khác mà tôi biết đều gặp phải vấn đề tương tự.

import java.security.Permission;

import static java.lang.System.getSecurityManager;
import static java.lang.System.setSecurityManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public enum ExitAssertions {
    ;

    public static <E extends Throwable> void assertExits(final int expectedStatus, final ThrowingExecutable<E> executable) throws E {
        final SecurityManager originalSecurityManager = getSecurityManager();
        setSecurityManager(new SecurityManager() {
            @Override
            public void checkPermission(final Permission perm) {
                if (originalSecurityManager != null)
                    originalSecurityManager.checkPermission(perm);
            }

            @Override
            public void checkPermission(final Permission perm, final Object context) {
                if (originalSecurityManager != null)
                    originalSecurityManager.checkPermission(perm, context);
            }

            @Override
            public void checkExit(final int status) {
                super.checkExit(status);
                throw new ExitException(status);
            }
        });
        try {
            executable.run();
            fail("Expected System.exit(" + expectedStatus + ") to be called, but it wasn't called.");
        } catch (final ExitException e) {
            assertEquals(expectedStatus, e.status, "Wrong System.exit() status.");
        } finally {
            setSecurityManager(originalSecurityManager);
        }
    }

    public interface ThrowingExecutable<E extends Throwable> {
        void run() throws E;
    }

    private static class ExitException extends SecurityException {
        final int status;

        private ExitException(final int status) {
            this.status = status;
        }
    }
}

Bạn có thể sử dụng lớp như thế này:

    @Test
    void example() {
        assertExits(0, () -> System.exit(0)); // succeeds
        assertExits(1, () -> System.exit(1)); // succeeds
        assertExits(2, () -> System.exit(1)); // fails
    }

Mã có thể dễ dàng được chuyển đến JUnit 4, TestNG hoặc bất kỳ khung nào khác, nếu cần. Yếu tố khung cụ thể duy nhất là thất bại trong bài kiểm tra. Điều này có thể dễ dàng được thay đổi thành một cái gì đó độc lập với khung (khác với Junit 4 Rule

Có chỗ để cải thiện, ví dụ, quá tải assertExits()với các thông điệp có thể tùy chỉnh.


1

Sử dụng Runtime.exec(String command)để bắt đầu JVM trong một quy trình riêng biệt.


3
Làm thế nào bạn sẽ tương tác với lớp được kiểm tra, nếu nó nằm trong một quy trình riêng biệt với bài kiểm tra đơn vị?
DNA

1
Trong 99% trường hợp System.exit nằm trong một phương thức chính. Do đó, bạn tương tác với chúng bằng các đối số dòng lệnh (ví dụ: args).
L. Holanda

1

Có một vấn đề nhỏ với SecurityManagergiải pháp. Một số phương pháp, chẳng hạn như JFrame.exitOnClose, cũng gọi SecurityManager.checkExit. Trong ứng dụng của tôi, tôi không muốn cuộc gọi đó thất bại, vì vậy tôi đã sử dụng

Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);

0

Gọi System.exit () là một cách thực hành tồi, trừ khi nó được thực hiện bên trong main (). Các phương thức này sẽ đưa ra một ngoại lệ, cuối cùng, được bắt bởi main () của bạn, người sau đó gọi System.exit với mã thích hợp.


8
Điều đó không trả lời câu hỏi, mặc dù. Điều gì xảy ra nếu chức năng đang được kiểm tra IS cuối cùng là phương thức chính? Vì vậy, việc gọi System.exit () có thể hợp lệ và ok từ góc độ thiết kế. Làm thế nào để bạn viết một trường hợp thử nghiệm cho nó?
Elie

3
Bạn không nên kiểm tra phương thức chính vì phương thức chính chỉ cần lấy bất kỳ đối số nào, chuyển chúng sang phương thức trình phân tích cú pháp và sau đó khởi động ứng dụng. Không nên có logic trong phương thức chính được kiểm tra.
Thomas Owens

5
@Elie: Trong các loại câu hỏi này có hai câu trả lời hợp lệ. Một người trả lời câu hỏi được đặt ra, và một người hỏi tại sao câu hỏi lại dựa trên. Cả hai loại câu trả lời cho một sự hiểu biết tốt hơn, và đặc biệt là cả hai cùng nhau.
runaros
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.