java - Akka:测试主管推荐

标签 java unit-testing akka typesafe-stack akka-supervision

我对 Akka 和使用 Java 来编写我的系统非常陌生。

问题定义
- 我有一个 TenantMonitor,当接收到 TenantMonitorMessage() 时,它会启动一个新的 actor DiskMonitorActor
- DiskMonitorActor 可能因各种原因而失败,并可能抛出 DiskExceptionDiskMonitorActor 已经过单元测试。

我需要什么?
- 我想测试行为 TenantMonitorActor,这样当 DiskException 发生时,它会采取正确的操作,例如 stop()resume() 或任何(取决于我的应用程序可能需要的)

我尝试了什么?
基于documentation ,我能执行的最接近的是名为 Expecting Log Messages 的部分。

哪里需要帮助?
- 虽然我知道期望正确的错误日志很重要,但它只是断言第一部分,异常被抛出并被正确记录,但无助于断言正确的 strategy 被调用

代码?
TenantMonitorActor

public class TenantMonitorActor extends UntypedActor {

  public static final String DISK_MONITOR = "diskMonitor";
  private static final String assetsLocationKey = "tenant.assetsLocation";
  private static final String schedulerKey = "monitoring.tenant.disk.schedule.seconds";
  private static final String thresholdPercentKey = "monitoring.tenant.disk.threshold.percent";

  private final LoggingAdapter logging = Logging.getLogger(getContext().system(), this);
  private final Config config;

  private TenantMonitorActor(final Config config) {
    this.config = config;
  }

  private static final SupervisorStrategy strategy =
      new OneForOneStrategy(1, Duration.create(1, TimeUnit.SECONDS),
                            new Function<Throwable, Directive>() {

                              public Directive apply(final Throwable param) throws Exception {
                                if (param instanceof DiskException) {
                                  return stop();
                                }
                                return restart();
                              }
                            });

  public static Props props(final Config config) {
    return Props.create(new Creator<TenantMonitorActor>(){
      public TenantMonitorActor create() throws Exception {
        return new TenantMonitorActor(config);
      }
    });
  }

  @Override
  public void onReceive(final Object message) throws Exception {
    if (message instanceof TenantMonitorMessage) {
      logging.info("Tenant Monitor Setup");
      setupDiskMonitoring();
    }
  }

  @Override
  public SupervisorStrategy supervisorStrategy() {
    return strategy;
  }



  private void setupDiskMonitoring() {
    final ActorRef diskMonitorActorRef = getDiskMonitorActorRef(config);

    final FiniteDuration start = Duration.create(0, TimeUnit.SECONDS);
    final FiniteDuration recurring = Duration.create(config.getInt(schedulerKey),
                                                     TimeUnit.SECONDS);

    final ActorSystem system = getContext().system();
    system.scheduler()
        .schedule(start, recurring, diskMonitorActorRef,
                  new DiskMonitorMessage(), system.dispatcher(), null);
  }

  private ActorRef getDiskMonitorActorRef(final Config monitoringConf) {
    final Props diskMonitorProps =
        DiskMonitorActor.props(new File(monitoringConf.getString(assetsLocationKey)),
                               monitoringConf.getLong(thresholdPercentKey));
    return getContext().actorOf(diskMonitorProps, DISK_MONITOR);

  }
}

测试

  @Test
  public void testActorForNonExistentLocation() throws Exception {
    final Map<String, String> configValues =
        Collections.singletonMap("tenant.assetsLocation", "/non/existentLocation");
    final Config config = mergeConfig(configValues);

    new JavaTestKit(system) {{
      assertEquals("system", system.name());

      final Props props = TenantMonitorActor.props(config);
      final ActorRef supervisor = system.actorOf(props, "supervisor");
      new EventFilter<Void>(DiskException.class) {

        @Override
        protected Void run() {
          supervisor.tell(new TenantMonitorMessage(), ActorRef.noSender());
          return null;
        }
      }.from("akka://system/user/supervisor/diskMonitor").occurrences(1).exec();

    }};
  }

更新
我能写的最好的是确保 DiskMonitor 在发生异常时停止

@Test
  public void testSupervisorForFailure() {
    new JavaTestKit(system) {{

      final Map<String, String> configValues =
          Collections.singletonMap("tenant.assetsLocation", "/non/existentLocation");
      final Config config = mergeConfig(configValues);

      final TestActorRef<TenantMonitorActor> tenantTestActorRef = getTenantMonitorActor(config);
      final ActorRef diskMonitorRef = tenantTestActorRef.underlyingActor().getContext()
          .getChild(TenantMonitorActor.DISK_MONITOR);

      final TestProbe testProbeDiskMonitor = new TestProbe(system);
      testProbeDiskMonitor.watch(diskMonitorRef);
      tenantTestActorRef.tell(new TenantMonitorMessage(), getRef());
      testProbeDiskMonitor.expectMsgClass(Terminated.class);
    }};
  }

有没有更好的方法?

最佳答案

我觉得测试主管策略是某种灰色地带——我们从哪里开始测试 Akka 本身取决于个人意见,而不是一个人对框架如何工作的理解。 ORM 框架中实体的测试验证让我觉得是一个类似的问题。我们不想测试电子邮件验证逻辑是否正确(例如在 Hibernate 中),而是我们的规则是否正确声明。

按照这个逻辑,我会把测试写成这样:

final TestActorRef<TenantMonitorActor> tenantTestActorRef =
  getTenantMonitorActor(config);
SupervisorStrategy.Directive directive = tenantTestActorRef.underlyingActor()
  .supervisorStrategy().decider().apply(new DiskException());
assertEquals(SupervisorStrategy.stop(), directive);

关于java - Akka:测试主管推荐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30007217/

相关文章:

java - 使用 JSoup 转到 aspx 表单上的下一页

java - 具有多个 WebAppContext 实例的单点登录 Jetty

javascript - 为 React-Native 应用开 Jest 测试 Animated.View

c - 使用RTRT测试工具进行C语言socket单元测试查询

java - Akka Actor 的沙箱和监控

java - Android Firebase UI 无法访问 aad

java - 在多线程进程中运行存储过程

unit-testing - 使用 Moq 的 lambda 表达式 stub 上的参数计数不匹配

scala - 创建基于时间的分块 Enumeratee

scala - 使用 Akka 测试套件从 ActorRef 获取底层 Actor