অ্যাসিক্রোনাস প্রক্রিয়াগুলি পরীক্ষা করতে কীভাবে JUnit ব্যবহার করবেন


204

JUnit এর সাথে অ্যাসিঙ্ক্রোনাস প্রক্রিয়াগুলিকে আগুন দেওয়ার পদ্ধতিগুলি আপনি কীভাবে পরীক্ষা করেন?

প্রক্রিয়াটি শেষ হওয়ার জন্য কীভাবে আমার পরীক্ষাটি অপেক্ষা করতে হয় তা আমি জানি না (এটি ঠিক কোনও ইউনিট পরীক্ষা নয়, এটি একীকরণের পরীক্ষার মতো, কারণ এটিতে কেবল এক শ্রেণি নয়) classes


আপনি জাট (জাভা অ্যাসিঙ্ক্রোনাস টেস্ট) চেষ্টা করে দেখতে পারেন: bitbucket.org/csolar/jat
cs0lar

2
জ্যাটটির 1 জন প্রহরী রয়েছে এবং 1.5 বছরে আপডেট হয়নি। প্রত্যাশাটি ঠিক 1 মাস আগে আপডেট হয়েছিল এবং এই লেখার সময় 1.6 সংস্করণে রয়েছে। আমি কোনও প্রকল্পের সাথেই অনুমোদিত নই, তবে আমি যদি আমার প্রকল্পের সাথে যুক্ত করতে বিনিয়োগ করতে চাই, তবে আমি এই সময়ে অ্যাওয়েটিলিটির আরও বেশি বিশ্বাসযোগ্যতা দেব।
লেস হ্যাজলউড

জ্যাটের এখনও কোনও আপডেট নেই: "সর্বশেষ আপডেট করা হয়েছে 2013-01-19"। লিঙ্কটি অনুসরণ করার জন্য কেবল সময় সাশ্রয় করুন।
deamon

@ লেজহেলউড, একজন প্রহরী জ্যাটের পক্ষে খারাপ, কিন্তু বছরের পর বছর কোনও আপডেট নেই ... কেবল একটি উদাহরণ। আপনার OS এর নিম্ন-স্তরের টিসিপি স্ট্যাকটি আপনি কত ঘন ঘন আপডেট করেন? জ্যাট বিকল্পের নীচে stackoverflow.com/questions/631598/… উত্তর দেওয়া হয়েছে ।
ব্যবহারকারী1742529

উত্তর:


46

আইএমএইচও ইউনিট টেস্টগুলি থ্রেড তৈরি করা বা অপেক্ষা করা খারাপ অভ্যাস practice ইত্যাদি You এজন্য আমি অ্যাসিঙ্ক প্রক্রিয়াগুলি পরীক্ষা করার জন্য 2-পদক্ষেপের পদ্ধতির প্রস্তাব করতে চাই।

  1. আপনার async প্রক্রিয়াটি সঠিকভাবে জমা দেওয়া হয়েছে তা পরীক্ষা করুন। আপনি যে অ্যাসিঙ্ক অনুরোধগুলি গ্রহণ করেন সেই বস্তুটিকে আপনি উপহাস করতে পারেন এবং জমা দেওয়া কাজের সঠিক বৈশিষ্ট্য ইত্যাদি রয়েছে তা নিশ্চিত করতে পারেন etc.
  2. পরীক্ষা করুন যে আপনার অ্যাসিঙ্ক কলব্যাকগুলি সঠিক কাজ করছে। এখানে আপনি মূলত জমা দেওয়া কাজটিকে উপহাস করতে পারেন এবং এটি সঠিকভাবে শুরু করা হয়েছে তা ধরে নিতে পারেন এবং আপনার কলব্যাকগুলি সঠিক কিনা তা যাচাই করতে পারেন।

148
অবশ্যই। তবে কখনও কখনও আপনার এমন কোড পরীক্ষা করা দরকার যা বিশেষভাবে থ্রেডগুলি পরিচালনা করতে পারে।
অভাব

77
আমাদের মধ্যে যারা জুনিট বা টেস্টএনজি ব্যবহার করে ইন্টিগ্রেশন টেস্টিং করতে (এবং কেবল ইউনিট টেস্টিং নয়) বা ব্যবহারকারী গ্রহণযোগ্যতা পরীক্ষা (যেমন ডাব্লু / শসাবার), একটি অ্যাসিঙ্ক সমাপ্তির জন্য অপেক্ষা করা এবং ফলাফল যাচাই করা একেবারে প্রয়োজনীয়।
লেস হ্যাজলউড

