tdd (android unit test)

47
TDD (Android Unit Test) ACT Lab Kihoon Kim ([email protected])

Upload: kihoon-kim

Post on 12-Apr-2017

1.297 views

Category:

Software


2 download

TRANSCRIPT

Page 1: TDD (Android Unit Test)

TDD(Android Unit Test)

ACT LabKihoon Kim

([email protected])

Page 2: TDD (Android Unit Test)

test the program before you write it- kent beck -

Page 3: TDD (Android Unit Test)

TDD (Test Driven Development)

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

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

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

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

Page 4: TDD (Android Unit Test)

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. 현재 실패한 테스트 코드를 성공시키는데 충분한 정도로만 제품 코드를 작성한다.

Page 5: TDD (Android Unit Test)

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

Page 6: TDD (Android Unit Test)

Design Coding Test

Design Test Coding

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

Refactoring

Refactoring

Page 7: TDD (Android Unit Test)

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

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

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

Page 8: TDD (Android Unit Test)

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

Page 9: TDD (Android Unit Test)

저희 팀은 이렇게 해요

Page 10: TDD (Android Unit Test)

Code

User Story

UI Design

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

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

[Dev]구현, 테스트

Page 11: TDD (Android Unit Test)

로그인 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

Page 12: TDD (Android Unit Test)

UI Design : Designer

Page 13: TDD (Android Unit Test)

코딩을 해볼까요 : Dev

Page 14: TDD (Android Unit Test)

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”

...}

Page 15: TDD (Android Unit Test)

LoginActivity 를 만들어야 겠죠

LoginActivity 도 만들고

AndroidManifest 에도

선언해 줍니다.

IDE의 도움을 받으면

편합니다.

Page 16: TDD (Android Unit Test)

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

만약 화면이 복잡하다면

현재 개발 할

User Story 에 나와있는

부분만 먼저 만들어요

Page 17: TDD (Android Unit Test)

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

# given

ID/PW가 입력된 상황# when

로그인 버튼 클릭되면

# then

다른 화면으로 이동

하는 것을 테스트

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

Test Runner

Test Case

Robolectricassertj

Page 18: TDD (Android Unit Test)

Run Test Code

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

Page 19: TDD (Android Unit Test)

동작하는 구현코드 작성

이동 할 다음 화면 생성

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

빈 Activity 생성

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

Page 20: TDD (Android Unit Test)

테스트 성공

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

Page 21: TDD (Android Unit Test)

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

테스트 코드 변경 없음

로그인 처리를 위한 Interface 생성

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

Page 22: TDD (Android Unit Test)

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

Mockito

mocking

stubbing

Page 23: TDD (Android Unit Test)

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

login fail

Toast 안 뜸

Page 24: TDD (Android Unit Test)

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

로그인 실패시

“Login Fail” 메시지를

Toast로 띄워주는 로직 추가

Page 25: TDD (Android Unit Test)

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

Page 26: TDD (Android Unit Test)

LoginService

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

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

Page 27: TDD (Android Unit Test)

무엇을 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

Page 28: TDD (Android Unit 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);

Page 29: TDD (Android Unit Test)

잘 정리된 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/

Page 30: TDD (Android Unit Test)

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

Page 31: TDD (Android Unit Test)

Test Driven Development==

Test First Development??

Page 32: TDD (Android Unit Test)

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

Page 33: TDD (Android Unit Test)

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

Test First 하는게 좋아요

BUT

Page 34: TDD (Android Unit Test)

개인적으로Test First 보다

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

Page 35: TDD (Android Unit Test)

Test Code를 확보하는 것

Refactoring

Side effect

회귀 테스트 (Regression Test)

빠른 피드백

Page 36: TDD (Android Unit Test)

CI + Test Automation

반복 수행 가능

빠른 피드백

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

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

기계 사람

Page 37: TDD (Android Unit Test)

Manual Test + Bug Hunting

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

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

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

Page 38: TDD (Android Unit Test)

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

Page 39: TDD (Android Unit Test)

Divide and Conquer

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

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

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

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

Page 40: TDD (Android Unit Test)

Good Enough + Time Boxing

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

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

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

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

Page 41: TDD (Android Unit Test)

거대한 객체를 만들지 말자

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

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

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

Super Giant Huge Class

Service or Activity ??

Page 42: TDD (Android Unit Test)

객체의 역할, 책임, 협력

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

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

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

message

message

Page 43: TDD (Android Unit Test)

자율적인 객체

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

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

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

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

Page 44: TDD (Android Unit Test)

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

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

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

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

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

message

Page 45: TDD (Android Unit Test)

사용하는 언어를 통일하자

개념적 설계, 구조적 설계

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

DESIGNER USER

SYSTEM

Page 46: TDD (Android Unit Test)

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

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

Page 47: TDD (Android Unit Test)

Q & A감사합니다