반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

Mockito로 테스트 하기

2013. 6. 17. 08:06 | Posted by 솔웅


반응형

오늘은 Lars Vogel 라는 친구가 작성한 Mockito 튜토리얼을 공부했습니다. 2012년 6월에 작성해서 2013는 2월에 Revision 됐으니까 아주 최근 글이네요. Mockito 에 대한 기본 설명과 예제가 있고 예제는 안드로이드 프로젝트에서 사용할 때의 예제를 보여 줍니다.

글 제목을 클릭하시면 원본이 있는 페이지로 가실 수 있습니다. 고맙게 자신이 어렵게 얻은 지식을 이렇게 공유해 주고 있는 친구에게 도움을 주는게 기본 예의 아닐까 합니다. 해당 페이지에는 Donation 할 수 있는 링크도 있고 광고도 있으니까 이 중 할 수 있는 방법으로 도움을 주실 수 있을 거예요. :)

 

Testing with Mockito - Tutorial

Lars Vogel

 

 


Version 1.1

02.05.2013


Testing with Mockito

이 튜토리얼은 이클립스에서 모키토 프레임워크로 테스트 하는 방법을 설명하고 있습니다.


 


1. Prerequisites

아래 튜토리얼은 JUnit 프레임워크로 단위테스트하는 것을 기반으로 작성했습니다.

JUnit 를 잘 모르신다면 JUnit Tutorial 를 참조하세요.

 


2. Testing with mock objects


Unit testing은 클래스나 메소드별로 별도로 테스팅하는 것을 말합니다.


자바 클래스들은 대개 다른 클래스들에 의존 합니다. mock object는 특정 메소드 호출에 어떤 output을 정의한 인터페이스나 클래스에 대한 dummy implementation을 말합니다. Mock object들은 dependency 없이 테스트 되어야 하는 클래스에 대한 단위테스트를 만들 때 유용하게 사용됩니다.


코드를 작성해서 이 mock object들을 생성하거나 이 클래스들을 simulate 하기 위해 mock framework을 사용할 수 있습니다. Mock frameworks는 runtime에 mock object들을 생성할 수 있도록 하고 그 behavior를 정의할 수 있도록 해 줍니다.

mock object에 대한 고전적인 예제는 data provider를 들 수 있습니다.  production에서는 실제 데이터베이스가 사용되지만 테스팅에서는 mock object를 사용해서 데이터베이스를 simulate 해야 합니다. 실제 데이터베이스를 사용할 때와 똑같은 상황을 만들어 놓고 테스트를 해야 되는 거죠.

이 mock object들은 테스트 될 클래스에 제공될 수 있습니다. 그래서 테스트 해야할 클래스가 외부 데이터에 대한 의존(dependency) 없이 동작하고 그 결과를 테스트 할 수 있습니다.

Mock frameworks는 또한 mock object 와의 상호작용(interaction)을 가능하게 해 그에 따른 테스트를 진행할 수 있도록 합니다. 예를 들어 mock object에 의해 call 된 메소드를 테스트를 하는 경우가 이 경우가 되겠죠.


 


3. Mockito


3.1. The Mockito testframework

Mockito 프레임워크에 대해서는 Mockito homepage 에 가시면 자세한 내용을 보실 수 있습니다.

Mockito 는 여러분에게 항상 mock objects를 생성할 수 있도록 기능을 제공합니다. 이 mock objects들은 method를 호출할 때 Pass 되도록 할 수 있어 그 메소드를 테스트 할 수 있도록 도와 줍니다.

 

3.2. Using Mockito

Mockitomock() 메소드를 call 해서 생성하거나 @Mock annotation에 근거해서 생성할 수 있습니다. annotation이 사용됐다면 MockitoAnnotations.initMocks(this) method call을 사용해서 초기화 해야 합니다.

아래의 예제를 보세요.

 