38
আসিনক্রোনাস প্রসেসগুলি ডান পাওয়ার জন্য সবচেয়ে জটিল কোডগুলির মধ্যে কিছু এবং আপনি বলছেন যে আপনি তাদের জন্য ইউনিট টেস্টিং ব্যবহার করবেন না এবং কেবল একটি থ্রেড দিয়ে পরীক্ষা করবেন? এটা খুব খারাপ ধারণা।
চার্লস

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

4
এটি গ্রহণযোগ্য উত্তর হওয়া উচিত নয়। পরীক্ষা ইউনিট পরীক্ষার বাইরে। ওপি একে ইউনিট টেস্টের চেয়ে ইন্টিগ্রেশন টেস্টের বেশি বলে আখ্যায়িত করে।
যেরেমিয়া অ্যাডামস

190

একটি বিকল্প হ'ল কাউন্টডাউনল্যাচ ক্লাস ব্যবহার করা ।

public class DatabaseTest {

    /**
     * Data limit
     */
    private static final int DATA_LIMIT = 5;

    /**
     * Countdown latch
     */
    private CountDownLatch lock = new CountDownLatch(1);

    /**
     * Received data
     */
    private List<Data> receiveddata;

    @Test
    public void testDataRetrieval() throws Exception {
        Database db = new MockDatabaseImpl();
        db.getData(DATA_LIMIT, new DataCallback() {
            @Override
            public void onSuccess(List<Data> data) {
                receiveddata = data;
                lock.countDown();
            }
        });

        lock.await(2000, TimeUnit.MILLISECONDS);

        assertNotNull(receiveddata);
        assertEquals(DATA_LIMIT, receiveddata.size());
    }
}

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

সম্পাদনা @jtahlborn এবং @Ring থেকে সরাসরি মন্তব্যগুলি CountDownLatch ধন্যবাদ কাছাকাছি ব্লক সিঙ্ক্রোনাইজ সরানো


8
দয়া করে এই উদাহরণটি অনুসরণ করবেন না, এটি ভুল। এটি একটি কাউন্টডাউনল্যাচে সিঙ্ক্রোনাইজ করা উচিত নয় কারণ এটি অভ্যন্তরীণভাবে থ্রেড-সুরক্ষা পরিচালনা করে।
jtahlorn

1
এটি সিঙ্ক্রোনাইজ করা অংশ না হওয়া পর্যন্ত ভাল পরামর্শ ছিল, এটি সম্ভবত ডিবাগিং সময়ের প্রায় 3-4 ঘন্টা খেয়েছিল। stackoverflow.com/questions/11007551/…
রিং করুন

2
ত্রুটির জন্য দুঃখিত। আমি উত্তরটি যথাযথভাবে সম্পাদনা করেছি।
মার্টিন

7
যদি আপনি যাচাই করে থাকেন যে অনস্যাক্সেস ডেকে আনা হয়েছিল, আপনার সেই লকটি থাকা উচিত awa
গিলবার্ট

1
@ মার্টিন যেটি সঠিক হবে তবে এর অর্থ হ'ল আপনার আলাদা সমস্যা আছে যা ঠিক করা দরকার।
জর্জ অ্যারিস্টি

75

আপনি অ্যায়েটিলিটি লাইব্রেরিটি ব্যবহার করে দেখতে পারেন । আপনি যে সিস্টেমগুলির কথা বলছেন তা পরীক্ষা করা সহজ করে তোলে।


21
একটি বন্ধুত্বপূর্ণ দাবি অস্বীকার: প্রকল্পে জোহান প্রধান অবদানকারী।
dbm

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

সত্যিই অসাধারণ.
আর কার্লুস

এটি নিখুঁত গ্রন্থাগার যা আমার async প্রক্রিয়া ইন্টিগ্রেশন পরীক্ষার প্রয়োজনীয়তা পূরণ করে। সত্যিই অসাধারণ. লাইব্রেরিটি বেশ ভালভাবে রক্ষণাবেক্ষণ করা হয়েছে এবং বেসিক থেকে অ্যাডভান্সড পর্যন্ত এমন বৈশিষ্ট্য রয়েছে যা আমি বিশ্বাস করি বেশিরভাগ দৃশ্যের জন্য যথেষ্ট। দুর্দান্ত রেফারেন্সের জন্য ধন্যবাদ!
তানভীর

