TDD (Android Unit Test)

  • View
    1.297

  • Download
    2

  • Category

    Software

Preview:

Citation preview

TDD(Android Unit Test)

ACT LabKihoon Kim

(koreakihoon@gmail.com)

test the program before you write it- kent beck -

TDD (Test Driven Development)

개발자는 바라는 향상 또는 새로운 함수를 정의하는 (초기적 결함을 점검하는) 자동화된 테스트 케이스를 작성한다.

그런 후에, 그 케이스를 통과하기 위한 최소한의 양의 코드를 생성한다.

그리고 마지막으로 그 새 코드를 표준에 맞도록 리팩토링한다.

https://ko.wikipedia.org/wiki/테스트_주도_개발

The Three Rules of TDD - UncleBob

1. You are not allowed to write any production code unless it is to make a failing unit test pass.

실패하는 테스트를 작성하기 전에는 제품 코드를 작성하지 않는다.

2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.

실패하는 테스트 코드를 한번에 하나 이상 작성하지 않는다. (컴파일 에러도 실패다)

3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test. 현재 실패한 테스트 코드를 성공시키는데 충분한 정도로만 제품 코드를 작성한다.

그래서 뭘 어떻게 하라는 건가요?

Design Coding Test

Design Test Coding

이렇게 하던 걸 이렇게 하라는 말이죠

Refactoring

Refactoring

테스트 코드를 먼저 작성 한다는 것은

무엇을 테스트 할지 결정해야 한다는 것

프로그래밍 목적을 명확히 해야 한다는 것

TDD의 장점?

● Clean Design

● Fast Feedback

● Concrete Evidence That Your

Software Works

● Write Better Code

● Reduce Gold-Plating

● Regression Test Suite

● Validates your design

● Gives confidence

● Maintains quality of implementation

● Is Automated

● No Dead code

● Easy maintenance

● Early issue discoveryhttps://www.slideshare.net/progmania1/tdd-class-1 https://www.slideshare.net/harshit040591/test-driven-development-with-jasmine

저희 팀은 이렇게 해요

Code

User Story

UI Design

[PM]도메인, 비즈니스 분석

[CX]사용자 인터뷰사용자 인터랙션 분석

[Dev]구현, 테스트

로그인 User story : PM

As 사용자로서I want 앱을 사용하기 위해서So that ID/PW로 로그인 하기 원한다.

# Acceptance CriteriaGiven ID=kihoon, PW=kim1234 를 입력된 상태에서When 로그인 버튼을 눌렀을때 로그인 성공 시Then 로그인 성공 페이지를 보여준다

Given ID=kihoon, PW=kim1234 를 입력된 상태에서When 로그인 버튼을 눌렀을때 로그인 실패 시Then “login fail” 메시지를 보여준다

개발 전 PM과 확인 할 사항

이번 스토리에서는 로그인 성공 페이지는 빈 페이지만 보여주면 되는가? => Yes

로그인 인증 API는 개발되어 있는가? => No

로그인 실패 메시지는 어떤 형식으로 보여 주나? Popup? Toast? => Toast

UI Design : Designer

코딩을 해볼까요 : Dev

Test Code 를 위한 라이브러리 설정: build.gradle

dependencies { … testCompile 'junit:junit:4.12' testCompile('com.squareup.assertj:assertj-android:1.1.1') { exclude group: 'com.android.support', module: 'support-annotations' } testCompile 'org.mockito:mockito-core:1.10.19' testCompile "org.robolectric:robolectric:3.2.2”

...}

LoginActivity 를 만들어야 겠죠

LoginActivity 도 만들고

AndroidManifest 에도

선언해 줍니다.

IDE의 도움을 받으면

편합니다.

복잡하지 않다면 전체 layout 부터 만들어요

만약 화면이 복잡하다면

현재 개발 할

User Story 에 나와있는

부분만 먼저 만들어요

실패하는 테스트 케이스 작성

# given

ID/PW가 입력된 상황# when

로그인 버튼 클릭되면

# then

다른 화면으로 이동

하는 것을 테스트