public static Intent createQuery(Context context, String query, String value) {
    Intent i = new Intent(context, MyListActivity.class);
    i.putExtra("QUERY", query);
    i.putExtra("VALUE", value);
    return i;
  } 

 

이 메소드는 Context 에 대해 mock object를 만들어서 테스트 할 수 있습니다.

 

package com.vogella.android.mockito.intent.test;

import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.test.AndroidTestCase;

import com.vogella.android.mockito.intent.MainActivity;

public class MainActivtityTest extends AndroidTestCase {
  @Mock
  Context context;

  @Override
  protected void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }

  public void testQuery() throws Exception {
    Intent intent = MainActivity.createQuery(context, "query", "value");
    assertNotNull(intent);
    Bundle extras = intent.getExtras();
    assertNotNull(extras);
    assertEquals("query", extras.getString("QUERY"));
    assertEquals("value", extras.getString("VALUE"));
  }
} 

Note

이 예제 코드는 단지 간단한 사용법을 보여 드리기 위한 겁니다. 이 예제는 안드로이드 프로그래밍을 예로 들었습니다. Android 에서 Mockito를 direct 하게 사용할 수 있습니다.

Tip

Static imports allows you to call static members, i.e. methods and fields of a class directly without specifying the class. If you declare Mockito.* as static member you can access methods like mock() directly.

 

 

Mockito 에는 아주 훌륭한 API 가 있습니다. verify() method 를 사용해서 해당 메소드가 호출 됐는지 여부를 확인 할 수 있습니다.

when(....)thenReturn(....) 를 사용하면 특정 상황에서의 return 값을 정해 줄 수 있습니다.

doReturn(object).when(kdskfsk).methodCall 도 비슷한 일을 합니다.

특정 상황을 체크하기 위해 mock object에 verify() method 를 사용해도 됩니다.

@Spy 나 spy() method 는 real object를 wrap 하기 위해 사용할 수 있습니다. Every call, unless specified otherwise is delegated to the object.

 

// Lets mock a LinkedList
List list = new LinkedList();
List spy = spy(list);

//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

// this would not work
// real method is called so spy.get(0)
// throws IndexOutOfBoundsException (list is still empty)
when(spy.get(0)).thenReturn("foo");

 

verifyNoMoreInteractions()는 더이상의 메소드 호출이 없음을 체크할 수 있도록 합니다.

 


4. Mockito on Android

release 1.9.5 Mockito 부터 Android 에서 direct로 사용할 수 있도록 됐습니다. 안드로이드 테스트 프로젝트에서 Mockito를 사용하려면 안드로이드 테스트 프로젝트의 libs 폴더에 아래 세개의 라이브러리를 추가 하시면 됩니다.

https://mockito.googlecode.com/files/mockito-all-1.9.5.jar
http://dexmaker.googlecode.com/files/dexmaker-1.0.jar
http://dexmaker.googlecode.com/files/dexmaker-mockito-1.0.jar 


 

5. Thank you

이 글이 괜찮으시다면 도움을 주세요.

여기 로 가시면 도움을 주실 수 있는 방법이 있습니다.


6. Questions and Discussion

질문을 올리시기 전에vogella FAQ 를 먼저 보시면 도움이 되실 겁니다. 이 글을 보시고 질문이 있으시거나 에러를 발견하셨다면 www.vogella.com Google Group를 이용해 주세요. how to create good questions 도 만들었습니다. 이 글을 보시면 여로분께 도움이 되실 겁니다.

 

7. Links and Literature  

7.1. EasyMock

EasyMock Homepage



반응형


반응형
 Tiffani - Brad Pitt Interview 영상
 

 

오늘 이 인터뷰 동영상이 떴길래 한번 받아 쓰기 해 봤어요.


Tiffany : How did you build to ... be able to .. take control of whole production and be a part of that at the same time

 

Brad Pitt : a want to just more responsibility but the you know I am really really thrill of with the with the finish film and people have so much fun with it ..

 