সত্যিই দুর্দান্ত পরামর্শ। ধন্যবাদ
রয়েলটাইগার

68

আপনি যদি একটি ব্যবহার করেন তাহলে CompletableFuture (জাভা 8 চালু) অথবা একটি SettableFuture (থেকে গুগল পেয়ারা ), আপনি তাড়াতাড়ি আপনার পরীক্ষার ফিনিস করতে পারেন যেমন বরং সময় একটি প্রাক সেট পরিমাণ অপেক্ষা না করে, এর কাজ। আপনার পরীক্ষাটি এরকম কিছু দেখাচ্ছে:

CompletableFuture<String> future = new CompletableFuture<>();
executorService.submit(new Runnable() {         
    @Override
    public void run() {
        future.complete("Hello World!");                
    }
});
assertEquals("Hello World!", future.get());

4
... এবং আপনি যদি জাভা-আট-এর চেয়ে আটজনের চেয়ে আটকে গেগা সেটেটে ফিউচারের চেষ্টা করুন যা বেশ একই জিনিসটি করে
মার্কাস টি


18

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

সুতরাং ধরুন আমি অ্যাসিক্রোনাস পদ্ধতি পরীক্ষা করার চেষ্টা করছি Foo#doAsync(Callback c),

class Foo {
  private final Executor executor;
  public Foo(Executor executor) {
    this.executor = executor;
  }

  public void doAsync(Callback c) {
    executor.execute(new Runnable() {
      @Override public void run() {
        // Do stuff here
        c.onComplete(data);
      }
    });
  }
}

উত্পাদনে, আমি Fooএকটি Executors.newSingleThreadExecutor()নির্বাহক উদাহরণ দিয়ে নির্মাণ করব, পরীক্ষার সময় আমি সম্ভবত এটি একটি সিঙ্ক্রোনাস এক্সিকিউটারের সাথে নির্মাণ করব যা নিম্নলিখিতগুলি করে -

class SynchronousExecutor implements Executor {
  @Override public void execute(Runnable r) {
    r.run();
  }
}

এখন আমার অ্যাসিক্রোনাস পদ্ধতির JUnit পরীক্ষাটি বেশ পরিষ্কার -

@Test public void testDoAsync() {
  Executor executor = new SynchronousExecutor();
  Foo objectToTest = new Foo(executor);

  Callback callback = mock(Callback.class);
  objectToTest.doAsync(callback);

  // Verify that Callback#onComplete was called using Mockito.
  verify(callback).onComplete(any(Data.class));

  // Assert that we got back the data that we expected.
  assertEquals(expectedData, callback.getData());
}

যদি আমি স্প্রিংয়ের মতো WebClient
অ্যাসিনক্রোনাস

8

থ্রেডযুক্ত / অ্যাসিঙ্ক কোডটি পরীক্ষা করার সাথে অন্তর্নিহিত কোনও ভুল নেই, বিশেষত যদি থ্রেডিং কোডটি আপনি পরীক্ষা করছেন তার বিন্দু । এই স্টাফ পরীক্ষা করার জন্য সাধারণ পন্থাটি হ'ল:

  • মূল পরীক্ষার থ্রেডটি ব্লক করুন
  • অন্যান্য থ্রেড থেকে কৃতিত্ব ব্যর্থ হয়েছে
  • মূল পরীক্ষার থ্রেড অবরোধ মুক্ত করুন
  • কোনও ব্যর্থতা রথ্রো

তবে এটি একটি পরীক্ষার জন্য অনেক বয়লারপ্লেট। আরও ভাল / সরল পদ্ধতি হ'ল কেবল কনকেন্টার ইউনাইট ব্যবহার করা :

  final Waiter waiter = new Waiter();

  new Thread(() -> {
    doSomeWork();
    waiter.assertTrue(true);
    waiter.resume();
  }).start();

  // Wait for resume() to be called
  waiter.await(1000);

CountdownLatchপদ্ধতির মাধ্যমে এটির সুবিধাটি হ'ল এটি হ'ল কম ভার্জোজ যেহেতু কোনও থ্রেডে ঘটে যাওয়া দৃ fail় ব্যর্থতা যথাযথভাবে মূল থ্রেডকে রিপোর্ট করা হয়েছে, অর্থাত্ পরীক্ষাটি কখন হওয়া উচিত নয়। কনক্র্যান্ট ইউনাইটের সাথে CountdownLatchপদ্ধতির তুলনা করা একটি লিখনআপ এখানে

