মকিতো দিয়ে স্থির পদ্ধতিগুলি উপহাস করা


371

আমি 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, তবে স্থির পদ্ধতিতে কীভাবে উপহাস করা যায় তা আমি জানি না। আমি আমার পরীক্ষার মামলার জন্য JUnit 4 এবং Mockito ব্যবহার করছি। এই নির্দিষ্ট ব্যবহারের ক্ষেত্রে উপহাস / যাচাই করার কোনও ভাল উপায় আছে?


1
এই সাহায্য করবে? stackoverflow.com/questions/19464975/...
sasankad

5
আপনি ডিজাইনের মাধ্যমে মকিতো দিয়ে পারবেন না :)
মারিউসজ

25
@ মারিউজস এটি ডিজাইনের মাধ্যমে নয় যে মকিতো (বা ইজিমক, বা জেমক) বিদ্রূপের staticপদ্ধতিগুলিকে সমর্থন করে না , তবে দুর্ঘটনাক্রমে । এই সীমাবদ্ধতা (উপহাসের finalক্লাস / পদ্ধতি, বা new-ড অবজেক্টগুলির জন্য কোনও সমর্থন ছাড়াই ) মশকরা বাস্তবায়নের জন্য নিযুক্ত পদ্ধতির একটি প্রাকৃতিক (তবে অনিচ্ছাকৃত) পরিণতি, যেখানে নতুন শ্রেণিগুলি গতিশীলভাবে তৈরি করা হয়েছে যা উপহাস করার জন্য প্রকারটি প্রয়োগ / প্রসারিত করে; অন্যান্য উপহাসাগুলি গ্রন্থাগারগুলি অন্যান্য পন্থাগুলি ব্যবহার করে যা এই সীমাবদ্ধতাগুলি এড়ায়। নেট নেট ওয়ার্ল্ডেও এটি ঘটেছিল।
রোজারিও

2
@ রোগারিও ব্যাখ্যার জন্য ধন্যবাদ। github.com/mockito/mockito/wiki/ FAQ আমি কি স্থির পদ্ধতিগুলি উপহাস করতে পারি? না। মকিতো স্থিতিশীল, পদ্ধতিগত কোডের চেয়ে অবজেক্ট ওরিয়েন্টেশন এবং নির্ভরতা ইঞ্জেকশনটিকে পছন্দ করে যা বুঝতে ও পরিবর্তন করা শক্ত। কিছু হয় নকশা এই সীমাবদ্ধতা পিছনে খুব :)
MariuszS

17
@ মারিউস এস আমি পড়েছি যে সরঞ্জামটি স্বীকার না করে বৈধ ব্যবহারের মামলাগুলি খারিজ করার প্রয়াস হিসাবে সীমাবদ্ধতা রয়েছে যা (সহজেই) সরানো যায় না এবং কোনও যুক্তিসঙ্গত ন্যায়সঙ্গততা প্রদান না করেই। বিটিডাব্লু, এখানে উল্লেখগুলি সহ বিপরীত দৃষ্টিভঙ্গির জন্য এমন আলোচনা
Rogorio

উত্তর:


350

মকিতোর শীর্ষে পাওয়ারমোকিটো ব্যবহার করুন ।

উদাহরণ কোড:

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

    @Test
    public void shouldVerifyParameters() throws Exception {

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

        //when
        sut.execute(); // System Under Test (sut)

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

    }

অধিক তথ্য:


4
যদিও এটি তাত্ত্বিকভাবে কাজ করে, অনুশীলনে খুব কঠিন সময়
কাটাচ্ছে

38
দুর্ভাগ্যক্রমে এর বিশাল অসুবিধা হ'ল যদিও পাওয়ারমকআরনারের প্রয়োজন।
ইনোকোন্টি

18
sut.execute ()? মানে?
তেজডে

4
সিস্টেম টেস্টের অধীনে, ক্লাসটির জন্য ড্রাইভারম্যানেজারের উপহাস করা দরকার। kaczanowscy.pl/tomek/2011-01/testing-basics-sut-and-docs
মারিউজএসএস

