2021년 7월 2일 금요일

Smoke Testing Sanity testing

Smoke Testing

-간단한 문제를 찾아보는 예비 테스트 

-초기 빌드 성공

-stable 과정  

-어느 코드에 구체적인 에러를 의미하는 게 아닌 거시적인 면에서 STATUS (Pass or fail)확인 

-문서화 함 

-Acceptance testing의 하위호환

-사용 주체는 개발자나 테스터 즉, white or black box에 속함 


sanity testing

-smoke testing 이후 수행 

-버그 픽스 확인 

-Regression testing의 하위호환 

-문서화 안 함 (테스터 경험에 의존 )

-사용 주체는 테스터 


What is Smoke Testing? How to do with EXAMPLES (guru99.com)

Sanity Testing Vs Smoke Testing: Introduction and Differences (guru99.com)

2021년 6월 28일 월요일

Android Test와 Mockito

Android Test를 하는 습관이 필요하다. UnitTest, Integration Testing, Functional testing 세가지를 나눠서 각각 상황과 시기에 적합한 테스트를 판단하고 진행한다.

UnitTest는 Base가 되는 뿌리라고 생각한다. 할 수 있다는 자신감은 언제나 필요한 덕목이지만 근거 없는 자신감은 코드 에러를 발생하게 한다.  내 자신감을 뒷 받침 해줄 수 있는 역할로 Unit Test를 수행한다. UnitTest에 주로 사용하는  Junit과 Robolectric testing 도구들은 범용성으로 구별할 수 있다. Junit의 경우 자바를 채택한 환경에서 돌아가는 반면, Robolectric 은 안드로이드 맞춤형으로 제작되어 테스트 할 때 간편함을 배로 높여줄 수 있다. 특징이 명확하게 나눠지는 만큼 무엇을 선택하느냐에 따라서 장단점이 존재할 수 있지만, 나는 이러한 장단점 까지 고려할 필요는 없을 것 같다.  

Integration Testing는 하나의 시스템을 이뤘을 때, 예상한 결과를 도출하는지 확인하는 과정이다. '하나의 시스템을 이룬다'는 2개 이상의 모듈 결합과 구체적인 시나리오 수행을 의미한다. 통합이라는 의미를 가진 테스트 방식에는 역시나 여러 상황을 요구할 수 있다.  Android Test를 중심으로 예를들자면, UI Test가 요구되는데 수고스럽게 특정 상황까지 만들어서 하기는 여러 비용이 소비된다. Espresso UI Test framework 를 결합해서 사용하면 UI를 통합한 테스트를 쉽고 빠르다는 이점이 존재한다. UI Test Framework는 방금 소개한 Espresso 이외에  Robotium, UIAutomator 등이 대표적이다. 

Mockito 라이브러리는 mock생성,증명(verification ) 그리고 stub를 원활하게 하도록 static method mock(), verify() 등을 지원한다. android studio dependencies작성 주의사항으로는 androidTest는 mockito-android x.x.x 를 적용해야 한다는 점, 자세히 알아 보지 않고서 섣부른 판단으로 나 처럼 mockito-core 를 적용하는 헛지거리를 하지 않길 바란다. 


Mockito를 알아보기 위해서는 여기를 참고하자ㅎㅎ.. 간단 명료하게 작성되어 있다.  

Answer (Mockito 3.11.2 API) (javadoc.io)



2021년 6월 8일 화요일

void Void Unit

 Void -> void wrapper


In Kotlin 


Void non-null type 


change the Unit or Void? (? operator)  


nothing 이라는 타입이 있기에 표현식 가능 


어래 링크에 식이라는 의미를 간결하게 설명하고 있음 

[Kotlin] 코틀린으로 보는 문(statement)과 식(expression) (leocat.kr)

2021년 6월 6일 일요일

코틀린 코루틴

 코루틴 보단 RX를 선호하는 편이지만 github를 보면 자주 보이는지라 

정리해보고자 글을 씁니당 

CoroutineScope 