যারা আরও কিছুটা বিশদ জানতে চান তাদের জন্যও আমি এই বিষয়ে একটি ব্লগ পোস্ট লিখেছিলাম ।


আমি অতীতে একই ধরণের সমাধানটি ব্যবহার করেছি github.com/MichaelTamm/junit-toolbox , এছাড়াও জুনিট.আর
জুনিট 4

4

কল করার বিষয়ে SomeObject.waitএবং এখানেnotifyAll বর্ণিত হিসাবে বা রোবটিয়ামগুলি পদ্ধতি ব্যবহার করার জন্য বা এটি করার জন্য আমি যে ক্লাসটি লিখেছি তা ব্যবহার করুন (কীভাবে ব্যবহার করবেন তার জন্য মন্তব্য এবং পরীক্ষার ক্লাস দেখুন) Solo.waitForCondition(...)


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

3

অ্যাসিঙ্ক্রোনাস যুক্তি পরীক্ষা করতে আমি একটি লাইব্রেরি সকেট.ইও পাইলিঙ্কডব্লকিংকুই ব্যবহার করে এটি সহজ এবং সংক্ষিপ্ত উপায়ে দেখায় । এখানে উদাহরণ :

    @Test(timeout = TIMEOUT)
public void message() throws URISyntaxException, InterruptedException {
    final BlockingQueue<Object> values = new LinkedBlockingQueue<Object>();

    socket = client();
    socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
        @Override
        public void call(Object... objects) {
            socket.send("foo", "bar");
        }
    }).on(Socket.EVENT_MESSAGE, new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            values.offer(args);
        }
    });
    socket.connect();

    assertThat((Object[])values.take(), is(new Object[] {"hello client"}));
    assertThat((Object[])values.take(), is(new Object[] {"foo", "bar"}));
    socket.disconnect();
}

লিংকডব্লকিংকুইউ ব্যবহার করে এআইপিআইকে ব্লক করে সিঙ্ক্রোনাসের মতো ফলাফল পেতে পারা যায়। ফলাফলের জন্য অপেক্ষা করার জন্য খুব বেশি সময় ধরে না এনে সময়সীমা নির্ধারণ করুন।


1
দুর্দান্ত পদ্ধতি!
অ্যামিনোগ্রাফি

3

এটি উল্লেখ করার মতো যে অনুশীলনে কনকুরেন্সির খুব দরকারী অধ্যায় Testing Concurrent Programsরয়েছে যা কিছু ইউনিট পরীক্ষার পদ্ধতির বর্ণনা দেয় এবং সমস্যার সমাধান দেয়।


1
এটি কোন পন্থা? আপনি একটি উদাহরণ দিতে পারেন?
ব্রুনো ফেরেরিরা

2

পরীক্ষার ফলাফলটি যদি অবিচ্ছিন্নভাবে উত্পন্ন হয় তবে আমি আজকাল এটি ব্যবহার করছি।

public class TestUtil {

    public static <R> R await(Consumer<CompletableFuture<R>> completer) {
        return await(20, TimeUnit.SECONDS, completer);
    }

    public static <R> R await(int time, TimeUnit unit, Consumer<CompletableFuture<R>> completer) {
        CompletableFuture<R> f = new CompletableFuture<>();
        completer.accept(f);
        try {
            return f.get(time, unit);
        } catch (InterruptedException | TimeoutException e) {
            throw new RuntimeException("Future timed out", e);
        } catch (ExecutionException e) {
            throw new RuntimeException("Future failed", e.getCause());
        }
    }
}

স্থিতিশীল আমদানি ব্যবহার করে, পরীক্ষাটি দারুণ সুন্দর পড়ে। (দ্রষ্টব্য, এই উদাহরণে আমি ধারণাটি বর্ণনা করার জন্য একটি থ্রেড শুরু করছি)

    @Test
    public void testAsync() {
        String result = await(f -> {
            new Thread(() -> f.complete("My Result")).start();
        });
        assertEquals("My Result", result);
    }

যদি f.completeনা ডাকা হয়, পরীক্ষা শেষ হওয়ার পরে ব্যর্থ হবে। আপনি f.completeExceptionallyতাড়াতাড়ি ব্যর্থতা ব্যবহার করতে পারেন ।


2