8
এফওয়াইআই, আপনি যদি ইতিমধ্যে JUnit4 ব্যবহার করছেন তবে আপনি এটি করতে পারেন @RunWith(PowerMockRunner.class)এবং এর নীচেও @PowerMockRunnerDelegate(JUnit4.class)
ইএম-ক্রিয়েশন

71

স্থিতিশীল পদ্ধতিগুলিকে ডড করার জন্য সাধারণ কৌশল যা আপনার ব্যবহার এড়িয়ে চলার কোনও উপায় নেই, তা হল মোড়ানো বস্তু তৈরি করে এবং তার পরিবর্তে মোড়কের জিনিসগুলি ব্যবহার করে।

মোড়কের জিনিসগুলি আসল স্ট্যাটিক ক্লাসের মুখোমুখি হয়ে যায় এবং আপনি সেগুলি পরীক্ষা করেন না।

একটি মোড়কের বস্তুর মতো কিছু হতে পারে

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;
    }
}

এবং এখানে আপনার কাছে এমন একটি ক্লাস রয়েছে যা সহজে পরীক্ষা করা যায়, কারণ আপনি সরাসরি স্থিতিশীল পদ্ধতি সহ কোনও শ্রেণি ব্যবহার করেন না।

আপনি যদি সিডিআই ব্যবহার করছেন এবং @ ইনজেক্ট টীকাটি ব্যবহার করতে পারেন তবে এটি আরও সহজ। কেবলমাত্র আপনার র্যাপার শিমটি @ অ্যাপ্লিকেশনসকোপড তৈরি করুন, সেই জিনিসটিকে একজন সহযোগী হিসাবে ইঞ্জেকশন দিন (আপনার পরীক্ষার জন্য অগোছালো কনস্ট্রাক্টরের দরকারও নেই), এবং উপহাস নিয়ে এগিয়ে যান।


3
আমি জাভা 8 "মিক্সিন" ইন্টারফেসগুলি স্বয়ংক্রিয়ভাবে উত্পন্ন করার জন্য একটি সরঞ্জাম তৈরি করেছি যা স্থির কলগুলিকে আবৃত করে: github.com/aro-tech/interface-it উত্পন্ন মিশ্রণগুলি অন্য কোনও ইন্টারফেসের মতো উপহাস করা যায়, অথবা যদি পরীক্ষার অধীনে আপনার শ্রেণীর "প্রয়োগসমূহ" ইন্টারফেস আপনি পরীক্ষার জন্য একটি সাবক্লাসে এর যে কোনও পদ্ধতি ওভাররাইড করতে পারেন।
অ্যার_টেক 21

25

আমারও একি দশা. ম্যাকস্ট্যাটিকের জন্য পাওয়ারমকের ডকুমেন্টেশন@PrepareForTest(TheClassThatContainsStaticMethod.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());
    }
}

? এবং JUnit 4 .when বর্তমানে .mockStatic জিনিসটা করতে পারা যাচ্ছে না
টেডি

পাওয়ারমক.মকস্ট্যাটিক এবং মকিতো.যখন মনে হয় কাজ করে না।
টেডি

যে কারও পরে এটি দেখার জন্য, আমার জন্য আমাকে পাওয়ারমকিতো.মকস্ট্যাটিক (স্ট্যাটিকক্লাস.class) টাইপ করতে হয়েছিল;
চিন্তাবিদ

আপনাকে পাওয়ারমক-এপি-মকিতো ম্যাভেন আর্টেরফ্যাক্ট অন্তর্ভুক্ত করতে হবে।
পিটারএস

23

পূর্বে উল্লিখিত হিসাবে আপনি মকিতো দিয়ে স্থির পদ্ধতিগুলি উপহাস করতে পারবেন না।

যদি আপনার পরীক্ষার কাঠামো পরিবর্তন করা কোনও বিকল্প না হয় তবে আপনি নিম্নলিখিতটি করতে পারেন:

ড্রাইভার ম্যানেজারের জন্য একটি ইন্টারফেস তৈরি করুন, এই ইন্টারফেসটিকে মক করুন, এটি কোনও প্রকার নির্ভরতা ইনজেকশনটির মাধ্যমে ইনজেকশন করুন এবং সেই মকটিতে যাচাই করুন।


7