코루린 영역 말 그대로 라이프사이클를 제어할 수 있다 . 코투린 시작을 위해서 꼭 필요하다 
coroutineScope에서 다른 coroutineScope를 생성할 수 있다. 

위와 같은 상황이 벌어지면 
 부모 자식관계를 형성


CoroutineContext 


코루틴 행동의 요소모음집(?) 이다 
CoroutineScope(Dispatchers.Default + job)
Dispatchersjob은 모두 context로 들어간다 
디버깅에 유용한 CoroutineName
Exception은  CoroutineExceptionHandler  으로 가능하다 

코루틴 내부 코루틴 실행에 경우 오버라이드 되어 수행 
즉, 부모 코루틴에 Context는 크게 신경 안씀 ..default라면 모를까 
-이러한 경우 부모에서 설정한 job과 자식에서 리턴되는 job Instance는 다름 

Job LifeCycle 

Active -> Cancel -> Cancelled
Active -> Completing -> Completed 

스코프 강제 취소나 스코프 영역 작업이 상황파악 될 수 있도록 State를 확장함수로 두고 사용해도 괜잖은 것 같다..


CoroutineBuilder

  • launch 리턴 값 없음 
  • async 리턴 값 존재, await로 시작 fire!! parallel 수행 
    -주의해야 할 점은 바로 선언과 동시에 Await 하면 
    withContext와 다를게 없음.. 
  • withContext parallel 아님 , await 없이 바로 수행 
  • runBlocking 말 그래도 Blocking (호출된 Thread 사용 -> Main에서 사용 X)



참고 

2021년 6월 5일 토요일

Scoped Storage

 Android Q(10) 부터는 스토리 쓰기에 WRITE_STORAGE_PERMISSION 필요없다 

개인공간(App 내부 파일) 별도의 읽기,쓰기 퍼미션이 필요 없다.  외부저장소 접근(READ)은

 READ_EXTERNAL_STORAGE 퍼미션이 필요하다. Write(쓰기)에서 생성시 

MediaStore를 통해서 파일을 생성할 수 있고, 특정 경로에만 저장 가능하다. 

예를 들어  이미지 경로를 따로 정해서 저장할 수 있는 게 아니라 

DCIM,Pictures 경로 설정을 해줘야 저장이 가능하다 

아니면 allowed directories are DCIM, Pictures  에러가 발생한다.

경로 설정 과정에서 헛갈릴 수 있는 게 외부저장소는

Application External storage directories 아닌

 External storage directories를 의미 

 이제 .. MediaStore나 SAF를 사용하여 외부저장소에서 파일을 해결해야 할 것 같다...



또한, 외부저장소 파일 수정,삭제하기 위해서는 별도의 Method가 존재 

  • createWriteRequest
  • createDeleteRequest


참고

이제는 Android Scoped Storage를 준비해야 할 때 | by Wyatt | Medium

[Android] 내부 저장소 경로 (tistory.com)

안드로이드 - MediaStore에 미디어 파일 저장하는 방법 (codechacha.com)


2021년 6월 3일 목요일

Retrofit2 적응기

Query , Path

overflow에도 헛갈리는 유저가 있는 것 같다.
1. 쿼리는 질의에 사용 
2. Path는 url 주소 중간 중간에 결로 변경을 목적으로 사용 

TimeOut

connectTimeout(Duration duration) – Tcp socket을 타켓 host 연결  타임아웃 설정

readTimeout(Duration duration) – data 전송 타임아웃 설정 

writeTimeout(Duration duration) – 서버에 send 시간 제한


OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.MILLISECONDS) .build();

0으로 하면 테스트할 때 강제 예외 발생시킬 줄 알았지만 그게 아니었다. 

