java - Mockito 与 akka Actor

标签 java mocking mockito akka

我使用 akka 框架及其 Java API 和mockito + Testkit 来对 actor 进行单元测试

这是 Actor

public class K8sDeploymentCreator extends AbstractActor {
  private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);


  @Override
  public Receive createReceive() {
    return receiveBuilder().match(createK8sDeployment.class, msg -> {
      KubeNamespace kubenamespace = new KubeNamespace();
      KubeDeployment kubeDeployment = new KubeDeployment();
      Namespace namespace = kubenamespace.createNamespace(msg.kubeClient, msg.service);
      Deployment deployment = kubeDeployment.createDeployment(msg.service, msg.kubeClient, namespace);
      log.info("sending complete depl msg");

      getSender().tell(new K8sDeploymentComplete(deployment), getSelf());
    })
        .matchAny(o -> log.info("received unknown message")).build();
  }

}

这是测试类

public class K8sDeploymentCreatorTest extends JUnitSuite {

  static ActorSystem system;


  @Before
  public  void setup() {
    system = ActorSystem.create();
    KubeDeployment mockKubeDeployment = mock(KubeDeployment.class);
    KubeNamespace mockKubeNamespace = mock(KubeNamespace.class);
    Deployment deployment = Mockito.mock(Deployment.class);
    Namespace namespace = Mockito.mock(Namespace.class);
    KubernetesClient kubeClient = Mockito.mock(KubernetesClient.class);
    Service serviceTodeploy = new Service("group","artifact","version");
    DeployEnvironment deployEnvironment = new DeployEnvironment();
    deployEnvironment.setName("K8sDeploymentCreatorTest");
    serviceTodeploy.setDeployEnvironment(deployEnvironment);
    when(mockKubeNamespace.createNamespace(kubeClient, serviceTodeploy)).thenReturn(namespace);
    when(mockKubeDeployment.createDeployment(serviceTodeploy, kubeClient, namespace)).thenReturn(deployment);

  }


  @AfterClass
  public static void teardown() {
    TestKit.shutdownActorSystem(system);
    system = null;
  }

  @Test
  public void testK8sDeployment() {

    new TestKit(system) {
      {
        final Props props = Props.create(K8sDeploymentCreator.class);
        final ActorRef underTest = system.actorOf(props);
        KubeDeployment mockKubeDeployment = mock(KubeDeployment.class);
        KubeNamespace mockKubeNamespace = mock(KubeNamespace.class);
        Deployment deployment = Mockito.mock(Deployment.class);
        Namespace namespace = Mockito.mock(Namespace.class);
        KubernetesClient kubeClient = Mockito.mock(KubernetesClient.class);
        DeployEnvironment deployEnvironment = new DeployEnvironment();
        deployEnvironment.setName("K8sDeploymentCreatorTest");
        Service serviceTodeploy = new Service("group","artifact","version");
        serviceTodeploy.setDeployEnvironment(deployEnvironment);

        createK8sDeployment msg = new createK8sDeployment(serviceTodeploy, kubeClient);
        underTest.tell(msg, getRef());
expectMsg(K8sDeploymentComplete)

      }
    };
  }

}

尝试在 createNamespace() 内执行代码时会出现 NPE (NullPointerException) 失败。这个方法已经被 mock 了,它是否应该跳过执行并只返回when语句所说的应该返回的内容?

这是因为我正在实例化 KubeNamspace 的新对象以及 KubeDeployment,因为联系人用于模拟?

最佳答案

您实际上并没有在测试中 mock 任何内容。您正在创建模拟对象,但它们没有被注入(inject)到被测试的代码中。您的参与者正在响应消息时执行以下代码:

KubeNamespace kubeNamespace = new KubeNamespace();
KubeDeployment kubeDeployment = new KubeDeployment();

这会创建新的未模拟对象,这些对象将按编码运行它们的过程 - 并且通常会导致 NPE,因为它们没有依赖的外部依赖项。

如果您想模拟以这种方式创建的对象,您要么必须重构代码以将它们的创建提取到可模拟工厂类中,要么使用更具侵入性的模拟库,例如 PowerMockjMockit .

工厂模拟示例

class KubeFactory {
    public KubeNamespace makeNamespace() {
        return new KubeNamespace();
    }
    public KubeDeployment makeDeployment() {
        return new KubeDeployment();
    }
}

public class K8sDeploymentCreator extends AbstractActor {

    private final KubeFactory factory;

    K8sDeploymentCreator() {
        this(new KubeFactory());
    }

    // This constructor allows you to override the factory used for testing
    K8sDeploymentCreator(KubeFactory factory) {
        this.factory = factory;
    }
    @Override
    public Receive createReceive() {
      return receiveBuilder().match(createK8sDeployment.class, msg -> {
          KubeNamespace kubenamespace = factory.makeNamespace();
          KubeDeployment kubeDeployment = factory.makeDeployment();
          // rest is as before...
        });
    }
}

然后在您的测试类中创建一个测试 KubeFactory ,它返回您正在测试的类的模拟实例:

@Test
public void testK8sDeployment() {

  new TestKit(system) {
    {
      final KubeFactory mockFactory = mock(KubeFactory.class);
      when(mockFactory.makeNamespace()).thenReturn(mockKubeNamespace);
      when(mockFactory.makeDeployment()).thenReturn(mockKubeDeployment);
      final Props props = Props.create(K8sDeploymentCreator.class, mockFactory);
      final ActorRef underTest = system.actorOf(props);
      // and so on...
    }
  }
}

关于java - Mockito 与 akka Actor ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49497065/

相关文章:

java traits 还是 mixins 模式?

Python Mocking - 如何修补函数内的变量

python - 如何模拟 MongoClient 进行 python 单元测试?

javascript - 无法 stub 不存在的自有属性 findOne

java - 用于验证变量已更改的单元测试

JAVA Maven 全新安装测试未完成迭代器

java - PackageInfo LoadLabel 性能低下

java - Java中如何检查一个类是否有实例?

java - 在 JFrame 中显示 JPanel 的内容

java - Mockito 静态函数模拟