মকিটো কিছু পদ্ধতি উপহাস করার জন্য ব্যবহার করুন তবে অন্যকে নয়


402

কোনও ক্লাসে কিছু পদ্ধতি উপহাস করার জন্য মকিতো ব্যবহার করে কোনও উপায় আছে, তবে অন্যেরা নেই?

উদাহরণস্বরূপ, এই (স্বীকৃত স্বীকৃতিযুক্ত) Stockশ্রেণিতে আমি উপহাস করতে চাই getPrice()এবং getQuantity()মানগুলি ফেরত দিতে চাই (নীচের পরীক্ষার স্নিপেটে দেখানো হয়েছে) তবে আমি ক্লাসে getValue()কোডেড হিসাবে গুণটি সম্পাদন করতে চাইStock

public class Stock {
  private final double price;
  private final int quantity;

  Stock(double price, int quantity) {
    this.price = price;
    this.quantity = quantity;
  }

  public double getPrice() {
    return price;
  }

  public int getQuantity() {
    return quantity;
  }
  public double getValue() {
    return getPrice() * getQuantity();
  }

  @Test
  public void getValueTest() {
    Stock stock = mock(Stock.class);
    when(stock.getPrice()).thenReturn(100.00);
    when(stock.getQuantity()).thenReturn(200);
    double value = stock.getValue();
    // Unfortunately the following assert fails, because the mock Stock getValue() method does not perform the Stock.getValue() calculation code.
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

4
আপনি এটি কেন করতে চান? আপনি হয় ক্লাস পরীক্ষা করা উচিত (কোন ক্ষেত্রে, সেখানে মোটেও উপহাস করা উচিত নয়) অথবা কোনও আলাদা ক্লাস পরীক্ষা করার সময় আপনার এটি উপহাস করা উচিত (যার ক্ষেত্রে কোনও কার্যকারিতা নেই)। আপনি কেন আংশিক উপহাস করবেন?
ওয়েলট্রাম্পিরাট

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

5
অবশ্যই: আপনার ডাটাবেস কলগুলি একটি পৃথক শ্রেণিতে সরান (ডোমেন লজিক এবং ডাটাবেস অ্যাক্সেস একই শ্রেণিতে হওয়া উচিত নয়; তারা দুটি পৃথক উদ্বেগ), এর ইন্টারফেসটি এক্সট্রাক্ট করুন, ডোমেন লজিক ক্লাস থেকে সংযোগ করার জন্য সেই ইন্টারফেসটি ব্যবহার করুন এবং কেবলমাত্র উপহাস করুন পরীক্ষার সময় ইন্টারফেস।
ওয়েলট্রাম্পিরট

1
আমি সম্পূর্ণরূপে একমত, তৃতীয় পক্ষের লাইব্রেরি সহ এখানে কোডের গবগুলি আপলোড না করেই পুরো ছবিটি ব্যাখ্যা করা শক্ত।
ভিক্টর গ্রেজি

1
আপনি সম্ভবত পারে। তবে, এটি "এটি করার সর্বোত্তম উপায়" হবে না: আপনার ডাটাবেস কোডটি এমন একটি বাস্তবায়ন বিশদ যা আপনি আপনার প্রয়োগের বাকী অংশ থেকে আড়াল করতে চান, সম্ভবত কোনও আলাদা প্যাকেজেও যেতে পারেন। আপনি প্রতিবার সিক্যুয়াল স্টেটমেন্ট পরিবর্তন করার সময় আপনার ডোমেন লজিকটি পুনরায় সংকলন করতে চান না, আপনি কি করবেন?
ওয়েলট্রাম্পিরাট

উত্তর:


642

আপনার প্রশ্নের সরাসরি উত্তর দিতে, হ্যাঁ, আপনি অন্যকে উপহাস না করে কিছু পদ্ধতি উপহাস করতে পারেন। একে আংশিক মক বলা হয় । দেখুন আংশিক ঠাট্টা উপর Mockito ডকুমেন্টেশন দেখুন।

উদাহরণস্বরূপ, আপনি নিজের পরীক্ষায় নিম্নলিখিতগুলির মতো কিছু করতে পারেন:

Stock stock = mock(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
when(stock.getValue()).thenCallRealMethod();  // Real implementation

সেক্ষেত্রে, প্রতিটি পদ্ধতির প্রয়োগের বিষয়টি উপহাস করা হয়, যদি না ক্লজটিতে নির্দিষ্ট thenCallRealMethod()করা থাকে when(..)

বিদ্রূপের পরিবর্তে গুপ্তচর দিয়ে অন্য উপায়ের সম্ভাবনাও রয়েছে :

Stock stock = spy(Stock.class);
when(stock.getPrice()).thenReturn(100.00);    // Mock implementation
when(stock.getQuantity()).thenReturn(200);    // Mock implementation
// All other method call will use the real implementations

সেক্ষেত্রে, সব পদ্ধতি বাস্তবায়ন, বাস্তব এক ছাড়া যদি আপনার সাথে একটি ব্যঙ্গ আচরণ সংজ্ঞায়িত করেছেন when(..)

when(Object)পূর্ববর্তী উদাহরণের মতো আপনি যখন গুপ্তচর ব্যবহার করেন তখন একটি গুরুত্বপূর্ণ সমস্যা রয়েছে । আসল পদ্ধতিটি বলা হবে (কারণ রানটাইমের stock.getPrice()আগে মূল্যায়ন করা when(..)হয়)। যদি আপনার পদ্ধতিতে যুক্তি না থাকে যা কল করা উচিত নয় এটি এই সমস্যা হতে পারে। আপনি আগের উদাহরণটি লিখতে পারেন:

Stock stock = spy(Stock.class);
doReturn(100.00).when(stock).getPrice();    // Mock implementation
doReturn(200).when(stock).getQuantity();    // Mock implementation
// All other method call will use the real implementations

আর একটি সম্ভাবনা ব্যবহার করা হতে পারে org.mockito.Mockito.CALLS_REAL_METHODSযেমন:

Stock MOCK_STOCK = Mockito.mock( Stock.class, CALLS_REAL_METHODS );

এই প্রতিনিধিরা বাস্তব বাস্তবায়নের জন্য আনস্টাবযুক্ত কলগুলি উপস্থাপন করে।


যাইহোক, আপনার উদাহরণ সহ, আমি বিশ্বাস করি এটি এখনও ব্যর্থ হবে, যেহেতু বাস্তবায়ন getValue()নির্ভর করে quantityএবং এর priceপরিবর্তে getQuantity()এবং getPrice()যা আপনাকে উপহাস করেছে।

আর একটি সম্ভাবনা হ'ল বিদ্রূপ পুরোপুরি এড়ানো:

@Test
public void getValueTest() {
    Stock stock = new Stock(100.00, 200);
    double value = stock.getValue();
    assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

21
আমি মনে করি এই উত্তরটি ভুল। আপনার অবজেক্টের একটি উদাহরণ স্পাই করা দরকার, ক্লাসটি মক করা উচিত নয়।
গাআরআরপেটটা

2
@ গআরআরপিএটা আমি বলব গুপ্তচরবৃত্তি এবং মশকরা উভয়ই যুক্তিসঙ্গত বিকল্প। এই মামলায় কোনটি সেরা তা বলা মুশকিল, কারণ ওপি বলে যে এটি একটি সরল উদাহরণ।
জন নিউমুইস

1
আঞ্চলিক উপহাসের ক্যাবেকে "স্পাই" দ্বারা আরও ভাল উপায়ে সরবরাহ করা হিসাবে এটি "মক" এর পরিবর্তে "স্পাই" হওয়া উচিত নয়।
তরুণ সাপরা

2
Stock stock = spy(Stock.class);এটি ভুল বলে মনে হচ্ছে, spyপদ্ধতিটি ক্লাস নয় কেবলমাত্র জিনিসগুলি গ্রহণ করবে বলে মনে হচ্ছে।
পরমবীর সিং কারওয়াল

4
doReturn(retval).when(spyObj).methodName(args)এবং এর মধ্যে পার্থক্যটি নির্দেশ করার জন্য +1when(spyObj.methodName(args)).thenReturn(retval)
ক্যাপ্টেন_ববলিত

140

একটি শ্রেণীর আংশিক উপহাসও মকিতো স্পাইয়ের মাধ্যমে সমর্থিত

List list = new LinkedList();
List spy = spy(list);

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls real methods
spy.add("one");
spy.add("two");

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

বিস্তারিত ব্যাখ্যার জন্য ডকস 1.10.19এবং 2.7.22ডক্স পরীক্ষা করুন ।


37

ডক্স অনুসারে :

Foo mock = mock(Foo.class, CALLS_REAL_METHODS);

// this calls the real implementation of Foo.getSomething()
value = mock.getSomething();

when(mock.getSomething()).thenReturn(fakeValue);

// now fakeValue is returned
value = mock.getSomething();

2
পরীক্ষা থেকে আমার নিয়ন্ত্রণ করা দরকার কয়েকটি বাদে যেখানে বাস্তব পদ্ধতি বাস্তবায়নের আহ্বান জানানো হয়েছে সেখানে কীভাবে একটি মোককে সেটআপ করবেন তা প্রদর্শনের জন্য আপনাকে ধন্যবাদ।
বিঘ_29

class NaughtyLinkedList extends LinkedList { public int size() { throw new RuntimeException("don't call me");} } @Test public void partialMockNaughtLinkedList(){ List mock = mock(NaughtyLinkedList.class, CALLS_REAL_METHODS); mock.add(new Object()); // this calls the real function when(mock.size()).thenReturn(2); // For whatever reason, this lines throws the RuntimeException. assertEquals(2,mock.size()); }এটি কাজ করে না। যে কারণেই হোক না কেন, যখন "কখন" কার্যকর করা হয়, এটি আসলে সেই পদ্ধতিটি সম্পাদন করে যা উপহাস করার কথা। কোড:
ল্যান্স কিন্ড

3
সমস্যাটি "কখন"। আপনি যখন আংশিক উপহাস করতে চান সেই জিনিসটি "কখন" আসলে সম্পাদন করবে। এটি এড়ানোর জন্য একটি বিকল্প রয়েছে: doReturn ()। DoReturn () এ আপনি দেখুন docs.mockito.googlecode.com/hg/1.9.5/org/mockito/...
ল্যান্স কাইন্ড

18

আপনি যা চান তা org.mockito.Mockito.CALLS_REAL_METHODSডক্স অনুসারে:

/**
 * Optional <code>Answer</code> to be used with {@link Mockito#mock(Class, Answer)}
 * <p>
 * {@link Answer} can be used to define the return values of unstubbed invocations.
 * <p>
 * This implementation can be helpful when working with legacy code.
 * When this implementation is used, unstubbed methods will delegate to the real implementation.
 * This is a way to create a partial mock object that calls real methods by default.
 * <p>
 * As usual you are going to read <b>the partial mock warning</b>:
 * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
 * How does partial mock fit into this paradigm? Well, it just doesn't... 
 * Partial mock usually means that the complexity has been moved to a different method on the same object.
 * In most cases, this is not the way you want to design your application.
 * <p>
 * However, there are rare cases when partial mocks come handy: 
 * dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.)
 * However, I wouldn't use partial mocks for new, test-driven & well-designed code.
 * <p>
 * Example:
 * <pre class="code"><code class="java">
 * Foo mock = mock(Foo.class, CALLS_REAL_METHODS);
 *
 * // this calls the real implementation of Foo.getSomething()
 * value = mock.getSomething();
 *
 * when(mock.getSomething()).thenReturn(fakeValue);
 *
 * // now fakeValue is returned
 * value = mock.getSomething();
 * </code></pre>
 */

সুতরাং আপনার কোডটি দেখতে হবে:

import org.junit.Test;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class StockTest {

    public class Stock {
        private final double price;
        private final int quantity;

        Stock(double price, int quantity) {
            this.price = price;
            this.quantity = quantity;
        }

        public double getPrice() {
            return price;
        }

        public int getQuantity() {
            return quantity;
        }

        public double getValue() {
            return getPrice() * getQuantity();
        }
    }

    @Test
    public void getValueTest() {
        Stock stock = mock(Stock.class, withSettings().defaultAnswer(CALLS_REAL_METHODS));
        when(stock.getPrice()).thenReturn(100.00);
        when(stock.getQuantity()).thenReturn(200);
        double value = stock.getValue();

        assertEquals("Stock value not correct", 100.00 * 200, value, .00001);
    }
}

থেকে কল Stock stock = mock(Stock.class);কল org.mockito.Mockito.mock(Class<T>)যা এই মত দেখায়:

 public static <T> T mock(Class<T> classToMock) {
    return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS));
}

মানটির ডকগুলি RETURNS_DEFAULTSবলুন:

/**
 * The default <code>Answer</code> of every mock <b>if</b> the mock was not stubbed.
 * Typically it just returns some empty value. 
 * <p>
 * {@link Answer} can be used to define the return values of unstubbed invocations. 
 * <p>
 * This implementation first tries the global configuration. 
 * If there is no global configuration then it uses {@link ReturnsEmptyValues} (returns zeros, empty collections, nulls, etc.)
 */

1
ভাল দাগযুক্ত ... তবে আমি কেবল জিজ্ঞাসা করতে পারি আপনি কেন এমন ব্যবহার করেন withSettings()...? দেখা যাচ্ছে যে org.mockito.internal.stubbing.answers.CallsRealMethods()(উদাহরণস্বরূপ) কাজটি করা হতে পারে ... এবং এই শ্রেণীর জাভাদোকটি স্পষ্টভাবে বলেছে এটি আংশিক উপহাসের জন্য ব্যবহার করার জন্য ...
মাইক রডেন্ট

3
এছাড়াও ... এটি এখানে অন্যান্য উত্তরের দ্বারা যে সমস্যার মুখোমুখি হয় তা চালিয়ে thenReturnদেবে না : অর্থাত্‍ পদ্ধতিটি বাস্তবায়িত করবে (যা সমস্যা তৈরি করতে পারে, যদিও এই উদাহরণে নেই), এবং তাই doReturnকি এইরকম ক্ষেত্রে বাঞ্ছনীয় ...?
মাইকে রডেন্ট

4

উপরের উত্তরে আগেই বলা হয়েছে, মকিতোর গুপ্তচর পদ্ধতি ব্যবহার করে আংশিক উপহাস করা আপনার সমস্যার সমাধান হতে পারে। কিছুটা ক্ষেত্রে আমি সম্মত হই যে, আপনার কংক্রিট ব্যবহারের ক্ষেত্রে, ডিবি লুকআপকে উপহাস করা আরও উপযুক্ত হতে পারে। আমার অভিজ্ঞতা থেকে এটি সর্বদা সম্ভব নয় - কমপক্ষে অন্যান্য কর্মকাণ্ড ছাড়াই নয় - যা আমি খুব জটিল বা কমপক্ষে ভঙ্গুর বলে বিবেচনা করব। দ্রষ্টব্য, এই আংশিক উপহাস মকিতোর মিত্র সংস্করণগুলির সাথে কাজ করে না। আপনি কমপক্ষে 1.8.0 ব্যবহার করুন।

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

আরও একটি জিনিস: আমি সত্যিই বুঝতে পারি না যে এখানে অনেকবার একটি প্রশ্ন জিজ্ঞাসা করা হলেও কমপক্ষে সমস্যাটি বোঝার চেষ্টা না করে "কেন আপনি এটি করতে চান" এর সাথে মন্তব্য পেয়ে যায়। বিশেষত যখন এটির পরে আংশিক উপহাসের প্রয়োজন হয় তখন ব্যবহারের অনেকগুলি কেস রয়েছে যা আমি কল্পনা করতে পারি যে এটি কার্যকর হবে। এজন্য মকিতো থেকে আসা ছেলেরা সেই কার্যকারিতাটি সরবরাহ করেছিল। এই বৈশিষ্ট্যটি অবশ্যই অতিরিক্ত ব্যবহার করা উচিত নয়। কিন্তু যখন আমরা টেস্ট কেস সেটআপগুলি নিয়ে কথা বলি যা অন্যথায় খুব জটিল উপায়ে প্রতিষ্ঠিত করা যায় নি, গুপ্তচরবৃত্তি ব্যবহার করা উচিত।


2
আমি মনে করি এই উত্তরটি আংশিক মতামত। সম্পাদনা বিবেচনা করুন।
২১

2
পরিবারে নতুন সদস্যকে উত্সাহিত করতে উত্সাহিত। এই-ইন জোনে পাওয়ার দরকার নেই, সেখানে প্রযুক্তিগতভাবে কিছুই ভুল বা ভুল ভাষা / স্বর নেই। নতুন সদস্যদের প্রতি সদয় হন। ধন্যবাদ।
সৌরভ পাতিল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.