0은 타임아웃 없음을 의미 , 1 and {@link Integer#MAX_VALUE} 사이 값으로 해야 함 

 

Logging


Retrofit2 테스트, 로그를 확인

1. HttpLoggingInterceptor class 안에 setLevel 함수에서 필요한 레벨을 정하자 

2.body, Basic, Header, None 
레벨 설정 이후에 

3.OkHttpClient class에 addInterceptor() 함수안에 logging 객체를 집어넣자 



Retrofit 2 - Handle Connection Timeout Exception - HowToDoInJava

2021년 5월 27일 목요일

ExoPlayer - tutorial

exoplayer 링크 


Media Playback on Android with ExoPlayer: Getting Started | raywenderlich.com

처음 시작한다면 여기 

왜? 해야하는지에 대한 설명이 좋다. 생략한 부분도 꽤 있지만 .. 



 UI components - ExoPlayer

전반적인 애기를 다루고 있다. 상세하게 하나하나 다 다루는건 아니지만 

큰 맥락에서는 참고하면 좋은 사이트 


2021년 5월 25일 화요일

kotlin 어노테이션 선언 방법

기존 JAVA

public @Interface Temp{

  String name();

}

그리고 Kotlin

annotation class Temp{

  val name: String

@Temp(name = "MEME")

class Tempest{

}


2021년 5월 22일 토요일

Android Content Providers with link

 Content URIs

<prefix>://<authority>/<data_type>/<id>

authority에는 contactsbrowser 등등 ..

 

Query함수

-안드로이드에서 이미지나 오디오 정보를 알기 위해서 사용

Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

uri - application에 접근가능한 주소

projection - 계획이란 의미를 가지고 있다. 얻고자 하는 Data정보를 넣어주자 좀 더 이야기를 한다면

MediaStore를 기반으로 Audio,Image,video ,Download로 세분화

selection  - "${MediaStore.Audio.Media._ID} = ?" 같이 where 역할

selectionArgs - selection ?에 들어가는 value 

Interaction between ContentProvider, other classes, and storage.

Figure 2. Interaction between ContentProvider, other classes, and storage.

DB에 직접적인 CRUD 하는 게 아니라면 (이미지나..,오디오 파일이나..)

ContentProvider로 class확장할 필요 없이 

ContentResolver로 Query로..



Android - Content Providers - Tutorialspoint

ContentProviderSample/MyContentProvider.java at master · iamkavinprabhu/ContentProviderSample (github.com)

android - How can I get only jpg or png images from cursor - Stack Overflow

@BindingAdapter 어떻게..할까

 잊지말자 

Companion Object는

statice 클래스를 만들뿐 그안에 함수까지 static으로 두는 것은 아니다 

그렇다면 Companion Objects 함수는 JVM에서 진짜 static은 아니다 

그럼 어떻게 해야 할까 

여러 방법이 있겠지만 @JvmStatic을 해당 함수에 붙여주자

아니라면 코틀린 파일(kt)을 따로 만들어서 fun을 적어주자 그러면 kotlin은 real static methods  간주한다


여기 사이트에는 2가지 방법이 나오는데 내게는 후자가 간단하다.

Defining Android Binding Adapter in Kotlin | by Herman Cheung | Medium

Databinding에 필요한 XML 문법

 DataBinding을 하면서 xml에 사용해야 할 문구의 필요성을 느꼈다 


  • Mathematical + - / * %
  • String concatenation +
  • Logical && ||
  • Binary & | ^
  • Unary + - ! ~
  • Shift >> >>> <<
  • Comparison == > < >= <= (Note that < needs to be escaped as &lt;)
  • instanceof
  • Grouping ()
  • Literals - character, String, numeric, null
  • Cast
  • Method calls
  • Field access
  • Array access []
  • Ternary operator ?:


2021년 5월 16일 일요일

RecyclerView LifeCylce

 RecyclerView는 3개의 컴포넌트로 이루어진다 

LayoutManager 

item Animator 

Adapter 


apapter는 itemViewType에 따라서 ViewHodler를 생성 

Recycler pool에 요청했지만 없을 경우 

CreateViewHolder에 따라서 생성한다 

RecyclerView pool에서 ViewHolder가 존재하기 때문에 에를들어 프로그레스 UI를 9번째 item에서 나타게 하고 싶으면 아마도 .. ViewType에 position을 매개변수로 한 함수를 사용하면 될 것 같은 느낌이다..  

순서

bindViewHolder -> onViewAttachedToWindow 


(3) RecyclerView ins and outs - Google I/O 2016 - YouTube

Android Recycler View Lifecycle diagrams and notes (landenlabs.com)

Recycler View 제대로 이해하기 - RecyclerView lifecycle (tistory.com)



2020년 11월 10일 화요일

임시테스트

 


2020년 6월 27일 토요일

[Android] firebase database 데이터 삭제 방법

firebase Database 데이터 삭제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Query query =databaseReference.child("message").orderByChild("userName").equalTo(editTextName.getText().toString());
                query.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                        for(DataSnapshot ds:dataSnapshot.getChildren()){
                      Log.d("getKey",ds.getRef().toString());
                            ds.getRef().removeValue();
                            //Toast.makeText(MainActivity.this, dataSnapshot.getKey().toString(), Toast.LENGTH_SHORT).show();
                        }

                    }
                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {
                    }
                });