পর্যবেক্ষণ: আপনি যখন কোনও স্ট্যাটিক সত্তার মধ্যে স্থিতিশীল পদ্ধতি কল করেন, আপনার @ParepareForTest এ শ্রেণি পরিবর্তন করতে হবে।

যেমন:

securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM);

উপরের কোডের জন্য আপনার যদি ম্যাসেজডিজাস্ট ক্লাসটি উপহাস করা প্রয়োজন, ব্যবহার করুন

@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());

আমি আমার স্থির শ্রেণিটি কেন উপহাস করছে না তা জানার চেষ্টা করে প্রাচীরের বিরুদ্ধে মাথা ঘুরছিলাম। আপনি ইন্টারভিউজের সমস্ত টিউটোরিয়ালে ভাববেন যে, খালি-হাড় ব্যবহারের ক্ষেত্রে একেরও বেশি intoোকা হত।
সফটওয়্যারসভ্যান্ট

6

আপনি এটি কিছুটা রিফ্যাক্টরিং দিয়ে করতে পারেন:

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কোনও উপহাসযুক্ত সংযোগ ফিরিয়ে দিতে, পরামিতিগুলিতে জোর দেওয়া ইত্যাদি করতে আপনার ক্লাস প্রসারিত করতে পারেন

প্রসারিত শ্রেণি পরীক্ষার ক্ষেত্রে থাকতে পারে, যদি এটি একই প্যাকেজে থাকে (যা আমি আপনাকে উত্সাহিত করি)

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);
    }
}

6

স্ট্যাটিক পদ্ধতিটিকে উপহাস করার জন্য আপনার একটি পাওয়ারমক চেহারাটি ব্যবহার করা উচিত: https://github.com/powermock/powermock/wiki/MockStatic । মকিতো এই কার্যকারিতা সরবরাহ করে না

আপনি মকিতো সম্পর্কে সুন্দর একটি নিবন্ধটি পড়তে পারেন: http://refcardz.dzone.com/refcardz/mockito


2
দয়া করে কোনও ওয়েবসাইটে লিঙ্ক করবেন না। উত্তরগুলিতে প্রকৃত ব্যবহারযোগ্য উত্তর অন্তর্ভুক্ত করা উচিত। যদি সাইটটি নীচে যায় বা পরিবর্তন হয় তবে উত্তরটি আর কার্যকর হবে না।
the_new_mr

6

মকিতো স্থিতিশীল পদ্ধতিগুলি ক্যাপচার করতে পারে না, তবে মকিতো ২.১৪.০.০ থেকে আপনি স্থির পদ্ধতির অনুরোধের উদাহরণ তৈরি করে এটি সিমুলেট করতে পারেন can

উদাহরণ ( তাদের পরীক্ষা থেকে নেওয়া ):

public class StaticMockingExperimentTest extends TestBase {

    Foo mock = Mockito.mock(Foo.class);
    MockHandler handler = Mockito.mockingDetails(mock).getMockHandler();
    Method staticMethod;
    InvocationFactory.RealMethodBehavior realMethod = new InvocationFactory.RealMethodBehavior() {
        @Override
        public Object call() throws Throwable {
            return null;
        }
    };

    @Before
    public void before() throws Throwable {
        staticMethod = Foo.class.getDeclaredMethod("staticMethod", String.class);
    }

    @Test
    public void verify_static_method() throws Throwable {
        //register staticMethod call on mock
        Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "some arg");
        handler.handle(invocation);

        //verify staticMethod on mock
        //Mockito cannot capture static methods so we will simulate this scenario in 3 steps:
        //1. Call standard 'verify' method. Internally, it will add verificationMode to the thread local state.
        //  Effectively, we indicate to Mockito that right now we are about to verify a method call on this mock.
        verify(mock);
        //2. Create the invocation instance using the new public API
        //  Mockito cannot capture static methods but we can create an invocation instance of that static invocation
        Invocation verification = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "some arg");
        //3. Make Mockito handle the static method invocation
        //  Mockito will find verification mode in thread local state and will try verify the invocation
        handler.handle(verification);

        //verify zero times, method with different argument
        verify(mock, times(0));
        Invocation differentArg = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "different arg");
        handler.handle(differentArg);
    }

    @Test
    public void stubbing_static_method() throws Throwable {
        //register staticMethod call on mock
        Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "foo");
        handler.handle(invocation);

        //register stubbing
        when(null).thenReturn("hey");

        //validate stubbed return value
        assertEquals("hey", handler.handle(invocation));
        assertEquals("hey", handler.handle(invocation));

        //default null value is returned if invoked with different argument
        Invocation differentArg = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "different arg");
        assertEquals(null, handler.handle(differentArg));
    }

    static class Foo {

        private final String arg;

        public Foo(String arg) {
            this.arg = arg;
        }

        public static String staticMethod(String arg) {
            return "";
        }

        @Override
        public String toString() {
            return "foo:" + arg;
        }
    }
}

