java - मॉकिटो के साथ स्थिर तरीकों का मज़ाक उड़ाते हुए




unit-testing mocking (6)

Mockito के शीर्ष पर PowerMockito उपयोग करें।

उदाहरण कोड:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class Mocker {

    @Test
    public void testName() throws Exception {

        //given
        PowerMockito.mockStatic(DriverManager.class);
        BDDMockito.given(DriverManager.getConnection(...)).willReturn(...);

        //when
        sut.execute();

        //then
        PowerMockito.verifyStatic();
        DriverManager.getConnection(...);

    }

अधिक जानकारी:

मैंने java.sql.Connection ऑब्जेक्ट्स बनाने के लिए एक कारखाना लिखा है:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return DriverManager.getConnection(...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

मैं DriverManager.getConnection को पारित पैरामीटर को सत्यापित करना चाहता हूं, लेकिन मुझे नहीं पता कि एक स्थिर विधि का नकल कैसे करें। मैं अपने परीक्षण मामलों के लिए जुनीट 4 और मॉकिटो का उपयोग कर रहा हूं। क्या इस विशिष्ट उपयोग-मामले को नकल / सत्यापित करने का कोई अच्छा तरीका है?


आप इसे थोड़ा सा रिफैक्टरिंग के साथ कर सकते हैं:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return _getConnection(...some params...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //method to forward parameters, enabling mocking, extension, etc
    Connection _getConnection(...some params...) throws SQLException {
        return DriverManager.getConnection(...some params...);
    }
}

फिर आप एक MySQLDatabaseConnectionFactory गए कनेक्शन को वापस करने के लिए, मापदंडों पर दावे आदि करने के लिए अपनी कक्षा MySQLDatabaseConnectionFactory का विस्तार कर सकते हैं।

विस्तारित वर्ग टेस्ट केस के भीतर रह सकता है, अगर यह उसी पैकेज में स्थित है (जो मैं आपको करने के लिए प्रोत्साहित करता हूं)

public class MockedConnectionFactory extends MySQLDatabaseConnectionFactory {

    Connection _getConnection(...some params...) throws SQLException {
        if (some param != something) throw new InvalidParameterException();

        //consider mocking some methods with when(yourMock.something()).thenReturn(value)
        return Mockito.mock(Connection.class);
    }
}

निरीक्षण: जब आप स्थैतिक इकाई के भीतर स्थैतिक विधि को कॉल करते हैं, तो आपको कक्षा को @PrepareForTest में बदलना होगा।

उदाहरण के लिए:

securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM);

उपरोक्त कोड के लिए यदि आपको MessageDigest क्लास का नकल करने की आवश्यकता है, तो उपयोग करें

@PrepareForTest(MessageDigest.class)

जबकि अगर आपके पास कुछ ऐसा है:

public class CustomObjectRule {

    object = DatatypeConverter.printHexBinary(MessageDigest.getInstance(SECURITY_ALGORITHM)
             .digest(message.getBytes(ENCODING)));

}

फिर, आपको इस कोड में रहने वाले वर्ग को तैयार करने की आवश्यकता होगी।

@PrepareForTest(CustomObjectRule.class)

और फिर विधि का मज़ाक उड़ाओ:

PowerMockito.mockStatic(MessageDigest.class);
PowerMockito.when(MessageDigest.getInstance(Mockito.anyString()))
      .thenThrow(new RuntimeException());

मेरा मुद्दा भी ऐसा ही था। स्वीकार किए गए उत्तर ने मेरे लिए काम नहीं किया, जब तक कि मैंने परिवर्तन नहीं किया: @PrepareForTest(TheClassYouWriteTestFor.class)

और मुझे BDDMockito का उपयोग करने की ज़रूरत नहीं है।

मेरी कक्षा:

public class SmokeRouteBuilder {
    public static String smokeMessageId() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("Exception occurred while fetching localhost address", e);
            return UUID.randomUUID().toString();
        }
    }
}

मेरी परीक्षा कक्षा:

@RunWith(PowerMockRunner.class)
@PrepareForTest(SmokeRouteBuilder.class)
public class SmokeRouteBuilderTest {
    @Test
    public void testSmokeMessageId_exception() throws UnknownHostException {
        UUID id = UUID.randomUUID();

        mockStatic(InetAddress.class);
        mockStatic(UUID.class);
        when(InetAddress.getLocalHost()).thenThrow(UnknownHostException.class);
        when(UUID.randomUUID()).thenReturn(id);

        assertEquals(id.toString(), SmokeRouteBuilder.smokeMessageId());
    }
}

स्थिर विधियों को चकमा देने के लिए सामान्य रणनीति जो आपके पास उपयोग करने से बचने का कोई तरीका नहीं है, लिपटे ऑब्जेक्ट्स बनाकर और रैपर ऑब्जेक्ट्स का उपयोग करके है।

रैपर ऑब्जेक्ट वास्तविक स्थैतिक वर्गों के लिए मुखौटे बन जाते हैं, और आप उनको परीक्षण नहीं करते हैं।

एक रैपर ऑब्जेक्ट कुछ ऐसा हो सकता है

public class Slf4jMdcWrapper {
    public static final Slf4jMdcWrapper SINGLETON = new Slf4jMdcWrapper();

    public String myApisToTheSaticMethodsInSlf4jMdcStaticUtilityClass() {
        return MDC.getWhateverIWant();
    }
}

अंत में, परीक्षण के तहत आपकी कक्षा इस सिंगलटन ऑब्जेक्ट का उपयोग कर सकती है, उदाहरण के लिए, वास्तविक जीवन उपयोग के लिए एक डिफ़ॉल्ट कन्स्ट्रक्टर है:

public class SomeClassUnderTest {
    final Slf4jMdcWrapper myMockableObject;

    /** constructor used by CDI or whatever real life use case */
    public myClassUnderTestContructor() {
        this.myMockableObject = Slf4jMdcWrapper.SINGLETON;
    }

    /** constructor used in tests*/
    myClassUnderTestContructor(Slf4jMdcWrapper myMock) {
        this.myMockableObject = myMock;
    }
}

और यहां आपके पास एक कक्षा है जिसे आसानी से परीक्षण किया जा सकता है, क्योंकि आप सीधे स्थिर विधियों वाले वर्ग का उपयोग नहीं करते हैं।

यदि आप सीडीआई का उपयोग कर रहे हैं और @ इंजेक्शन एनोटेशन का उपयोग कर सकते हैं तो यह और भी आसान है। बस अपने रैपर बीन @ एप्प्लिकेशंसकोप्ड बनाएं, उस चीज़ को एक सहयोगी के रूप में इंजेक्शन दें (आपको परीक्षण के लिए गन्दा रचनाकारों की भी आवश्यकता नहीं है), और मॉकिंग के साथ आगे बढ़ें।


स्थैतिक विधि का नकल करने के लिए आपको पॉवरमॉक का उपयोग करना चाहिए: https://github.com/powermock/powermock/wiki/MockStatic । मॉकिटो इस कार्यक्षमता प्रदान नहीं करता है

आप मॉकिटो के बारे में अच्छा लेख पढ़ सकते हैं: http://refcardz.dzone.com/refcardz/mockito





mockito