এখানে অনেক উত্তর রয়েছে তবে একটি সহজ একটি হ'ল একটি সম্পূর্ণ কমপ্লিটেবল ফিউচার তৈরি করা এবং এটি ব্যবহার করা:

CompletableFuture.completedFuture("donzo")

সুতরাং আমার পরীক্ষায়:

this.exactly(2).of(mockEventHubClientWrapper).sendASync(with(any(LinkedList.class)));
this.will(returnValue(new CompletableFuture<>().completedFuture("donzo")));

আমি নিশ্চিত করছি যে এই সমস্ত জিনিস যেভাবেই বলা হবে। আপনি যদি এই কোডটি ব্যবহার করেন তবে এই কৌশলটি কাজ করে:

CompletableFuture.allOf(calls.toArray(new CompletableFuture[0])).join();

সমস্ত CompletableFutures সমাপ্ত হওয়ার সাথে সাথে এটি সরাসরি জিপ করবে!


2

আপনি যখনই পারবেন সমান্তরাল থ্রেড দিয়ে পরীক্ষা করা এড়িয়ে চলুন (যা বেশিরভাগ সময় হয়)। এটি কেবল আপনার পরীক্ষাগুলিকে ঝাপটায় পরিণত করবে (কখনও কখনও পাস করবে, কখনও কখনও ব্যর্থ হবে)।

কেবল যখন আপনাকে অন্য কোনও গ্রন্থাগার / সিস্টেম কল করার প্রয়োজন হয় তখন আপনাকে অন্যান্য থ্রেডের জন্য অপেক্ষা করতে হতে পারে, সেক্ষেত্রে সর্বদা অ্যাওয়েটিলিটি লাইব্রেরিটি পরিবর্তে ব্যবহার করুন Thread.sleep()

কখনই কেবল কল করবেন না get()বা join()আপনার পরীক্ষাগুলিতে না করুন, নাহলে ভবিষ্যত কখনই পূরণ না হয় আপনার পরীক্ষাগুলি আপনার সিআই সার্ভারে চিরতরে চলে। isDone()কল করার আগে সর্বদা আপনার পরীক্ষাগুলিতে প্রথমে দৃ .়তা জানান get()। কমপ্লিটসটেজের জন্য, এটি .toCompletableFuture().isDone()

যখন আপনি এই জাতীয় কোনও অবরুদ্ধকরণ পদ্ধতি পরীক্ষা করেন:

public static CompletionStage<String> createGreeting(CompletableFuture<String> future) {
    return future.thenApply(result -> "Hello " + result);
}

তারপরে আপনি পরীক্ষায় একটি সম্পূর্ণ ফিউচার পাশ করে কেবল ফলাফলটি পরীক্ষা করা উচিত নয়, আপনারও নিশ্চিত হওয়া উচিত যে doSomething()কল করে join()বা আপনার পদ্ধতিটি ব্লক না করে get()। এটি বিশেষত গুরুত্বপূর্ণ যদি আপনি একটি অবরুদ্ধকরণ কাঠামো ব্যবহার করেন।

এটি করার জন্য, আপনি সম্পূর্ণরূপে ম্যানুয়ালি সমাপ্তির জন্য সেট করেছেন এমন একটি সম্পূর্ণ না হওয়া ভবিষ্যতের সাথে পরীক্ষা করুন:

@Test
public void testDoSomething() throws Exception {
    CompletableFuture<String> innerFuture = new CompletableFuture<>();
    CompletableFuture<String> futureResult = createGreeting(innerFuture).toCompletableFuture();
    assertFalse(futureResult.isDone());

    // this triggers the future to complete
    innerFuture.complete("world");
    assertTrue(futureResult.isDone());

    // futher asserts about fooResult here
    assertEquals(futureResult.get(), "Hello world");
}

এইভাবে, future.join()আপনি doSomething () এ যুক্ত করলে, পরীক্ষা ব্যর্থ হবে।

যদি আপনার পরিষেবা কোনও এক্সিকিউটর সার্ভিস যেমন ব্যবহার করে thenApplyAsync(..., executorService)তবে আপনার পরীক্ষাগুলিতে একক থ্রেডযুক্ত এক্সিকিউটার সার্ভিস ইনজেক্ট করুন, যেমন পেয়ারা থেকে প্রাপ্ত:

ExecutorService executorService = Executors.newSingleThreadExecutor();