Tiffany : And is there any specific reason you want to take on this whold of the picture

 

Brad Pitt : My boys My boys love this kind of film so that's why I've got started. and I can carry out way from there


Tiffany : It is Actually be most highly anticipated film .. in this summer.. all around the world

 

 


Tiffany : Now we are a couple of hours away from your red carpet event for this movie in Korea.
  Anything you looking for it to...

 

Brad Pitt : Well I just want to say that I am great to be here. It's really so much.. so nice . It is just looking for people to see it because it is so much fun.. i mean .. it is just begins begins very quickly and then it's just doesn't let up to the temporaly(?) control..

 

Tiffany : There is one word that I'd like to teach you so you can say to all of your korean fans. It's call 대박.  you use that one like you want to say something like really cool it would like .. Could you tell to camera and all of your Korean fans. All of your voyage 대박.

 

 

암만 들어도 뭐라고 말하는지 감이 안 잡히는 단어가 몇개 있더라구요.

미국에서 살지만 수십년간 한국말만 쓰다가 와서 영어 사용이 힘들긴 합니다.

한국말은 회의 시간에 딴 짓 하면서 들어도 뭔 얘기인지 다 알아 듣지만 여기선 회의 할 때 정신 바짝 차리지 않으면 뭔 말 하는지 모르고 그냥 지나 가요...

 

그래서 회의 한번 하고 나오면 진이 빠지죠.

 

한국에서는 일하는데 에너지를 다 쏟으면 되지만 여기는 의사소통하는데에도 많은 에너지를 쏟아야 되서 확실히 한국에서 일하는 것 보다 더 힘든거 같아요.

 

Anyway... 혹시 저기 물음표로 표시한 단어가 확실하지 않은데..  뭐라고 말 했는지 아시는 분들은 좀 알려 주세요.

저건 아무리 들어도 잘 모르겠더라구요.

 

 

반응형


반응형

오랜만에 코로나 튜토리얼을 번역해 봅니다.

거르지 않고 꼬박꼬박 해 오다가 이번 TDD 프로젝트에 참여하면서 두달넘게 공부를 하지 못했네요.

오랫동안 모바일 앱을 만들지 못했는데... 이제 모바일 앱 하나 만들고 싶습니다.

 

----------------------------------------------------

 

Posted on . Written by

 

지난주의 "Goodbye Globals!" 튜토리얼에 이어서 오늘은 non-global 메소드 안에서 scene들 사이에서 오디오 파일들을 어떻게 관리할지에 대해 얘기 나누도록 하겠습니다. 대부분의 앱에서 특정 오디오 파일들은 전체 앱내에서 필요로 하는 경우들이 많습니다. 그리고 어떤 오디오 파일은 특정 scene 에서만 필요로 하는 경우도 있구요. 특정 scene에서 오디오 파일이 로드 돼서 다른 scene 에서도 overlap 되는 상황도 필요할 때가 있습니다. 이런 경우 scene 내의 객체들을 cleanup 하는 것과 별도로 오디오 파일을 관리해야 해서 좀 복잡하게 됩니다.

 

이런 경우 어떻게 관리 해야 할까요? main.lua 에서 common sound를 로드하고 그 사운드들에 global 로 처리하도록 할 겁니다. 그러면 어떤 scene 에서도 해당 사운드를 플레이 할 수 있죠. 그런데 왠만하면 이 global 한 객체들을 사용하지 않는게 좋습니다. 그리고 실제로는 글로벌이 아니면서 여러분만의 global space를 생성하는 방법을 알려 드린 지난주 튜토리얼을 이런 경우에도 활용할 수 있습니다.

 

 

 

Creating the “sfx.lua” Module

 

첫번째로 할 일은 오디오를 처리할 모둘 이름을 sfx.lua 로 해서 만듭니다. 처음 시작하는 몇 라인은 아주 기본적인 겁니다. 약간의 세팅과 데이터가 들어있는 myData.lua 모듈을 사용합니다. 이 내용은 지난주에 자세히 다뤘었습니다. 그리고 나서 변수들을 담을 namespace를 생성합니다. 

 

