ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 불안정(Flakey) 테스트와 예방법
    테스팅 관련/테스트 2020. 8. 20. 16:49

     

     

    https://miro.medium.com/max/2800/1*wVuMmt7AQueS7Wfuvrtx3A.png

     

    불안정(Flakey) 테스트
    어떤 결과가 나올지 알 수 없는 테스트 (때에 따라 성공/실패)

    무엇이 테스트를 불안정하게 만들까?

    • 동시실행(Concurrency)
      • 멀티 스레드/프로세스가 동시에 작동할 때, 순서나 작동시간이 때에 따라 달라질 수 있다. 
    • 시간
      • 테스트 성공/실패 시간을 정하면, 시스템 환경에 따라 성공/실패 유무가 정해진다.
    • 환경 변화
      • 다른 시스템 환경, 파일, 데이터베이스 등이 원인이 될 수 있다.

     

    멀티 스레드
    1개의 응용 프로그램이 스레드(thread)로 불리는 처리 단위를 복수 생성하여 복수의 처리를 병행하는 것. 즉, 응용 프로그램 내에서의 다중 작업(multitasking) 처리를 말한다.

     

    public class Multithread {
    
        class Runner implements Runnable {
            
            StringBuffer log;
            String id;
            
            public Runner(String id, StringBuffer log) {
           	    this.id = id;
                this.log = log;
            }
            
            void appendLog() {
                synchronized(log) {
                	log.append(id);
                }
            }
            
            public void someComputation() {
                Thread.yield();
            }
            
            @Override
            public void run() {
                for (int i=0; i<10; i++) {
                	someComputation();
                    appendLog();
                }
            }
        }
        
        public String runThreads() {
        	StringBuffer buff = new StringBuffer();
            Runner a = new Runner("a", buff);
            Runner b = new Runner("b", buff);
            Thread t1 = new Thread(a);
            Thread t2 = new Thread(b);
            t1.start();
            t2.start();
            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) { }
            return buff.toString();
        }
     }      

    동시실행

    동시실행은 코드와 테스트에 미묘한 장애(failure)들을 발생시킨다

    • 멀티 스레드
    • 멀티 프로세스

     

    멀티 스레드의 결과는 이러한 상황에 발생된다:

    • 다른 순서
    • 다른 시간(timing)
    • .... 그리고 스레드가 독립적이지 않다면, 다른 결과를 야기시킬 수 있다

     

    import static org.junit.Assert.*;
    
    public class MultithreadTest {
    
        // 불안정 테스트
        @Test
        public void testRunThreadsFlakey() throws Exception {
        	Multithread m = new Multithread();
            String logResult = m.runThreads();
            System.out.println("logResult equals: " + logResult);
            assertEquals(logResult, "babababababababa");
        }
        
        // 연속 호출을 확인하기위해 모의객체(Mocks)를 사용할 때도
        // 이 불안정함은 같은 방식으로 나타난다.
        
        
        // Q: 테스트 또는 프로그램 중에 어떤 것이 불안정한 것일까?
        //    순서가 꼭 baba로 나타나야 하는 걸까...?
        //    보통, 각 스레드가 몇 번 실행되었는지를 확인한다.
        @Test
        public void testRunThreads() throws Exception {
        	Multithread m = new Multithread();
            String logResult = m.runThreads();
            System.out.println("logResult equals: " + logResult);
            char [] logArray = logResult.toCharArray();
            Arrays.sort(logArray);
            logResult = new String(logArray);
            assertEquals(logResult, "aaaaaaaaaaabbbbbbbbbbb");
        }
        
        // 모의객체에서도, 순서보다는 총 몇 번의 호출이
        // 발생하였는지에 중점을 둘 수 있다.
        
        public long [] randomizeMemory() {
        	int randomSize = (int)(Math.random() * 20000.0);
            long longArray [] = new long[randomSize + 1];
            longArray[0] = 1;
            int randomSize1 = (int)(Math.random() * 20000.0);
            long longArray1 [] = new long[randomSize1 + 1];
            longArray1[0] = 1;
            return longArray;
        }
    }    

    환경 변화

    불안정하게 이끄는 요소들:

    • 날짜/시간 변화
    • 기본 OS
    • GUI 프레임워크
    • 사용 가능 메모리 (시간이 오래 걸릴 수도 있음)
    • 프로세서 속도
    • 네트워크 속도
    • 디스크 속도
    • JVM 버전
    • 메모리 객체의 위치 (포인터)

     

    해시코드의 기본 구현은 객체의 포인터 위치를 사용한다 = 해시코드는 (다른 플랫폼) 또는 (같은 플랫폼의 다른 실행)에 따라 불안정하다
    // 환경 변화
     
    // [데이터 구조에 따른 불안정함]
    // 포인터를 조심히 사용하지 않으면, 불안정함을 발생시킬 수 있다
    // 이 테스트는 하나의 플랫폼/VM 구성에서 통과한다
    // 하지만, 다른 플랫폼들에서는 fail한다
    @Test
    public void flakeyTestAddToSet() throws Exception {
        long [] randomarray = randomizeMemory();
        ClassWithoutHashcode c1 = new ClassWithoutHashcode("a");
        ClassWithoutHashcode c2 = new ClassWithoutHashcode("c");   
        Set<ClassWithoutHashcode> aSet = new HashSet<>();
        aSet.add(c1);
        aSet.add(c2);
        classWithoutHashcode [] result = {c1, c2};
        System.out.println("C1 hash code: " + c1.hashCode());
        System.out.println("C2 hash code: " + c2.hashCode());    
        assertThat(aSet.toArray(), equalTo(result));
    }

     


    불안정 테스트를 피해야하는 이유

    • 불안정한 이유를 찾아내야한다 (개발자들은 코드가 올바르게 작동 하는지/안 하는지 알지 못 한다)
    • 소프트웨어 또는 테스트가 튼튼하기를 바란다
    • '테스트의 문제'가 아니라 '프로그램의 문제'를 보여주는 테스트를 작성하는게 테스터의 목표다

     


     

Designed by Tistory.