তাদের লক্ষ্য সরাসরি স্ট্যাটিক বিদ্রূপকে সমর্থন করা নয়, তবে এর পাবলিক এপিআইগুলিকে উন্নত করা যাতে অন্যান্য গ্রন্থাগারগুলি যেমন পাওয়ারমকিতোকে অভ্যন্তরীণ এপিআইয়ের উপর নির্ভর করতে না হয় বা সরাসরি কিছু মকিতো কোড নকল করতে হয় না। ( উত্স )

দাবি অস্বীকার: মকিতো টিম মনে করে যে স্থির পদ্ধতিতে জাহান্নামের রাস্তা প্রশস্ত হয়েছে। তবে মকিতোর কাজটি আপনার কোডটিকে স্থির পদ্ধতি থেকে রক্ষা করা নয়। আপনি যদি আপনার দলটিকে স্ট্যাটিক উপহাস করা পছন্দ করেন না, তবে আপনার সংস্থায় পাওয়ারমোকিটো ব্যবহার বন্ধ করুন। জাভা পরীক্ষাগুলি কীভাবে লেখা উচিত (যেমন মক স্ট্যাটিক্স করবেন না !!!) সম্পর্কে মতামত দর্শনের সাথে মকিতোকে একটি টুলকিট হিসাবে বিকশিত করা দরকার। তবে মকিতো কোনও মতামতী নয়। আমরা স্থির বিদ্রূপের মতো প্রস্তাবিত প্রস্তাবিত ব্যবহারগুলি অবরুদ্ধ করতে চাই না। এটা শুধু আমাদের কাজ নয়।



1

যেহেতু এই পদ্ধতিটি স্থিতিশীল, আপনার এটি ব্যবহার করার জন্য এটি ইতিমধ্যে আপনার প্রয়োজনীয় সমস্ত কিছু রয়েছে, তাই এটি উপহাসের উদ্দেশ্যকে পরাস্ত করে। স্থির পদ্ধতিগুলি উপহাস করা একটি খারাপ অভ্যাস হিসাবে বিবেচিত হয়।

যদি আপনি এটি করার চেষ্টা করেন তবে এর অর্থ আপনি পরীক্ষাটি চালাতে চান তাতে কোনও সমস্যা আছে।

অবশ্যই আপনি পাওয়ারমোকিটো বা এটি করতে সক্ষম অন্য কোনও কাঠামো ব্যবহার করতে পারেন তবে আপনার পদ্ধতির পুনর্বিবেচনা করার চেষ্টা করুন।

উদাহরণস্বরূপ: বস্তুগুলিকে উপহাস / সরবরাহ করার চেষ্টা করুন, যা স্থির পদ্ধতিটি পরিবর্তে গ্রাস করে।


0

জেমকিত কাঠামো ব্যবহার করুন । এটা আমার জন্য কাজ করেছে। আপনাকে বিদ্রূপের জন্য DBConenction.getConnication () পদ্ধতির জন্য বিবৃতি লিখতে হবে না। কেবল নীচের কোডটি যথেষ্ট।

নিচে মক হ'ল মকিত.মক প্যাকেজ

Connection jdbcConnection = Mockito.mock(Connection.class);

MockUp<DBConnection> mockUp = new MockUp<DBConnection>() {

            DBConnection singleton = new DBConnection();

            @Mock
            public DBConnection getInstance() { 
                return singleton;
            }

            @Mock
            public Connection getConnection() {
                return jdbcConnection;
            }
         };
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.