যদি আপনার কোডটি কাঁটাচামিনপুল যেমন ব্যবহার করে তবে এক্সিকিউটর সার্ভিস ব্যবহার করার জন্য কোডটি thenApplyAsync(...)পুনরায় লিখুন (অনেকগুলি ভাল কারণ রয়েছে), বা অ্যাওয়েটিলিটি ব্যবহার করুন।

উদাহরণটি সংক্ষিপ্ত করার জন্য, আমি বার সার্ভিসটিকে পরীক্ষার একটি জাভা 8 লাম্বদা হিসাবে প্রয়োগ করা একটি পদ্ধতি যুক্তি হিসাবে তৈরি করেছি, সাধারণত এটি এমন একটি ইনজেকশন রেফারেন্স হবে যা আপনি উপহাস করবেন।


আরে @tkruse, এই কৌশলটি ব্যবহার করে সম্ভবত আপনার কাছে কোনও পাবলিক গিট রেপো আছে?
ক্রিশ্চিয়ানো

@ ক্রিশ্চিয়ানো: এটি এসও দর্শনের বিরুদ্ধে be পরিবর্তে, যখন আপনি একটি খালি জুনিট পরীক্ষা শ্রেণিতে আটকান তখন আমি কোনও অতিরিক্ত কোড (সমস্ত আমদানি জাভা 8 + বা জুনিট) ছাড়াই সংকলনের পদ্ধতিগুলিকে পরিবর্তন করেছি। Upvote নির্দ্বিধায়।
tkruse

আমি এখন বুঝতে পেরেছি। ধন্যবাদ। আমার সমস্যা এখন পরীক্ষা করা যখন পদ্ধতিগুলি কমপ্লেটেবল ফিউচার ফিরিয়ে দেয় তবে কমপ্লেটেবল ফিউচার ব্যতীত অন্য বিষয়গুলিকে পরামিতি হিসাবে গ্রহণ করে।
ক্রিশ্চিয়ানো

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

1

আমি অপেক্ষা এবং বিজ্ঞপ্তি ব্যবহার পছন্দ। এটি সহজ এবং স্পষ্ট।

@Test
public void test() throws Throwable {
    final boolean[] asyncExecuted = {false};
    final Throwable[] asyncThrowable= {null};

    // do anything async
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // Put your test here.
                fail(); 
            }
            // lets inform the test thread that there is an error.
            catch (Throwable throwable){
                asyncThrowable[0] = throwable;
            }
            // ensure to release asyncExecuted in case of error.
            finally {
                synchronized (asyncExecuted){
                    asyncExecuted[0] = true;
                    asyncExecuted.notify();
                }
            }
        }
    }).start();

    // Waiting for the test is complete
    synchronized (asyncExecuted){
        while(!asyncExecuted[0]){
            asyncExecuted.wait();
        }
    }

    // get any async error, including exceptions and assertationErrors
    if(asyncThrowable[0] != null){
        throw asyncThrowable[0];
    }
}

মূলত, বেনামি অভ্যন্তর শ্রেণীর অভ্যন্তরে ব্যবহার করার জন্য আমাদের একটি চূড়ান্ত অ্যারে রেফারেন্স তৈরি করতে হবে। আমি বরং একটি বুলিয়ান তৈরি করব [], কারণ যদি আমাদের অপেক্ষা করতে হয় তবে আমি নিয়ন্ত্রণের জন্য একটি মান রাখতে পারি) যখন সবকিছু হয়ে যায়, আমরা কেবল asyncExecutes ছেড়ে দেই।


1
যদি আপনার দৃser়তা ব্যর্থ হয় তবে মূল পরীক্ষার থ্রেড এটি সম্পর্কে জানতে পারবে না।
জোনাথন

সমাধানের জন্য ধন্যবাদ, আমাকে ওয়েবসকেট সংযোগ দিয়ে কোডটি ডিবাগ করতে সহায়তা করে।
নজর সাখারেঙ্কো

@ জোনাথন, আমি কোনও দৃser়তা এবং ব্যতিক্রম ধরতে কোডটি আপডেট করেছি এবং এটি ਮੁੱਖ পরীক্ষার থ্রেডে অবহিত করব।
পাওলো

1

সেখানে থাকা সমস্ত বসন্ত ব্যবহারকারীদের জন্য, আমি আজকাল সাধারণত আমার ইন্টিগ্রেশন টেস্টগুলি করি, যেখানে অ্যাসিঙ্ক আচরণ জড়িত:

কোনও এসিঙ্ক টাস্ক (যেমন I / O কল হিসাবে) শেষ হয়ে গেলে প্রোডাকশন কোডে একটি অ্যাপ্লিকেশন ইভেন্ট ফায়ার করুন। উত্পাদনে অ্যাসিঙ্ক অপারেশনের প্রতিক্রিয়া পরিচালনা করতে বেশিরভাগ সময় এই ইভেন্টটি প্রয়োজনীয়।

এই ইভেন্টটি স্থানে রেখে, আপনি তারপরে আপনার পরীক্ষার ক্ষেত্রে নিম্নলিখিত কৌশলটি ব্যবহার করতে পারেন:

  1. পরীক্ষার অধীনে সিস্টেমটি কার্যকর করুন
  2. ইভেন্টটির জন্য শুনুন এবং নিশ্চিত করুন যে ইভেন্টটি চালিত হয়েছে
  3. আপনার দৃ Do়তা কি

এটি ভেঙে ফেলার জন্য আপনার প্রথমে কিছুটা ডোমেন ইভেন্ট নিক্ষেপ করতে হবে। আমি সম্পন্ন কাজটি সনাক্ত করতে এখানে একটি ইউইউডি ব্যবহার করছি, তবে আপনি যতক্ষণ না এটি অনন্য ততক্ষণ অন্য কিছু ব্যবহার করতে পারবেন free

(দ্রষ্টব্য, নীচের কোড স্নিপেটগুলিও বয়লার প্লেট কোড থেকে মুক্তি পেতে লম্বোক টীকা ব্যবহার করে )

@RequiredArgsConstructor
class TaskCompletedEvent() {
  private final UUID taskId;
  // add more fields containing the result of the task if required
}

প্রোডাকশন কোড নিজেই তখন সাধারণত দেখতে এরকম লাগে:

@Component
@RequiredArgsConstructor
class Production {

  private final ApplicationEventPublisher eventPublisher;

  void doSomeTask(UUID taskId) {
    // do something like calling a REST endpoint asynchronously
    eventPublisher.publishEvent(new TaskCompletedEvent(taskId));
  }

}

এরপরে @EventListenerপরীক্ষার কোডে প্রকাশিত ইভেন্টটি ধরতে আমি একটি স্প্রিং ব্যবহার করতে পারি । ইভেন্ট শ্রোতা খানিকটা বেশি জড়িত, কারণ এটি সুরক্ষিতভাবে থ্রেডে দুটি কেস পরিচালনা করতে হবে:

  1. প্রোডাকশন কোড টেস্ট কেসের চেয়ে দ্রুত এবং ইভেন্টটি ইভেন্টের জন্য পরীক্ষা কেস পরীক্ষা করার আগেই ইভেন্টটি ইতিমধ্যে বরখাস্ত হয়ে গেছে
  2. পরীক্ষার কেস উত্পাদন কোডের চেয়ে দ্রুত এবং পরীক্ষার ক্ষেত্রে ইভেন্টটির জন্য অপেক্ষা করতে হয়।

CountDownLatchএখানে অন্যান্য উত্তরে বর্ণিত হিসাবে দ্বিতীয় মামলার জন্য ব্যবহৃত হয়। এছাড়াও নোট @Orderকরুন, ইভেন্ট হ্যান্ডলার পদ্ধতিতে টীকাটি নিশ্চিত করে তোলে যে, ইভেন্টে ব্যবহৃত হ্যান্ডলার পদ্ধতিটি উত্পাদনে ব্যবহৃত অন্য কোনও ইভেন্ট শ্রোতার পরে ডেকে আনে।

@Component
class TaskCompletionEventListener {

  private Map<UUID, CountDownLatch> waitLatches = new ConcurrentHashMap<>();
  private List<UUID> eventsReceived = new ArrayList<>();

  void waitForCompletion(UUID taskId) {
    synchronized (this) {
      if (eventAlreadyReceived(taskId)) {
        return;
      }
      checkNobodyIsWaiting(taskId);
      createLatch(taskId);
    }
    waitForEvent(taskId);
  }

  private void checkNobodyIsWaiting(UUID taskId) {
    if (waitLatches.containsKey(taskId)) {
      throw new IllegalArgumentException("Only one waiting test per task ID supported, but another test is already waiting for " + taskId + " to complete.");
    }
  }