(실제 구현 로직은 아직 없음)

Test Runner

Test Case

Robolectricassertj

Run Test Code

LoginButton Click 이벤트를 핸들링하는 로직이 없기 때문에 Test Fail..

동작하는 구현코드 작성

이동 할 다음 화면 생성

로그인 버튼 클릭 핸들러생성 및 처리 로직 구현

빈 Activity 생성

로그인 버튼 클릭 이벤트 핸들러

테스트 성공

앞에서 만든 실제 Activity class로 변경

로그인 성공/실패 처리는?

테스트 코드 변경 없음

로그인 처리를 위한 Interface 생성

보통 Dagger와 같은 DI 라이브러리를 사용하여 인젝션 처리하지만 단순히 객체 생성

로그인 성공 케이스에 대한 Unit Test

Mockito

mocking

stubbing

로그인 실패 케이스에 대한 Unit Test

login fail

Toast 안 뜸

로그인 실패시 실패 메시지 띄워주는 로직 추가

로그인 실패시

“Login Fail” 메시지를

Toast로 띄워주는 로직 추가

로그인 User Story에 대한 테스트 코드 Passed

LoginService

로그인 Rest API가 없는 상황에서 현재 LoginService 로직 상으로 항상 true를 리턴하기 때문에 런타임에는 성공에 대한 케이스만 테스트 가능

하지만, Test Code 에서 LoginService에 대해서 stubbing 을 함으로써 성공 실패 케이스에 대해 모두 테스트 할 수 있었음

무엇을 Test 해야 하나?

Interface 에 대해서 테스트 코드 작성

- User Interface- Input : input text, click button…- output : show toast/dialog, launch Activity..

- API (Application Programming Interface)- return_value method_name (input_value)- void method_name() ← ex) Input: message queue, Output: Rest Call

특정 상태를 테스트 하기보다는 행위에 대해서 테스트

Gray Box Test

mocking 하는 대상은 무엇인가?

LoginActivity LoginService

dependency

테스트 대상 객체 Mocking

when(subject.loginService.login(anyString(), anyString())).thenReturn(true);

verify(subject.loginService).login("kihoon", "kim 1234");

subject.loginService = mock(LoginService.class);

잘 정리된 robolectric tutorial 이 없네요..

https://github.com/robolectric/robolectric-samples

http://robolectric.org/writing-a-test/

http://robolectric.org/javadoc/latest/

IDE의 도움을 받고, 구글 검색을 잘 해야 될 것 같아요

shadow class 에 대한 이해와 사용법을 아셔야 될 것 같아요

http://robolectric.org/extending/

Mockito 사용법을 익히는 것도 중요합니다

# mock 객체 생성, interface 도 가능Comparable c= mock(Comparable.class);

# 기대하는 결과를 stubbingwhen(c.compareTo("Mockito")).thenReturn(1);doThrow(new IOException()).when(mocksoc).close();

# mock객체의 메소드가 실행됐는지 검증verify(test).testing(eq(12));

# 실제 객체를 테스트 하고 싶을 때 spy 객체 생성 List spy = spy(list);doReturn("foo").when(spy).get(0);

# 특정 메소드가 실행될때 인자값이 잘 넘어가는지 확인@Captor ArgumentCaptor<List<String>> captor;verify(mockedList).addAll(captor.capture());

http://site.mockito.org/http://www.vogella.com/tutorials/Mockito/article.html

Test Driven Development==

Test First Development??

중요한 것은..좋은 설계를 하고좋은 제품을 만드는 것

Test를 먼저 작성하지 않으면 귀찮고 시간이 없다는 핑계로Test Code를 안 만들게 되요습관이 될 때 까지는

Test First 하는게 좋아요

BUT

개인적으로Test First 보다

더 중요하게 생각하는 것들..

Test Code를 확보하는 것

Refactoring

Side effect

회귀 테스트 (Regression Test)

빠른 피드백

CI + Test Automation

반복 수행 가능

빠른 피드백

기능 점검은 사람보다 컴퓨터가 빠르다

Testing = Checking + Exploringhttps://www.thoughtworks.com/insights/blog/qa-dead