local myData = require( "mydata" )
local sfx = {}  --create the main Sound Effects (sfx) table.
return sfx

 

루아의 required 는 처음에 한번만 실행된다는 것을 기억해 두세요. 처음 시작할 때 어떤 세팅이나 configure를 core code 에서 할 수 있다는 얘기입니다.

 

예를 들어

 

sfx.boomSound = audio.loadSound( "audio/explosion2.wav" )
sfx.bigBoomSound = audio.loadSound( "audio/explosion.wav" )
sfx.killMeSound = audio.loadSound( "audio/idied.wav" )
sfx.missileSound = audio.loadSound( "audio/missile.wav" )
sfx.blasterSound = audio.loadSound( "audio/scifi048.wav" )
sfx.bossSound = audio.loadSound( "audio/scifi026.wav" )
sfx.shieldsSound = audio.loadSound( "audio/shields.wav" )


이제 간단하게 sound에 대해 처리 하는 부분을 만들겁니다. 그리고 오디오 API 에게 각각의 사운드를 로드하도록 합니다. 그리고 그 handle 을 sfx 테이블에 저장합니다. 루아에서는 이 테이블을 dot 이나 bracket syntax로 구현할 수 있도록 제공합니다. sfx.boomSound 나 sfx["boomSound"] 이렇게 두가지 방법으로 처리할 수 있습니다. 이렇게 하면 나중에 sound를 참조할 handle name을 사용할 수 있습니다. 예를 들어 어떤 scene에서 sfx.lua 가 필요하면 아래와 같이 해당 오디오 파일을 플레이 시키면 됩니다.

 

audio.play( sfx.boomSound )

 

더 나아가서 이 메소드를 extend 해서 추가적인 기능을 부여할 수도 있습니다. 아래와 같이 추가적으로 오디오 파라미터들을 셋업 할 수 있는거죠.

 

audio.reserveChannels( 5 )
masterVolume = audio.getVolume()
audio.setVolume( 0.80, { channel = 1 } )  --music track
audio.setVolume( 0.66, { channel = 2 } )  --boss sound
audio.setVolume( 1.0,  { channel = 3 } )  --voice overs
audio.setVolume( 1.0,  { channel = 4 } )  --alien voice
audio.setVolume( 0.25, { channel = 5 } )  --weak explosion

 

sfx 테이블에서 call 할 수 있는 init 함수를 간단히 생성할 수 있습니다.

 

sfx.init = function()
   audio.reserveChannels(5)
   sfx.masterVolume = audio.getVolume()  --print( "volume "..masterVolume )
   audio.setVolume( 0.80, { channel = 1 } )  --music track
   audio.setVolume( 0.66, { channel = 2 } )  --boss sound
   audio.setVolume( 1.0,  { channel = 3 } )  --voice overs
   audio.setVolume( 1.0,  { channel = 4 } )  --alien voice
   audio.setVolume( 0.25, { channel = 5 } )  --weak explosion
end

 

이제 masterVolume sfx 테이블에 저장됐습니다. 그리고 나중에 이런 다른 action들이 필요 하면 그에 맞는 액션을 사용할 수 있습니다.
그리고 다른 trick으로는 여러분 앱의 sound on/off 세팅을 담당할 audio.play 의 버전을 만들 수 있다는 겁니다. 예를 들어 아래와 같이 필요한 사운드를 필요할 때 사용할 수 있습니다.

 

if ( settings.soundOn ) then
   audio.play( "beep" )
end

 

sfx.play 라는 함수를 생성해서 audio.play 와 비슷하게 작동하도록 할 수 있습니다. 이  방법을 사용하면 sound setting을 좀 더 편하게 할 수 있습니다.

 

