2011-10-27 6 views
0

私は何とかユニットテストにしようとしているシナリオの例:リファクタリングとテスト

方法は三つのパラメータを取得:Company namePlane Number 1Plane Number 2

public Double getDistanceBetweenPlanes(Sting company, String plane1, String plane2) 
{ 
    -Apply some validation logic to see that plane numbers and company realy exists 
    -Use complex geLocation and setelate data to get plane location 

    return plane1.geoLocation() - plane2.geoLocation() ; 
} 

を、私はユニットテストにこれをしたいです。

私は実際の航空機番号を持っていません。私は衛星にライブ接続していません。

まだ私はこのAPIをテストできるように何とか全部を模擬できるようにしたいと思います。 それをリファクタリングする方法についてのご意見はありますか?テストのために作成する新しいパブリックメソッド

答えて

2

通常、テストされていないコードをリファクタリングするとき、私はTDDとBDDを練習するときに何をするのかを指しています。

まず最初に、(既存のコードの)1行= 1要件という単純な契約を書いておきます。 テストをコーディングしているうちに、私はテストしたくないことに気付くでしょう。この場合、DIは別のクラスに異なる責任を持つコードを削除するのに役立ちます。オブジェクト設計で作業するときは、異なる懸念のあるオブジェクト間の動作や相互作用に焦点を当てることが重要です。エイリアスクラスBDDMockitoであるMockitoは、あなたがこのアプローチを支持するのを助けることができます。

ここでは、できることの例を示します。

public class PlaneLocator { 
    private CompanyProvider companyProvider; 
    private PlaneProvider companyProvider; 
    private LocationUtil locationUtil; 

    // Constructor injection or property injection 

    public Double getDistanceBetweenPlanes(String companyId, String plane1Id, String plane2Id) { 
     Company company = companyProvider.get(company); 
     Plane plane1 = planeProvider.get(plane1Id); 
     Plane plane1 = planeProvider.get(plane1Id); 

     return locationUtil.distance(plane1.geoLocation(), plane2.geoLocation()); 
    } 
} 

簡単なJUnitテストは、(mockito 1.9.0からの機能を使用して)次のようになります。

@RunWith(MockitoJUnitRunner.class) 
public class PlaneLocatorTest { 
    @Mock CompanyProvider mockedCompanyProvider; 
    @Mock PlaneProvider mockedPlaneProvider; 
    @Mock LocationUtil locationUtil; 
    @InjectMocks SatelitePlaneLocator testedPlaneLocator; 

    @Test public void should_use_LocationUtil_to_get_distance_between_plane_location() { 
     // given 
     given(companyProvider.get(anyString())).willReturn(new Company()); 
     given(planeProvider.get(anyString())) 
       .willReturn(Plane.builder().withLocation(new Location("A"))) 
       .willReturn(Plane.builder().withLocation(new Location("B"))); 

     // when 
     testedPlaneLocator.getDistanceBetweenPlanes("AF", "1251", "721"); 

     // then 
     verify(locationUtil).distance(isA(Location.class), isA(Location.class)); 
    } 

    // other more specific test on PlaneLocator 
} 

そして、あなたは、次の依存関係が、がそれぞれどのように説明し、彼自身のユニットテストを持つ注射していますこのクラスは、入力と共同研究を考慮すると、動作します。

public class DefaultCompanyProvider implements CompanyProvider { 
    public Company get(String companyId) { 
     companyIdValidator.validate(companyId); 

     // retrieve/create the company 
     return company; 
    } 
} 
public class SatellitePlaneProvider implements PlaneProvider { 
    public Plane get(Plane planeId) { 
     planeIdValidator.validate(planeId); 

     // retrieve/create the plane with costly satellite data (using a SatelliteService for example) 
     return plane; 
    } 
} 

この方法を使用すると、はるかに低い結合でコードをリファクタリングすることができます。心配のよりよい分離は、コードベースのより良い理解、より良い保守性、およびより容易な進化を可能にするでしょう。

私はまた、さらに有名なアンクルボブ・マーティンからTDDにこのブログの記事変換優先前提http://cleancoder.posterous.com/the-transformation-priority-premise

+0

まさに私が探していたものです。私は、機能がほとんどないようなコードが割り当てられているように思えば、少し不快であると認めますが、これは挑戦的なことです。 –

0

jモックやイージーモックのようなモックを使用できます。最後の方法は、期待にsettedメソッドが呼び出される保証する

@Test 
public testMethod(){ 
Mockery context = new Mockery(); 
plane1 = context.mock(Plane.class); 
plane2 = context.mock(Plane.class); 

context.expectations(new Expectations(){{ 
    oneOf(plane1).geoLocation(); will(returnValue(integerNumber1)); 
    oneOf(plane2).geoLocation(); will(returnValue(integerNumber2)); 
}}); 
assertEquals(
    instanceUnderTest.method(plane1,plane2), 
    integerNumber1-integerNumber2 
) 
context.assertIsSatisfied() 
} 

:あなたはこのような何かを書くつもりjmockで

。そうでない場合、例外が発生し、テストは失敗します。

+0

おかげで、しかし問題は、メインにその入力されたの読み込みにリダイレクトしたいと思いますAPIメソッドはPlaneオブジェクトではなく、プレーンIDを持つ文字列であり、プレーンIDと企業IDを持つクエリに基づいて、Planeオブジェクトが呼び出されたメソッドのスコープ内に作成されます。 –

+0

あなたの質問でそれを明確にする必要があります:) – Brice