기계 사람

Manual Test + Bug Hunting

자동화 테스트 코드도 결국은 사람이 작성하는 것.

TDD를 통해 Test Code 를 작성한다고 모든 비즈니스 요구사항을 100% 커버한다고 확신 할 수 없음

Test Code 는 AC를 위주로 작성하고자주나오는 버그나 예외 사항에 대해서 Test Code 추가해 나감

Test Strategy

UI Test (End to End) 와 Unit Test 간 상호 보완적으로 작성 필요

E2E test 실행은 오래 걸리지만Unit Test로 어려운 걸 쉽게 해결하는 경우가 있음

Rest API 자동화 테스트 중요 - 생각보다 모르는 사이에 자주 바뀜(http://rest-assured.io/)

https://martinfowler.com/bliki/TestPyramid.html

Divide and Conquer

User Story가 작게 쓰여지는 것이 중요

그렇지 않다면, 일을 작게 나눠서 개발해 나가는 연습이 필요

짧고 반복적인 개발 리듬을 찾는게 중요

그렇지 않으면 너무 깊게 설계/개발하거나, 실제 필요하지 않은 기능을 개발자 상상 만으로 개발하게됨

Good Enough + Time Boxing

구현은 간단한데 테스트 코드가 어려운 경우가 있음 (Thread, 외부 Library, Touch Event..)

100% 테스트 코드를 만드는 것에 집착하지 말자

한번에 완벽한 코드를 작성하려고 하지 말자머리 속에서 설계가 되는 순간 코드로 옮겨 동작하는지 테스트 해보자소스 코드는 시간 축을 가지고 있다. 점진 적으로 개선해 나가는 것이 좋다.

페어 프로그래밍을 한다면 ‘이정도면 충분해' 를 외쳐 주자

거대한 객체를 만들지 말자

요구사항이 추가된다면??

if( A 경우 )else if ( B 경우) else if ( C 경우) else if ( D 경우) else if ( E 경우) ……

여러 요인으로 수정되는 메소드/클래스는 요구사항 변경을 어렵게 합니다

Super Giant Huge Class

Service or Activity ??

객체의 역할, 책임, 협력

적절한 책임과 역할을 객체에게 부여하고객체들 간의 협력 관계를 만들어 나가는 것이 중요

TDD를 할때 중요한 것 중 하나가 이런 객체들을 식별해내는 것

객체는 다른 객체에게 메시지를 전달 함으로써 다른 객체들과 협력을 하게 된다(Java 에서 메시지를 전달하는 방법은 객체의 method를 호출하는 것)

message

message

자율적인 객체

객체는 메시지를 수신했을 때만 자신의 책임을 수행하게 된다.

다른 객체는 메시지를 전달하며 협력 할 뿐, 그 객체가 내부적으로 어떻게 동작하는지 관심없다.

즉, 객체 내부의 구현이 변경되더라도 메시지가 변하지 않는다면 다른 객체를 변경하지 않아도 된다.

비즈니스 요구사항의 세부 구현을 객체 내부로 숨김으로써 변경에 대응할 수 있게 된다.

인터페이스(interface)를 사용하자

OOP 에서 자신의 책임을 다른 객체에게 public 하게 제공하는 것이 바로 interface 이다.

interface 를 통해 객체 간 의존성을 낮출 수 있다.

interface 를 통해 객체가 자율적으로 동작할 수 있게 해준다.

java interface에는 field 를 선언 할 수 없다. 즉, 상태가 아닌 행위를 테스트 해야 한다.

message

사용하는 언어를 통일하자

개념적 설계, 구조적 설계

모델을 구현 코드에 최대한 반영

DESIGNER USER

SYSTEM

불필요 할지도 모르는 기능의 완벽한 품질 보다버그가 있더라도 사용자의 빠른 피드백을 받는게 중요..

사용자 피드백에 의한 요구사항 변경을 환영하는 마음 가짐을 갖는 것도 중요하지만 대응 가능한 소스코드를 갖고 있는 것 역시 중요

Q & A감사합니다

Recommended