  private boolean eventAlreadyReceived(UUID taskId) {
    return eventsReceived.remove(taskId);
  }

  private void createLatch(UUID taskId) {
    waitLatches.put(taskId, new CountDownLatch(1));
  }

  @SneakyThrows
  private void waitForEvent(UUID taskId) {
    var latch = waitLatches.get(taskId);
    latch.await();
  }

  @EventListener
  @Order
  void eventReceived(TaskCompletedEvent event) {
    var taskId = event.getTaskId();
    synchronized (this) {
      if (isSomebodyWaiting(taskId)) {
        notifyWaitingTest(taskId);
      } else {
        eventsReceived.add(taskId);
      }
    }
  }

  private boolean isSomebodyWaiting(UUID taskId) {
    return waitLatches.containsKey(taskId);
  }

  private void notifyWaitingTest(UUID taskId) {
    var latch = waitLatches.remove(taskId);
    latch.countDown();
  }

}

শেষ পদক্ষেপটি একটি পরীক্ষার ক্ষেত্রে পরীক্ষার অধীনে সিস্টেমটি কার্যকর করা is আমি এখানে JUnit 5 এর সাথে একটি স্প্রিংবুট পরীক্ষা ব্যবহার করছি, তবে এটি একটি স্প্রিং প্রসঙ্গ ব্যবহার করে সমস্ত পরীক্ষার জন্য একই কাজ করা উচিত।

@SpringBootTest
class ProductionIntegrationTest {

  @Autowired
  private Production sut;

  @Autowired
  private TaskCompletionEventListener listener;

  @Test
  void thatTaskCompletesSuccessfully() {
    var taskId = UUID.randomUUID();
    sut.doSomeTask(taskId);
    listener.waitForCompletion(taskId);
    // do some assertions like looking into the DB if value was stored successfully
  }

}

দ্রষ্টব্য, এখানে অন্যান্য উত্তরের বিপরীতে, আপনি যদি একই সময়ে সমান্তরাল এবং একাধিক থ্রেডে আপনার পরীক্ষাগুলি সম্পাদন করেন তবে এই সমাধানটিও কাজ করবে।


0

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

উদাহরণস্বরূপ এই কোডটি পরীক্ষা করা যা একটি অ্যাসিনক্রোনাস পদ্ধতির ফলাফলগুলিতে কাজ করে।

public class Example {
    private Dependency dependency;

    public Example(Dependency dependency) {
        this.dependency = dependency;            
    }

    public CompletableFuture<String> someAsyncMethod(){
        return dependency.asyncMethod()
                .handle((r,ex) -> {
                    if(ex != null) {
                        return "got exception";
                    } else {
                        return r.toString();
                    }
                });
    }
}

public class Dependency {
    public CompletableFuture<Integer> asyncMethod() {
        // do some async stuff       
    }
}

পরীক্ষায় উপকৃত হয়ে সিঙ্ক্রোনাস বাস্তবায়নের সাথে নির্ভরতা। ইউনিট পরীক্ষাটি সম্পূর্ণ সিঙ্ক্রোনাস এবং 150 মিমিগুলিতে চলে runs

public class DependencyTest {
    private Example sut;
    private Dependency dependency;

    public void setup() {
        dependency = Mockito.mock(Dependency.class);;
        sut = new Example(dependency);
    }

    @Test public void success() throws InterruptedException, ExecutionException {
        when(dependency.asyncMethod()).thenReturn(CompletableFuture.completedFuture(5));

        // When
        CompletableFuture<String> result = sut.someAsyncMethod();

        // Then
        assertThat(result.isCompletedExceptionally(), is(equalTo(false)));
        String value = result.get();
        assertThat(value, is(equalTo("5")));
    }

    @Test public void failed() throws InterruptedException, ExecutionException {
        // Given
        CompletableFuture<Integer> c = new CompletableFuture<Integer>();
        c.completeExceptionally(new RuntimeException("failed"));
        when(dependency.asyncMethod()).thenReturn(c);

        // When
        CompletableFuture<String> result = sut.someAsyncMethod();

        // Then
        assertThat(result.isCompletedExceptionally(), is(equalTo(false)));
        String value = result.get();
        assertThat(value, is(equalTo("got exception")));
    }
}

আপনি অ্যাসিঙ্ক আচরণটি পরীক্ষা করতে পারবেন না তবে যুক্তিটি সঠিক হলে আপনি পরীক্ষা করতে পারেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.