sfx.play = function( handle, options )

   if ( myData.settings and myData.settings.soundOn == false ) then
      --your settings dictate NOT to play this sound
      return false
   end

   --otherwise, one of three things is true:
   --1. myData.settings is nil. You haven't set up control, so play the sound.
   --2. myData.settings.soundOn is nil. You have settings, but not a soundOn flag.
   --   So, play the sound since you haven't set up control.
   --3. soundOn is true, which means you want to play the sound. So, play it!
   audio.play( handle, options )

end

Loading Scene-Specific Sounds

Storyboard 와 함께 오디오를 사용할 때 만날 수 있는 어려움들에는 아래와 같은 것들이 있습니다.


• 해당 scene의 main chunk에서 사운드를 로딩하면 문제가 발생할 수 있습니다.왜냐하면 reload를 하려면 scene을 remove 한 다음에 recreate을 시켜야 하기 때문이죠.


createScene 이벤트는 scene이 로드될 때마다 로드될 필요가 없습니다. 그리고 큰 사운드가 로드되면 transition이 delay 되게 될 겁니다.


enterScene 이벤트는 매번 fire 됩니다. 하지만 스크린에 해당 scene이 완전히 로드되기 이전에는 사운드가 로드되지 않는다는 특징이 있습니다.

 

위의 enterSceneexitScene은 한 쌍으로 발생됩니다. enterScene은 scene-specific sound를 로드할 최적의 장소일 것입니다. 그리고 나서 exitScene 이벤트에서 audio.dispose()를 사용해서 그것들을 처리하면 됩니다. (이 때 객체를 nil 처리하는 것을 잊지 마세요.)

 

그런데 만약에 앱이 새로운 scene으로 갈 때 sound 가 갑자기 끊기지 않고 계속 play 되어야 하는 상황이면 어떨까요? 다음 scene에서는 이 audio handle에 접근할 수 있는 방법이 더 이상 없습니다. 이 문제점을 해결 하려면 sfx테이블로 sound 를 로딩하시면 됩니다. 그리고 onComplete phase에서 anonymous 함수를 사용해서 dispose 시키면 됩니다.

 

아래 코드를 보세요.

 

local sfx = require( "sfx" )

-- forward declare the handle
sfx.longsound = nil

function scene:createScene( event )
   local group = self.view

   local background = display.newRect( 0, 0, display.contentWidth, display.contentHeight )
   background.x = display.contentCenterX
   background.y = display.contentCenterY
   group:insert( background )

   local function leaveScene(event)
      if ( event.phase == "ended" ) then
         storyboard.gotoScene( "b" )
      end
   end

   local button = display.newRect( 100,100,100,100 )
   group:insert( button )
   button:setFillColor( 255,0,255 )
   button:addEventListener( "touch", leaveScene )

end

function scene:enterScene( event )
    sfx.longsound = audio.loadSound("audio/mirv_missiles_online.wav")
audio.play( sfx.longsound, { onComplete = function()
                                      audio.dispose( sfx.longsound )
                                      end } )

 

end 

 

루아에서는 onComplete 이벤트가 call 할 수 있는 anonymous 함수를 사용할 수 있도록 제공합니다. 여러분은 이 메소드를 오디오 파일이 끝났을 때 dispose 하도록 할 때 사용하실 수 있습니다. sound 가 실제로 사용되기 전에 sfx table에서 sound handle 하도록 미리 선언해둬야 한다는 것을 잊지 마세요.

 

여러분은 모듈에서 오디오를 처리하는 것의 장점을 아실 겁니다. 특히 Storyboard나 다른 scene manager utility를 사용할 때 말이죠. 오디오는 조심스럽게 그리고 제대로 관리 되어야 한다는 사실을 명심 하셔야 합니다. 물론 그 오디오 파일을 dispose 시키는 것도 말이죠. 그렇게 함으로서 메모리 누수를 방지하고 다른 이상한 문제점들을 방지할 수 있습니다.

 

반응형