cs




equalTo를 이용하여 userName값이 "temp"인지 확인합니다.
snapshot은 말 그대로 한 순간이죠. 여기엔 쿼리(위에서 언급한 equalTo..)에 맞는 특정 데이터 값이 들어있습니다. 
꼭 dataSnapshot.getChildren() 를 하고서 참조로 접근해야 합니다.
그냥 참조로 냅다 접근해서 삭제하면 Tree 방식으로 이루어져 있기 때문에 전부 삭제될 수 있습니다.

2020년 6월 25일 목요일

Java -> POJO ,POJO -> Java

서론 개소리
여태것 뭐하다 이런걸 이제 알았나 싶다 ..
개념적으로 어려운건 아니지만 사실 이걸 지금 알았다는 내 자신을 돌아보는 계기가 되었다. 난 과연 개발자의 삶을 살려고 하는 것인가 'ㅅ'
여튼 짧게나마 포스팅 해보려 한다.


본론






참고
어떻게 맵핑 방식이 이뤄지는지 상세하게 나와있다. 내가 찾아본 사이트 중 가장 읽기도 좋고 설명도 잘 되어있어던 것 같다.
http://tutorials.jenkov.com/java-json/jackson-objectmapper.html

http://tutorials.jenkov.com/java-json/jackson-annotations.html

상세한 작동 원리 이런거 보다는 간단한 이해와 사용법만 알고 싶다면 아래 사이트를 추천한다.
https://www.geeksforgeeks.org/convert-java-object-to-json-string-using-jackson-api/

혹시 찾고 있을지 모르는 사용자에게  Json에서 ->object 과정을 프리뷰 할 수 있는 사이트를 첨부한다.
http://www.jsonschema2pojo.org/

2020년 6월 24일 수요일

[Android] firebase Image Upload (이미지 업로드하기 )


어제 해본거라 이 글을 작성한 날짜 기준으로는 deprecate된 것도 없고 작동 아주 잘 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void uploadImage() {
        if(fileUri != null)
        {
            final ProgressDialog progressDialog = new ProgressDialog(this);
            progressDialog.setTitle("이미지 업로드...");
            progressDialog.show();
            //UUID 기기식별 firebase 폴더/파일명
            StorageReference ref = storageReference.child("images/"+ UUID.randomUUID().toString());
            //파일입력
            ref.putFile(fileUri)
                    .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                        @Override
                        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                            progressDialog.dismiss();
                            Toast.makeText(MainActivity.this"업로드 완료", Toast.LENGTH_SHORT).show();
                        }
                    })
                    .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            progressDialog.dismiss();
                            Toast.makeText(MainActivity.this"업로드 실패 "+e.getMessage(), Toast.LENGTH_SHORT).show();
                        }
                    })
                    .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
                        @Override
                        public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
                            double progress = (100.0*taskSnapshot.getBytesTransferred()/taskSnapshot
                                    .getTotalByteCount());
                            progressDialog.setMessage("Uploaded "+(int)progress+"%");
                        }
                    });
        }
    }
