Unit tests for actors, no state. (#117)

* actor unit tests, no state

clean up a bit

* Rename class

* Fix merge

Co-authored-by: Artur Souza <artursouza.ms@outlook.com>
This commit is contained in:
Leon Mai 2020-01-17 15:14:05 -08:00 committed by Artur Souza
parent 8d91f9b22c
commit 04ee301371
5 changed files with 829 additions and 9 deletions

View File

@ -0,0 +1,223 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyForTestsImpl;
import io.dapr.client.DaprClient;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.io.NotSerializableException;
import java.nio.charset.IllegalCharsetNameException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ActorNoStateTest {
private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger();
private final ActorRuntimeContext context = createContext();
private ActorManager<ActorImpl> manager = new ActorManager<>(context);
public interface MyActor {
// The test will only call the versions of this in a derived class to the user code base class.
// The user code base class version will throw.
Mono<String> stringInStringOut(String input);
Mono<Boolean> stringInBooleanOut(String input);
Mono<Void> stringInVoidOutIntentionallyThrows(String input);
Mono<MyData> classInClassOut(MyData input);
}
@ActorType(Name = "MyActor")
public static class ActorImpl extends AbstractActor implements MyActor, Actor {
private final ActorId id;
private boolean activated;
private boolean methodReturningVoidInvoked;
//public MyActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
public ActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
this.id = id;
this.activated = true;
this.methodReturningVoidInvoked = false;
}
@Override
public Mono<String> stringInStringOut(String s) {
return Mono.fromSupplier(() -> {
return s + s;
}
);
}
@Override
public Mono<Boolean> stringInBooleanOut(String s) {
return Mono.fromSupplier(() -> {
if (s.equals("true")) {
return true;
} else {
return false;
}
});
}
@Override
public Mono<Void> stringInVoidOutIntentionallyThrows(String input) {
return Mono.fromRunnable(() -> {
// IllegalMonitorStateException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new IllegalMonitorStateException("IntentionalException");
});
}
@Override
public Mono<MyData> classInClassOut(MyData input) {
return Mono.fromSupplier(() -> {
return new MyData(
input.getName() + input.getName(),
input.getNum() + input.getNum());
});
}
}
static class MyData {
private String name;
private int num;
public MyData() {
this.name = "";
this.num = 0;
}
public MyData(String name, int num) {
this.name = name;
this.num = num;
}
public String getName() {
return this.name;
}
public int getNum() {
return this.num;
}
}
@Test
public void stringInStringOut() {
ActorProxy proxy = createActorProxy();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
Assert.assertEquals(
"abcabc",
proxy.invokeActorMethod("stringInStringOut", "abc", String.class).block());
}
@Test
public void stringInBooleanOut() {
ActorProxy proxy = createActorProxy();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
Assert.assertEquals(
false,
proxy.invokeActorMethod("stringInBooleanOut", "hello world", Boolean.class).block());
Assert.assertEquals(
true,
proxy.invokeActorMethod("stringInBooleanOut", "true", Boolean.class).block());
}
@Test(expected = IllegalMonitorStateException.class)
public void stringInVoidOutIntentionallyThrows() {
ActorProxy actorProxy = createActorProxy();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
actorProxy.invokeActorMethod("stringInVoidOutIntentionallyThrows", "hello world").block();
}
@Test
public void classInClassOut() {
ActorProxy actorProxy = createActorProxy();
MyData d = new MyData("hi", 3);
// this should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
MyData response = actorProxy.invokeActorMethod("classInClassOut", d, MyData.class).block();
Assert.assertEquals(
"hihi",
response.getName());
Assert.assertEquals(
6,
response.getNum());
}
private static ActorId newActorId() {
return new ActorId(Integer.toString(ACTOR_ID_COUNT.incrementAndGet()));
}
private ActorProxy createActorProxy() {
ActorId actorId = newActorId();
// Mock daprClient for ActorProxy only, not for runtime.
DaprClient daprClient = mock(DaprClient.class);
when(daprClient.invokeActorMethod(
eq(context.getActorTypeInformation().getName()),
eq(actorId.toString()),
any(),
any()))
.thenAnswer(invocationOnMock ->
this.manager.invokeMethod(
new ActorId(invocationOnMock.getArgument(1, String.class)),
invocationOnMock.getArgument(2, String.class),
Utilities.toStringOrNull(context.getActorSerializer().unwrapData(
invocationOnMock.getArgument(3, String.class))))
.map(s -> {
try {
return context.getActorSerializer().wrapData(s.getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
}));
this.manager.activateActor(actorId).block();
return new ActorProxyForTestsImpl(
context.getActorTypeInformation().getName(),
actorId,
new ActorStateSerializer(),
daprClient);
}
private static <T extends AbstractActor> ActorRuntimeContext createContext() {
DaprClient daprClient = mock(DaprClient.class);
when(daprClient.registerActorTimer(any(), any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.registerActorReminder(any(), any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.unregisterActorTimer(any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.unregisterActorReminder(any(), any(), any())).thenReturn(Mono.empty());
return new ActorRuntimeContext(
mock(ActorRuntime.class),
new ActorStateSerializer(),
new DefaultActorFactory<T>(),
ActorTypeInformation.create(ActorImpl.class),
daprClient,
mock(DaprStateAsyncProvider.class)
);
}
}

View File

@ -608,7 +608,7 @@ public class ActorStatefulTest {
this.manager.invokeMethod(
new ActorId(invocationOnMock.getArgument(1, String.class)),
invocationOnMock.getArgument(2, String.class),
toStringOrNull(context.getActorSerializer().unwrapData(
Utilities.toStringOrNull(context.getActorSerializer().unwrapData(
invocationOnMock.getArgument(3, String.class))))
.map(s -> {
try {
@ -632,14 +632,6 @@ public class ActorStatefulTest {
return this.context.getActorSerializer().serializeString(params);
}
private static String toStringOrNull(byte[] s) {
if (s == null) {
return null;
}
return new String(s);
}
private static ActorId newActorId() {
return new ActorId(Integer.toString(ACTOR_ID_COUNT.incrementAndGet()));
}

View File

@ -0,0 +1,377 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyForTestsImpl;
import io.dapr.client.DaprClient;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.io.NotSerializableException;
import java.nio.charset.IllegalCharsetNameException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class DerivedActorTest {
private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger();
private final ActorRuntimeContext context = createContext();
private ActorManager<ActorChild> manager = new ActorManager<>(context);
public interface MyActor {
// These 4 will be implemented in the user code class that extends AbstractActor, but it
// will not be implemented in another class that will inherit that.
Mono<String> onlyImplementedInParentStringInStringOut(String input);
Mono<Boolean> onlyImplementedInParentStringInBooleanOut(String input);
Mono<Void> onlyImplementedInParentStringInVoidOut(String input);
Mono<MyData> onlyImplementedInParentClassInClassOut(MyData input);
// used to validate onlyImplementedInParentStringInVoidOut() was called
boolean methodReturningVoidInvoked();
// The test will only call the versions of this in a derived class to the user code base class.
// The user code base class version will throw.
Mono<String> stringInStringOut(String input);
Mono<Boolean> stringInBooleanOut(String input);
Mono<Void> stringInVoidOut(String input);
Mono<Void> stringInVoidOutIntentionallyThrows(String input);
Mono<MyData> classInClassOut(MyData input);
}
@ActorType(Name = "MyActor")
public static class ActorParent extends AbstractActor implements MyActor, Actor {
private final ActorId id;
private boolean activated;
private boolean methodReturningVoidInvoked;
public ActorParent(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
this.id = id;
this.activated = true;
this.methodReturningVoidInvoked = false;
}
@Override
public Mono<String> onlyImplementedInParentStringInStringOut(String input) {
return Mono.fromSupplier(() -> {
return input + input + input;
});
}
@Override
public Mono<Boolean> onlyImplementedInParentStringInBooleanOut(String input) {
return Mono.fromSupplier(() -> {
if (input.equals("icecream")) {
return true;
} else {
return false;
}
});
}
@Override
public Mono<Void> onlyImplementedInParentStringInVoidOut(String input) {
return Mono.fromRunnable(() -> {
this.methodReturningVoidInvoked = true;
System.out.println("Received " + input);
});
}
@Override
public Mono<MyData> onlyImplementedInParentClassInClassOut(MyData input) {
return Mono.fromSupplier(() -> {
return new MyData(
input.getName() + input.getName() + input.getName(),
input.getNum() + input.getNum() + input.getNum());
});
}
@Override
public boolean methodReturningVoidInvoked() {
return this.methodReturningVoidInvoked;
}
@Override
public Mono<String> stringInStringOut(String s) {
return Mono.fromSupplier(() -> {
// In the cases below we intentionally only call the derived version of this.
// ArithmeticException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new ArithmeticException("This method should not have been called");
}
);
}
@Override
public Mono<Boolean> stringInBooleanOut(String s) {
return Mono.fromSupplier(() -> {
// In the cases below we intentionally only call the derived version of this.
// ArithmeticException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new ArithmeticException("This method should not have been called");
});
}
@Override
public Mono<Void> stringInVoidOut(String input) {
return Mono.fromRunnable(() -> {
this.methodReturningVoidInvoked = true;
System.out.println("Received " + input);
});
}
@Override
public Mono<Void> stringInVoidOutIntentionallyThrows(String input) {
return Mono.fromRunnable(() -> {
// IllegalMonitorStateException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new IllegalMonitorStateException("IntentionalException");
});
}
@Override
public Mono<MyData> classInClassOut(MyData input) {
return Mono.fromSupplier(() -> {
// In the cases below we intentionally only call the derived version of this.
// ArithmeticException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new ArithmeticException("This method should not have been called");
});
}
}
public static class ActorChild extends ActorParent implements MyActor, Actor {
private final ActorId id;
private boolean activated;
public ActorChild(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
this.id = id;
this.activated = true;
}
@Override
public Mono<String> stringInStringOut(String s) {
return Mono.fromSupplier(() -> {
return s + s;
}
);
}
@Override
public Mono<Boolean> stringInBooleanOut(String s) {
return Mono.fromSupplier(() -> {
if (s.equals("true")) {
return true;
} else {
return false;
}
});
}
@Override
public Mono<MyData> classInClassOut(MyData input) {
return Mono.fromSupplier(() -> {
return new MyData(
input.getName() + input.getName(),
input.getNum() + input.getNum());
});
}
}
static class MyData {
private String name;
private int num;
public MyData() {
this.name = "";
this.num = 0;
}
public MyData(String name, int num) {
this.name = name;
this.num = num;
}
public String getName() {
return this.name;
}
public int getNum() {
return this.num;
}
}
@Test
public void stringInStringOut() {
ActorProxy proxy = createActorProxyForActorChild();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
Assert.assertEquals(
"abcabc",
proxy.invokeActorMethod("stringInStringOut", "abc", String.class).block());
}
@Test
public void stringInBooleanOut() {
ActorProxy proxy = createActorProxyForActorChild();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
Assert.assertEquals(
false,
proxy.invokeActorMethod("stringInBooleanOut", "hello world", Boolean.class).block());
Assert.assertEquals(
true,
proxy.invokeActorMethod("stringInBooleanOut", "true", Boolean.class).block());
}
@Test
public void stringInVoidOut() {
ActorProxy actorProxy = createActorProxyForActorChild();
// stringInVoidOut() has not been invoked so this is false
Assert.assertEquals(
false,
actorProxy.invokeActorMethod("methodReturningVoidInvoked", Boolean.class).block());
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
actorProxy.invokeActorMethod("stringInVoidOut", "hello world").block();
Assert.assertEquals(
true,
actorProxy.invokeActorMethod("methodReturningVoidInvoked", Boolean.class).block());
}
@Test(expected = IllegalMonitorStateException.class)
public void stringInVoidOutIntentionallyThrows() {
ActorProxy actorProxy = createActorProxyForActorChild();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
actorProxy.invokeActorMethod("stringInVoidOutIntentionallyThrows", "hello world").block();
}
@Test
public void classInClassOut() {
ActorProxy actorProxy = createActorProxyForActorChild();
MyData d = new MyData("hi", 3);
// this should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
MyData response = actorProxy.invokeActorMethod("classInClassOut", d, MyData.class).block();
Assert.assertEquals(
"hihi",
response.getName());
Assert.assertEquals(
6,
response.getNum());
}
// The actor methods this test invokes are all implemented in ActorParent only. We're asserting it's callable when the actor proxy is for an ActorChild.
@Test
public void testInheritedActorMethods() {
ActorProxy actorProxy = createActorProxyForActorChild();
Assert.assertEquals(
"www",
actorProxy.invokeActorMethod("onlyImplementedInParentStringInStringOut", "w", String.class).block());
Assert.assertEquals(
true,
actorProxy.invokeActorMethod("onlyImplementedInParentStringInBooleanOut", "icecream", Boolean.class).block());
// onlyImplementedInParentStringInVoidOut() has not been invoked so this is false
Assert.assertEquals(
false,
actorProxy.invokeActorMethod("methodReturningVoidInvoked", Boolean.class).block());
actorProxy.invokeActorMethod("onlyImplementedInParentStringInVoidOut", "icecream", Boolean.class).block();
// now it should return true.
Assert.assertEquals(
true,
actorProxy.invokeActorMethod("methodReturningVoidInvoked", Boolean.class).block());
MyData d = new MyData("hi", 3);
MyData response = actorProxy.invokeActorMethod("onlyImplementedInParentClassInClassOut", d, MyData.class).block();
Assert.assertEquals(
"hihihi",
response.getName());
Assert.assertEquals(
9,
response.getNum());
}
private static ActorId newActorId() {
return new ActorId(Integer.toString(ACTOR_ID_COUNT.incrementAndGet()));
}
private ActorProxy createActorProxyForActorChild() {
ActorId actorId = newActorId();
// Mock daprClient for ActorProxy only, not for runtime.
DaprClient daprClient = mock(DaprClient.class);
when(daprClient.invokeActorMethod(
eq(context.getActorTypeInformation().getName()),
eq(actorId.toString()),
any(),
any()))
.thenAnswer(invocationOnMock ->
this.manager.invokeMethod(
new ActorId(invocationOnMock.getArgument(1, String.class)),
invocationOnMock.getArgument(2, String.class),
Utilities.toStringOrNull(context.getActorSerializer().unwrapData(
invocationOnMock.getArgument(3, String.class))))
.map(s -> {
try {
return context.getActorSerializer().wrapData(s.getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
}));
this.manager.activateActor(actorId).block();
return new ActorProxyForTestsImpl(
context.getActorTypeInformation().getName(),
actorId,
new ActorStateSerializer(),
daprClient);
}
private static <T extends AbstractActor> ActorRuntimeContext createContext() {
DaprClient daprClient = mock(DaprClient.class);
when(daprClient.registerActorTimer(any(), any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.registerActorReminder(any(), any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.unregisterActorTimer(any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.unregisterActorReminder(any(), any(), any())).thenReturn(Mono.empty());
return new ActorRuntimeContext(
mock(ActorRuntime.class),
new ActorStateSerializer(),
new DefaultActorFactory<T>(),
ActorTypeInformation.create(ActorChild.class),
daprClient,
mock(DaprStateAsyncProvider.class)
);
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyForTestsImpl;
import io.dapr.client.DaprClient;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.io.NotSerializableException;
import java.nio.charset.IllegalCharsetNameException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ThrowFromPreAndPostActorMethodsTest {
private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger();
private final ActorRuntimeContext context = createContext();
private ActorManager<ActorChild> manager = new ActorManager<>(context);
public interface MyActor {
Mono<Boolean> stringInBooleanOut(String input);
}
@ActorType(Name = "MyActor")
public static class ActorParent extends AbstractActor implements MyActor, Actor {
private final ActorId id;
private boolean activated;
private boolean methodReturningVoidInvoked;
public ActorParent(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
this.id = id;
this.activated = true;
this.methodReturningVoidInvoked = false;
}
@Override
public Mono<Void> onPreActorMethodInternal(ActorMethodContext actorMethodContext) {
// IllegalMonitorStateException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new IllegalMonitorStateException("Intentional throw from onPreActorMethodInternal");
}
@Override
public Mono<Boolean> stringInBooleanOut(String s) {
return Mono.fromSupplier(() -> {
// In the cases below we intentionally only call the derived version of this.
// ArithmeticException is being thrown only because it's un unusual exception so it's unlikely
// to collide with something else.
throw new ArithmeticException("This method should not have been called");
});
}
}
public static class ActorChild extends ActorParent implements MyActor, Actor {
private final ActorId id;
private boolean activated;
public ActorChild(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
this.id = id;
this.activated = true;
}
@Override
public Mono<Boolean> stringInBooleanOut(String s) {
return Mono.fromSupplier(() -> {
if (s.equals("true")) {
return true;
} else {
return false;
}
});
}
}
static class MyData {
private String name;
private int num;
public MyData() {
this.name = "";
this.num = 0;
}
public MyData(String name, int num) {
this.name = name;
this.num = num;
}
public String getName() {
return this.name;
}
public int getNum() {
return this.num;
}
}
// IllegalMonitorStateException should be intentionally thrown. This type was chosen for this test just because
// it is unlikely to collide.
@Test(expected = IllegalMonitorStateException.class)
public void stringInBooleanOut1() {
ActorProxy proxy = createActorProxyForActorChild();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
Assert.assertEquals(
false,
proxy.invokeActorMethod("stringInBooleanOut", "hello world", Boolean.class).block());
}
// IllegalMonitorStateException should be intentionally thrown. This type was chosen for this test just because
// it is unlikely to collide.
@Test(expected = IllegalMonitorStateException.class)
public void stringInBooleanOut2() {
ActorProxy proxy = createActorProxyForActorChild();
// these should only call the actor methods for ActorChild. The implementations in ActorParent will throw.
Assert.assertEquals(
true,
proxy.invokeActorMethod("stringInBooleanOut", "true", Boolean.class).block());
}
private static ActorId newActorId() {
return new ActorId(Integer.toString(ACTOR_ID_COUNT.incrementAndGet()));
}
private ActorProxy createActorProxyForActorChild() {
ActorId actorId = newActorId();
// Mock daprClient for ActorProxy only, not for runtime.
DaprClient daprClient = mock(DaprClient.class);
when(daprClient.invokeActorMethod(
eq(context.getActorTypeInformation().getName()),
eq(actorId.toString()),
any(),
any()))
.thenAnswer(invocationOnMock ->
this.manager.invokeMethod(
new ActorId(invocationOnMock.getArgument(1, String.class)),
invocationOnMock.getArgument(2, String.class),
Utilities.toStringOrNull(context.getActorSerializer().unwrapData(
invocationOnMock.getArgument(3, String.class) )))
.map(s -> {
try {
return context.getActorSerializer().wrapData(s.getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
}));
this.manager.activateActor(actorId).block();
return new ActorProxyForTestsImpl(
context.getActorTypeInformation().getName(),
actorId,
new ActorStateSerializer(),
daprClient);
}
private static <T extends AbstractActor> ActorRuntimeContext createContext() {
DaprClient daprClient = mock(DaprClient.class);
when(daprClient.registerActorTimer(any(), any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.registerActorReminder(any(), any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.unregisterActorTimer(any(), any(), any())).thenReturn(Mono.empty());
when(daprClient.unregisterActorReminder(any(), any(), any())).thenReturn(Mono.empty());
return new ActorRuntimeContext(
mock(ActorRuntime.class),
new ActorStateSerializer(),
new DefaultActorFactory<T>(),
ActorTypeInformation.create(ActorChild.class),
daprClient,
mock(DaprStateAsyncProvider.class)
);
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.client.DaprClient;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Utilities
*/
class Utilities {
static String toStringOrNull(byte[] s) {
if (s == null) {
return null;
}
return new String(s);
}
}