Skip to content

Latest commit

 

History

History

unittesting

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Unit testing sample

This sample illustrates how applications can write unit tests with Dapr's Java SDK, JUnit 5 and Mockito.

Pre-requisites

Checking out the code

Clone this repository:

git clone https://github.com/dapr/java-sdk.git
cd java-sdk

Then build the Maven project:

# make sure you are in the `java-sdk` directory.
mvn install

Then change into the examples directory:

cd examples

Initialize Dapr

Run dapr init to initialize Dapr in Self-Hosted Mode if it's not already initialized.

Understanding the code

Example App Test

This example, found in DaprExampleTest.java, will simulate an application code via the App class:

  private static final class MyApp {

    private final DaprClient daprClient;

    private final Function<ActorId, MyActor> actorProxyFactory;

    /**
     * Example of constructor that can be used for production code.
     * @param client Dapr client.
     * @param actorClient Dapr Actor client.
     */
    public MyApp(DaprClient client, ActorClient actorClient) {
      this.daprClient = client;
      this.actorProxyFactory = (actorId) -> new ActorProxyBuilder<>(MyActor.class, actorClient).build(actorId);
    }

    /**
     * Example of constructor that can be used for test code.
     * @param client Dapr client.
     * @param actorProxyFactory Factory method to create actor proxy instances.
     */
    public MyApp(DaprClient client, Function<ActorId, MyActor> actorProxyFactory) {
      this.daprClient = client;
      this.actorProxyFactory = actorProxyFactory;
    }

    public String getState() {
      return daprClient.getState("appid", "statekey", String.class).block().getValue();
    }

    public String invokeActor() {
      MyActor proxy = actorProxyFactory.apply(new ActorId("myactorId"));
      return proxy.hello();
    }
  }

This class has two constructors. The first one can be used by production code by passing the proper instances of DaprClient and ActorClient. The second, contains two methods: getState() will retrieve a state from the DaprClient, while invokeActor() will create an instance of the Actor proxy for MyActor interface and invoke a method on it.

  @ActorType(name = "MyActor")
  public interface MyActor {
    String hello();
  }

The first test validates the getState() method while mocking DaprClient:

  @Test
  public void testGetState() {
    DaprClient daprClient = Mockito.mock(DaprClient.class);
    Mockito.when(daprClient.getState("appid", "statekey", String.class)).thenReturn(
        Mono.just(new State<>("statekey", "myvalue", "1")));

    MyApp app = new MyApp(daprClient, (ActorClient) null);

    String value = app.getState();

    assertEquals("myvalue", value);
  }

The second test uses a mock implementation of the factory method and checks the actor invocation by mocking the MyActor interface:

  @Test
  public void testInvokeActor() {
    MyActor actorMock = Mockito.mock(MyActor.class);
    Mockito.when(actorMock.hello()).thenReturn("hello world");

    MyApp app = new MyApp(null, actorId -> actorMock);

    String value = app.invokeActor();

    assertEquals("hello world", value);
  }

Running the example

Run this example with the following command:

java -jar target/dapr-java-sdk-examples-exec.jar org.junit.platform.console.ConsoleLauncher --select-class=io.dapr.examples.unittesting.DaprExampleTest

After running, Junit should print the output as follows:

╷
├─ JUnit Jupiter ✔
│  └─ DaprExampleTest ✔
│     ├─ testGetState() ✔
│     └─ testInvokeActor() ✔
└─ JUnit Vintage ✔

Test run finished after 1210 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         2 tests found           ]
[         0 tests skipped         ]
[         2 tests started         ]
[         0 tests aborted         ]
[         2 tests successful      ]
[         0 tests failed          ]

Example Workflow Test

This example, found in DaprWorkflowExampleTest.java, shows how to mock and test a dapr workflow:

private class DemoWorkflow extends Workflow {

  @Override
  public void run(WorkflowContext ctx) {
    String name = ctx.getName();
    String id = ctx.getInstanceId();
    try {
      ctx.waitForExternalEvent("myEvent", Duration.ofSeconds(10)).await();
    } catch (TaskCanceledException e) {
      ctx.getLogger().warn("Timed out");
    }
    String output = name + ":" + id;
    ctx.complete(output);
  }
}

The example provides its own workflow, but for a production system you would want to import and use your own workflow. The goal of unit testing a workflow is to ensure that the business logic functions as expected. For our example, that involves the following two sections:

String output = name + ":" + id;
} catch (TaskCanceledException e) {
    ctx.getLogger().warn("Timed out");
}

The first test validates the output of our workflow by mocking the WorkflowContext and verifying the .complete method:

  @Test
  public void testWorkflow() {
    WorkflowContext mockContext = Mockito.mock(WorkflowContext.class);
    String name = "DemoWorkflow";
    String id = "my-workflow-123";
  
    Mockito.when(mockContext.getName()).thenReturn(name);
    Mockito.when(mockContext.getInstanceId()).thenReturn(id);
  
    new DemoWorkflow().run(mockContext);
  
    String expectedOutput = name + ":" + id;
    Mockito.verify(mockContext, times(1)).complete(expectedOutput);
  }

The second test validates the catch block of our workflow by throwing the expected exception from our mock and verifying the expected call on our mock Logger:

  @Test
  public void testWorkflowWaitForEventTimeout() {
    WorkflowContext mockContext = Mockito.mock(WorkflowContext.class);
    Logger mockLogger = Mockito.mock(Logger.class);
  
    Mockito.when(mockContext.getLogger()).thenReturn(mockLogger);
    Mockito.when(mockContext.waitForExternalEvent(anyString(),any(Duration.class)))
    .thenThrow(TaskCanceledException.class);
  
    new DemoWorkflow().run(mockContext);
  
    Mockito.verify(mockLogger, times(1)).warn("Timed out");
}

The third test is similar but validates the inverse of the test above, ensuring that a code path we do not expect to be triggered is indeed avoided:

  @Test
  public void testWorkflowWaitForEventNoTimeout() {
    WorkflowContext mockContext = Mockito.mock(WorkflowContext.class);
    Logger mockLogger = Mockito.mock(Logger.class);

    Mockito.when(mockContext.getLogger()).thenReturn(mockLogger);

    new DemoWorkflow().run(mockContext);

    Mockito.verify(mockLogger, times(0)).warn(anyString());
}

Running the example

Run this example with the following command:

java -jar target/dapr-java-sdk-examples-exec.jar org.junit.platform.console.ConsoleLauncher --select-class=io.dapr.examples.unittesting.DaprWorkflowExampleTest

After running, Junit should print the output as follows:

╷
├─ JUnit Jupiter ✔
│  └─ DaprWorkflowExampleTest ✔
│     ├─ testWorkflowWaitForEventTimeout() ✔
│     ├─ testWorkflowWaitForEventNoTimeout() ✔
│     └─ testWorkflow() ✔
└─ JUnit Vintage ✔

Test run finished after 815 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         3 tests found           ]
[         0 tests skipped         ]
[         3 tests started         ]
[         0 tests aborted         ]
[         3 tests successful      ]
[         0 tests failed          ]