cs

fileUri  파일 URI같은 경우에는 함수 밖에서 설정 하였다.

ProgressDialog  Toast 매세지와 비슷하게 현재 진행상황을 표시하여 유저가 현 상황을 인식하도록 할 수 있다.
OnProgressListener ControllableTask 가 기능을 수행될 때 호출된다.
그러면 ControllableTask 뭘까 비동기적 운용을 표현해준다. 밑에를 보면 멈춤,취소와 같은 제어적 표현을 비동직적으로 나타낸다라고 되어있다. 이름 그대로 제어할 수 있는 기능을 갖췄다 라고 알아두고 넘어가려구 한다. 
(Represents an asynchronous operation that can be paused, resumed and canceled. This task also receives progress and other state change notifications.)


  • UploadTaskSnapshot   uploadTast에서 작동중인 상황을 transferred와 TotalByte를 통해 가져올 수 있다. 그 외의 메소드 2개가 더 있는데 궁금하면 참고에 사이트가 있으니 들어가 보자.
  • 혹여나 이렇게 해놓고 파일 URI도 완벽하게 가져오는데 안된다면 Toast 메세지를 확인해보라 필자의 경우 권한 인증(구글로그인, 익명로그인 등)을 안해줘서 거부당했으니  


참고
https://firebase.google.com/docs/storage/android/upload-files?hl=ko
https://firebase.google.com/docs/reference/android/com/google/firebase/storage/UploadTask.TaskSnapshot

[Android] 갤러리 이미지 읽기, 쓰기 코드

할 때 마다 애매한 부분이 있어서 찾아보게 되는 고생을 덜기 위해 정리해 보았다 ;;



1
2
3
4
5
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
       temp += Manifest.permission.READ_EXTERNAL_STORAGE + " ";
   }
   if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
       temp += Manifest.permission.WRITE_EXTERNAL_STORAGE + " "; }
cs

 " "로 구분자를 넣은 이유는 split로 String 배열을 만들어 넘겨주기 위해서다.
requestPermissions의 인자값을 보면 permissions 이 String[] 으로 선언되어서 사용자의 여러 요청을 받을 수 있다. 

public static void requestPermissions(final @NonNull Activity activity,        final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode)


1
2
3
4
  if (TextUtils.isEmpty(temp) == false)
       {//split는 특정 구분문자를 중심으로 배열로 바꿔준다.
           ActivityCompat.requestPermissions(this, temp.trim().split(" "),1);
       }
cs

사용자에게 요청을 해야 이미지를 비트맵으로 바꾸던가 해서 안드로이드 view에 설정할 수 있다. 

2020년 6월 6일 토요일

[알고리즘] 깊이 탐색 알고리즘

깊이 탐색 알고리즘 이란 ?
한 노드를 탐색할 때 재귀적으로 탐색하여 마지막 노드 까지 내려갔다가 다시 위로 올라와서 시작한다.

Depth-First Search
방식 :
https://en.wikipedia.org/wiki/Depth-first_search#/media/File:Depth-First-Search.gif


[자료구조] 그래프 자료구조

https://gmlwjd9405.github.io/2018/08/15/algorithm-bfs.html

2020년 6월 3일 수요일

[Kotlin] if-else 조건문

kotlin의 if - else 조금은 생소하다.

val num:Int =10;
    val temp:Int = if(num>5){
        println("num은 5보다 크지")
        num
    }else{
        println("num은 5보다 작다")
        -1
    }
    println(temp.toString())

우선 출력 결과
num은 5보다 크지 
10
이렇게 출력된다.

if-else 구문에서 println 밑에 num이 있다. if문이나 else에 해당하는 경우 밑에 있는 값이 temp의 값이 된다. 만약 ,
println("num은 5보다 크지")
        num
println("메롱")

이렇게 된다면 어떻게 될까 , 보기 좋게 에러가 출력되었다. ㅇㅅㅇ

temp를 Int로 선언하였기에 Int값이 아니라면 곤란하다.

© 특히하고 특별한
Maira Gall