반응형

JUnit Rule이란 ?

JUnit Rule은 테스트 케이스를 실행하기 전/후에 추가 코드를 실행할 수 있도록 도와준다.

즉, @Before@After로 선언된 메서드에서도 실행 전후처리로 코드를 넣을 수 있지만,

JUnit Rule로 작성하면 재사용하거나 더 확장 가능한 기능으로 개발할 수 있는 장점이 있다.

 

즉, 요약하자면

  • Rule은 테스트 클래스에서 동작 방식을 재정의 하거나 쉽게 추가하는 것을 가능하게 한다.
  • 사용자는 기존의 Rule을 재사용하거나 확장하는 것이 가능해진다.

 

Rule의 종류

1. TemporaryFoler Rule

  • 임시 폴더, 파일들을 생성할 수 있다.
  • 테스트가 모두 끝난 후 삭제한다.
  • 기본적으로 resource를 삭제하지 못하는 경우 어떠한 exception도 반환하지 않는다.
package com.example.myapplication;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class MyTest {
        @Rule
        public final TemporaryFolder folder = new TemporaryFolder();

        @Test
        public void testUsingTempFolder() throws IOException {
            File createdFile = folder.newFile("myfile.txt");
            File createdFolder = folder.newFolder("subfolder");

            System.out.println(createdFile);
            System.out.println(createdFolder);

            assertThat(2, is(folder.getRoot().list().length));
        }
}

 

 

2. ExternalResources Rule

  • 외부 Resource(DB Connection, File, Socket) 초기화 / 반환을 관리한다.
  • 특정 자원을 다른 테스트 케이스에서 재사용할 때 유용하다.
package com.example.myapplication;

import org.junit.AfterClass;
import org.junit.AssumptionViolatedException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;

public class MyTest {
    private Server myServer = new Server();

    @Rule
    public final ExternalResource resource = new ExternalResource() {
        @Override
        protected void before() {
            myServer.connect();
        }

        @Override
        protected void after() {
            myServer.disconnect();
        }
    };

    @Test
    public void serverTest() {
        System.out.println("Server test");
    }

    public static class Server {
        public void connect() {
            System.out.println("connect...");
        }

        public void disconnect() {
            System.out.println("disconnect...");
        }
    }
}

 

 

3. ErrorCollector Rule

  • 에러가 발생하더라도 지속적으로 테스트를 진행하게 도와주는 Rule이다.
package com.example.myapplication;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;

public class MyTest {
    public static class UsesErrorCollectorTwice {
        @Rule
        public final ErrorCollector collector = new ErrorCollector();

        @Test
        public void errorTest() {
            collector.addError(new Throwable("first thing went wrong"));
            collector.addError(new Throwable("second thing went wrong"));
        }
    }
}

 

 

 

4. Verifier Rule

  • 테스트 자체를 검증하는 assert와는 다르게, 테스트 케이스 실행 후 만족해야하는 환경조건이나 Global조건(객체들의 종합 상태)을 검사하는데 사용된다.
package com.example.myapplication;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Verifier;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class MyTest {

    private static String mSequence = "";

    @Rule
    public final Verifier collector = new Verifier() {
        @Override
        protected void verify() {
            mSequence += "Verify";
            System.out.println("verify : " + mSequence);
        }
    };

    @Test
    public void example() {
        mSequence += "test";
        System.out.println("example : " + mSequence);
        assertThat("test", is(mSequence));
    }

    @Test
    public void verifierRunsAfterTest1() {
        System.out.println("verifierRunsAfterTest1 : " + mSequence);
        assertThat("testVerify", is(mSequence));
    }

    @Test
    public void verifierRunsAfterTest2() {
        System.out.println("verifierRunsAfterTest2 : " + mSequence);
        assertThat("testVerifyVerify", is(mSequence));
    }
}

해당 코드를 보면 하나의 test를 진행할 때마다

Rule의 Verify 메서드가 호출됨을 알 수 있다.

 

 

 

 

 

 

 

 

 

