代码之家  ›  专栏  ›  技术社区  ›  Francesco

模拟顶点。io异步处理程序

  •  8
  • Francesco  · 技术社区  · 7 年前

    当我进行同步时,我编写了模拟持久性部分的单元测试,并检查调用方的行为。下面是我通常做的一个例子:

    @Mock
    private OfferPersistenceServiceImpl persistenceService;
    @Inject
    @InjectMocks
    private OfferServiceImpl offerService;
    ...
    @Test
    public void createInvalidOffer() {
      offer = new Offer(null, null, null, null, null, 4, 200D, 90D);
      String expectedMessage = Offer.class.getName() + " is not valid: " + offer.toString();
      Mockito.when(persistenceService.create(offer)).thenThrow(new IllegalArgumentException(expectedMessage));
      Response response = offerService.create(offer);
      Mockito.verify(persistenceService, Mockito.times(1)).create(offer);
      Assert.assertEquals(INVALID_INPUT, response.getStatus());
      String actualMessage = response.getEntity().toString();
      Assert.assertEquals(expectedMessage, actualMessage);
    }
    

    但现在我爱上了Vertx。io(我对它很陌生),我想异步。美好的但是Vertx有处理程序,所以要模拟的新持久性组件如下所示:

    ...
    mongoClient.insert(COLLECTION, offer, h-> {
      ...
    });
    

    所以我在猜测如何模仿handler h 来测试谁在使用它 mongoClient 或者即使这是使用Vertx进行测试的正确方法。io。我正在使用 vertx.io 3.5.0 , junit 4.12 mockito 2.13.0 . 谢谢

    使现代化 我试着听从泽吉蒙德的建议,但我不知道莫基托 Answer ArgumentCaptor 你能帮我吗。以下是我迄今为止所做的尝试。 使用 参数捕获器 :

    JsonObject offer = Mockito.mock(JsonObject.class);
    Mockito.when(msg.body()).thenReturn(offer);         
    Mockito.doNothing().when(offerMongo).validate(offer);
    RuntimeException rex = new RuntimeException("some message");
    ...
    ArgumentCaptor<Handler<AsyncResult<String>>> handlerCaptor =
    ArgumentCaptor.forClass(Handler.class);
    ArgumentCaptor<AsyncResult<String>> asyncResultCaptor =
    ArgumentCaptor.forClass(AsyncResult.class);
    offerMongo.create(msg);
    Mockito.verify(mongoClient,
    Mockito.times(1)).insert(Mockito.anyString(), Mockito.any(), handlerCaptor.capture());
    Mockito.verify(handlerCaptor.getValue(),
    Mockito.times(1)).handle(asyncResultCaptor.capture());
    Mockito.when(asyncResultCaptor.getValue().succeeded()).thenReturn(false);
    Mockito.when(asyncResultCaptor.getValue().cause()).thenReturn(rex);
    Assert.assertEquals(Json.encode(rex), msg.body().encode());
    

    和使用 答复 :

    ArgumentCaptor<AsyncResult<String>> handlerCaptor =
    ArgumentCaptor.forClass(AsyncResult.class);
    AsyncResult<String> result = Mockito.mock(AsyncResult.class);
    Mockito.when(result.succeeded()).thenReturn(true);
    Mockito.when(result.cause()).thenReturn(rex);
    Mockito.doAnswer(new Answer<MongoClient>() {
      @Override
      public MongoClient answer(InvocationOnMock invocation) throws Throwable {
        ((Handler<AsyncResult<String>>)
        invocation.getArguments()[2]).handle(handlerCaptor.capture());
            return null;
          }
        }).when(mongoClient).insert(Mockito.anyString(), Mockito.any(),
    Mockito.any());
    userMongo.create(msg);
    Assert.assertEquals(Json.encode(rex), msg.body().encode());
    

    现在我很困惑。有没有办法嘲笑 AsyncResult 让它返回false on succeed() ?

    2 回复  |  直到 7 年前
        1
  •  9
  •   Francesco    7 年前

    最后,我得到了一些时间去调查,我做到了。这是我的解决方案。

    @RunWith(PowerMockRunner.class)
    @PowerMockRunnerDelegate(VertxUnitRunner.class)
    @PrepareForTest({ MongoClient.class })
    public class PersistenceTest {
    
    private MongoClient mongo;
    private Vertx vertx;
    
    @Before
    public void initSingleTest(TestContext ctx) throws Exception {
      vertx = Vertx.vertx();
      mongo = Mockito.mock(MongoClient.class);
      PowerMockito.mockStatic(MongoClient.class);
      PowerMockito.when(MongoClient.createShared(Mockito.any(), Mockito.any())).thenReturn(mongo);
      vertx.deployVerticle(Persistence.class, new DeploymentOptions(), ctx.asyncAssertSuccess());
    }
    
    @SuppressWarnings("unchecked")
    @Test
    public void loadSomeDocs(TestContext ctx) {
      Doc expected = new Doc();
      expected.setName("report");
      expected.setPreview("loremipsum");
      Message<JsonObject> msg = Mockito.mock(Message.class);
      Mockito.when(msg.body()).thenReturn(JsonObject.mapFrom(expected));
      JsonObject result = new JsonObject().put("name", "report").put("preview", "loremipsum");
      AsyncResult<JsonObject> asyncResult = Mockito.mock(AsyncResult.class);
      Mockito.when(asyncResult.succeeded()).thenReturn(true);
      Mockito.when(asyncResult.result()).thenReturn(result);
      Mockito.doAnswer(new Answer<AsyncResult<JsonObject>>() {
        @Override
        public AsyncResult<JsonObject> answer(InvocationOnMock arg0) throws Throwable {
        ((Handler<AsyncResult<JsonObject>>) arg0.getArgument(3)).handle(asyncResult);
        return null;
        }
      }).when(mongo).findOne(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
      Async async = ctx.async();
      vertx.eventBus().send("persistence", new JsonObject(), msgh -> {
        if (msgh.failed()) {
        System.out.println(msgh.cause().getMessage());
        }
        ctx.assertTrue(msgh.succeeded());
        ctx.assertEquals(expected, Json.decodeValue(msgh.result().body().toString(), Doc.class));
        async.complete();
      });
      async.await();
      }
    }
    

    使用 Powemockito 嘲笑 MongoClient.createShared 静态方法,所以当verticle启动时,您将有您的模拟。模拟异步处理程序需要编写一些代码。正如您所看到的,mocking开始于 Message<JsonObject> msg = Mockito.mock(Message.class); 结束于 Mockito.doAnswer(new Answer... . 在 Answer 的方法选择处理程序参数并强制它处理异步结果,然后就完成了。

        2
  •  2
  •   Domenic D.    3 年前

    通常,我会用注释来发布此内容,但格式会丢失。公认的解决方案非常有效,只需注意,可以使用Java 8+对其进行简化,并且可以使用实际对象而不是JSON。

    doAnswer((Answer<AsyncResult<List<Sample>>>) arguments -> {
                ((Handler<AsyncResult<List<Sample>>>) arguments.getArgument(1)).handle(asyncResult);
                return null;
            }).when(sampleService).findSamplesBySampleFilter(any(), any());
    

    getArgument(1)是指方法中处理程序参数的索引,例如:

    @Fluent
    @Nonnull
    SampleService findSamplesBySampleFilter(@Nonnull final SampleFilter sampleFilter,
                                      @Nonnull final Handler<AsyncResult<List<Sample>>> resultHandler);