2012-08-09 7 views
26

kullanarak java'da yeni Date() nasıl yapılır Bazı hesaplamalar yapmak için geçerli saati kullanan bir işlev var. Ben mockito kullanarak alay etmek isterim.Mockito

sınıfın bir örneği test etmek istiyorum:

@Test 
public void testDoubleTime(){ 
    mockDateSomeHow(Date.class).when(getTime()).return(30); 
    assertEquals(60,new ClassToTest().getDoubleTime()); 
} 

o alay etmek mümkün mü:

public class ClassToTest { 
    public long getDoubleTime(){ 
     return new Date().getTime()*2; 
    } 
} 

Ben böyle bir şey istiyorum? Test edilmek için "test edilmiş" kodu değiştirmek istemem.

+3

Neden test edilen kodu değiştirmediniz? Daha test edilebilir kod genellikle daha gevşek bir şekilde birleştirilir ... neden bunu istemezdiniz? – blank

+0

[Java System.currentTimeMillis'i geçersiz kıl] olası bir kopyası (http://stackoverflow.com/questions/2001671/override-java-system-currenttimemillis) –

+0

... ve başka bir şey - 'test' kodunu değiştirmek kolaydır - siz ' bir hata yaptığınızda size söyleyecek testler var - diğer yandan test edilmemiş kodları değiştirmek ... Michael Feathers kung foo'ya ihtiyacınız var;) – blank

cevap

39

Yapmanız gereken en doğru şey, kodunuzu aşağıda gösterildiği gibi test etmek için yeniden yapılandırmaktır. Tarihinde doğrudan bağımlılık kaldırmak için kodunuzu yeniden yapılandırılması normal çalışma zamanı ve test çalışma zamanı için farklı uygulamaları enjekte izin verir:

interface DateTime { 
    Date getDate(); 
} 

class DateTimeImpl implements DateTime { 
    @Override 
    public Date getDate() { 
     return new Date(); 
    } 
} 

class MyClass { 

    private final DateTime dateTime; 
    // inject your Mock DateTime when testing other wise inject DateTimeImpl 

    public MyClass(final DateTime dateTime) { 
     this.dateTime = dateTime; 
    } 

    public long getDoubleTime(){ 
     return dateTime.getDate().getTime()*2; 
    } 
} 

public class MyClassTest { 
    private MyClass myClassTest; 

    @Before 
    public void setUp() { 
     final Date date = Mockito.mock(Date.class); 
     Mockito.when(date.getTime()).thenReturn(30L); 

     final DateTime dt = Mockito.mock(DateTime.class); 
     Mockito.when(dt.getDate()).thenReturn(date); 

     myClassTest = new MyClass(dt); 
    } 

    @Test 
    public void someTest() { 
     final long doubleTime = myClassTest.getDoubleTime(); 
     assertEquals(60, doubleTime); 
    } 
} 
+1

Ben hemfikirim. Her zaman böyle yaparım. Harika çalışıyor, orijinal koddaki değişiklik minimum ve test etmek kolay. – stmax

+12

Bu sorunu çözmenin klasik yolu budur ("DateTime" olarak adlandırdığınız şey daha açıklayıcı bir şekilde 'Clock' veya benzeri bir şey olabilir). Ancak bu, kodunuzun yeniden yapılandırılması ve testin yapılmasına izin vermek için birazcık karmaşıklık eklenmesi anlamına geliyor. –

+0

Bu yüzden, bence bu iyi bir yaklaşımdı, ancak soru Mockito ile nasıl yapılacağıydı: D –

6

Sen alay edebilmek için Mockito arttırır PowerMock kullanarak bu yapabilirdi statik yöntemler. O zaman en sonunda zaman alır mock System.currentTimeMillis(), olabilir.

Sizin olabilir. 'un'a sahip olup olmadığına dair bir fikir ileri sürmeyeceğim.

+1

Böyle bir şeyin nasıl yapıldığını bilmekle ilgileniyorum, bu sorunun amacı. Örnek var mı –

+0

Bu, [Powermock belgeleri] 'dir (http://code.google.com/p/powermock/wiki/MockSystem). PowerMockito – Brad

+1

ile aynı şekilde çalışmalıdır Her "yeni" yi bir sarıcı nesneye çevirmek ve enjekte edilen bağımlılık yoluyla bir dolaylılık oluşturmak, sadece kodu gereksiz yere zorlaştırır ve zorlaştırır. Sınıfınızda bir ArrayList veya HashMap oluşturuyorsanız, şimdi bir ArrayListFactory veya bir HashMapFactory oluşturacak ve bunu sınıfınıza enjekte edebilecek misiniz? PowerMock'u "yeni" kullandığınız her yerde kullanmamanız çok sıkı bir şekilde birleştirilmiş bir sistem yaratabilir. PowerMock gibi araçların iyi bir uyum olduğunu söyleyebilmek, yetenekli bir geliştiricinin parçası olmaktır. – Aneesh

14

Eğer planı ayrı olamaz ve System.currentTimeMillis() etkileyecek istemiyorum eski kodunuz varsa, doğrudan soruya cevap vermez Powermock ve PowerMockito

//note the static import 
import static org.powermock.api.mockito.PowerMockito.whenNew; 

@PrepareForTest({ LegacyClassA.class, LegacyClassB.class }) 

@Before 
public void setUp() throws Exception { 

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
    sdf.setTimeZone(TimeZone.getTimeZone("PST")); 

    Date NOW = sdf.parse("2015-05-23 00:00:00"); 

    // everytime we call new Date() inside a method of any class 
    // declared in @PrepareForTest we will get the NOW instance 
    whenNew(Date.class).withNoArguments().thenReturn(NOW); 

} 

public class LegacyClassA { 
    public Date getSomeDate() { 
    return new Date(); //returns NOW 
    } 
} 
1

bir yaklaşım kullanarak bu deneyin ama altında yatan çözebilir Sorun (tekrarlanabilir testlere sahip olmak), Date'un testler için bir parametre olarak izin vermesi ve varsayılan tarihe bir temsilci eklemesidir. böylece

public class ClassToTest { 

    public long getDoubleTime() { 
     return getDoubleTime(new Date()); 
    } 

    long getDoubleTime(Date date) { // package visibility for tests 
     return date.getTime() * 2; 
    } 
} 

üretim kodunda gibi

, sen getDoubleTime(Date date) karşı getDoubleTime() ve testi kullanın.