5. TestWatcher

  • 테스트 Interceptor (starting, succeeded, failed , finished)을 intercept한다.
package com.example.myapplication;

import org.junit.AfterClass;
import org.junit.AssumptionViolatedException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import static org.junit.Assert.fail;

public class MyTest {
    private static String mWatchedLog = "";

    @Rule
    public final TestRule watchman = new TestWatcher() {
        @Override
        public Statement apply(Statement base, Description description) {
            return super.apply(base, description);
        }

        @Override
        protected void succeeded(Description description) {
            mWatchedLog += description.getDisplayName() + " " + "success!\n";
        }

        @Override
        protected void failed(Throwable e, Description description) {
            mWatchedLog += description.getDisplayName() + " " + e.getClass().getSimpleName() + "\n";
        }

        @Override
        protected void skipped(AssumptionViolatedException e, Description description) {
            mWatchedLog += description.getDisplayName() + " " + e.getClass().getSimpleName() + "\n";
        }

        @Override
        protected void starting(Description description) {
            super.starting(description);
        }

        @Override
        protected void finished(Description description) {
            super.finished(description);
        }
    };

    @AfterClass
    public static void teardown(){
        System.out.println("===== Show Logs =====");
        System.out.println(mWatchedLog);
    }

    @Test
    public void fails() {
        fail();
    }

    @Test
    public void test_success() {
        fail("test_success fail");
    }
}

 

 

 

 

6. TestName

  • 테스트 메소드명을 얻을 수 있다.
package com.example.myapplication;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class MyTest {
    @Rule
    public final TestName name = new TestName();

    @Test
    public void testA() {
        assertThat("testA", is(name.getMethodName()));
    }

    @Test
    public void testB() {
        assertThat("testB", is(name.getMethodName()));
    }
}

 

 

 

7. Timeout

  • 하나의 테스트가 통과하기 위한 timeout 설정할 수 있다. (vs @Timeout)
package com.example.myapplication;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class MyTest {
    public static String log;

    @Rule
    public final TestRule globalTimeout = Timeout.millis(20);

    @Test
    public void testInfiniteLoop1() {
        log += "ran1";
        for(;;) {}
    }

    @Test
    public void testInfiniteLoop2() {
        log += "ran2";
    }
}

 

 

 

 

 

8. ExpectedException

  • 예외 직접 확인할 수 있다. (vs @Expected)
  • Error 메시지도 검증이 가능하다.
package com.example.myapplication;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.core.StringStartsWith.startsWith;

public class MyTest {
    @Rule
    public final ExpectedException thrown = ExpectedException.none();

    @Test
    public void throwsNothing() {

    }

    @Test
    public void throwsNullPointerException() {
        thrown.expect(NullPointerException.class);
        throw new NullPointerException();
    }

    @Test
    public void throwsNullPointerExceptionWithMessage() {
        // NullPointerException이고 
        // happend가 포함되고 What로 시작하는 exception message 여야한다.
        thrown.expect(NullPointerException.class);
        thrown.expectMessage("happened?");
        thrown.expectMessage(startsWith("What"));
        // 
        throw new NullPointerException("What happened?");
    }
}

 

 

9. ClassRule

  • TestSuite의 클래스마다 적용할 수 있는 Rule입니다.

 

package com.example.myapplication;

import org.apache.maven.settings.Server;
import org.junit.ClassRule;
import org.junit.rules.ExternalResource;

@RunWith(Suite.class)
@SuiteClasses({A.class, B.class})
public class MyTest {
    public static final Server myServer = new Server();

    @ClassRule
    public static final ExternalResource resource = new ExternalResource() {
        @Override
        protected void before() throws Throwable {
            myServer.connect();
        };

        @Override
        protected void after() {
            myServer.disconnect();
        };
    };
}

 

 

 

 

 

 

출처

https://junit.org/junit4/javadoc/4.12/org/junit/ClassRule.html

https://beomseok95.tistory.com/300

https://nesoy.github.io/articles/2018-12/Junit-Rule

 

반응형