I'm wondering how to go about checking that a method returns a container encapsulating some collection which is the aggregate of multiple other containers returned by mock objects. That is, it contains all the elements of the individual containers. I have some tests elsewhere that check the container 'works' (add/addAll/etc), so I know that works, but I'm not sure how go about with the test below 'createsRoadUsersAccordingToAllAddedCreators'.
I have a RoadUserCreationDaemon class which I call create upon which returns a RoadUserContainer according to added RoadUserCreator's. A simplified version:
public class RoadUserCreationDaemon {
private SimulationManager simulationManager;
private List<RoadUserCreator> roadUserCreators;
public RoadUserCreationDaemon(SimulationManager simulationManager) {
this.simulationManager = simulationManager;
roadUserCreators = new ArrayList<RoadUserCreator>();
}
public void addRoadUserCreator(RoadUserCreator roadUserCreator) {
roadUserCreators.add(roadUserCreator);
}
public RoadUserContainer createRoadUsers() {
RoadUserContainer roadUsers = new RoadUserContainerImpl();
for (RoadUserCreator creator : roadUserCreators) {
roadUsers.addAll(createRoadUsers(creator));
}
return roadUsers;
}
public RoadUserContainer createRoadUsers(
RoadUserCreator roadUserCreator) {
return roadUserCreator.create();
}
}
I started by writing a test (JUnit4 / JMock2.5.1) for createRoadUsers which returns a RoadUserContainer with a supplied creator. Then I started writing a test for a non-parameterised createRoadUsers to see if it returns a container with all the elements of the individual containers returned by the creators:
#RunWith(JMock.class)
public class TestRoadUserCreationDaemon {
Mockery context = new JUnit4Mockery();
private RoadUserCreationDaemon daemon;
private RoadUserCreator roadUserCreator;
private SimulationManager simulationManager;
private RoadUserContainer createdRoadUsers;
#Before
public void setUp() {
simulationManager = context.mock(SimulationManager.class);
daemon = new RoadUserCreationDaemon(simulationManager);
roadUserCreator = context.mock(RoadUserCreator.class);
createdRoadUsers = context.mock(RoadUserContainer.class);
}
#Test
public void createsRoadUsersAccordingToAllAddedCreators() throws Exception {
final RoadUserCreator anotherRoadUserCreator = context.mock(RoadUserCreator.class, "anotherRUC");
final RoadUserContainer moreCreatedRoadUsers = context.mock(RoadUserContainer.class, "moreCRU");
context.checking(new Expectations() {{
oneOf (roadUserCreator).create(); will(returnValue(createdRoadUsers));
oneOf (anotherRoadUserCreator).create(); will(returnValue(moreCreatedRoadUsers));
oneOf (createdRoadUsers).roadUsersAsList();
oneOf (moreCreatedRoadUsers).roadUsersAsList();
}});
daemon.addRoadUserCreator(roadUserCreator);
daemon.addRoadUserCreator(anotherRoadUserCreator);
daemon.createRoadUsers();
//how to easily check that the two lists are equivilant - have same items, but not the same object?
//assertEquals(createdRoadUsers, daemon.createRoadUsers() );
}
#Test
public void createsRoadUsersAccordingToCreator() throws Exception {
context.checking(new Expectations() {{
oneOf (roadUserCreator).create(); will(returnValue(createdRoadUsers));
}});
assertEquals(createdRoadUsers, daemon.createRoadUsers(roadUserCreator));
}
}
As the comment says...I'm not sure how to proceed in a non-ugly way.
The 'RoadUserContainer' interface:
public interface RoadUserContainer extends Iterable<RoadUser> {
public void add(RoadUser roadUser);
public Iterator<RoadUser> iterator();
public void addAll(RoadUserContainer createRoadUsers);
public List<RoadUser> roadUsersAsList();
public boolean equals(RoadUserContainer otherContainer);
...
}
I am new to TDD and mocking, and this is my first Java project for >6 years, so feel free to comment on ancillary aesthetics!
I would probably initially use real containers and mock the other objects. Then use hamcrest to interrogate the resulting object.
The test I would want to create would look something like this:
final RoadUser roadUser0 = context.mock(RoadUser.class, "roadUser0");
final RoadUser roadUser1 = context.mock(RoadUser.class, "roadUser1");
final RoadUser roadUser2 = context.mock(RoadUser.class, "roadUser2");
final RoadUserCreator roadUserCreator0 = context.mock(RoadUserCreator.class, "roadUserCreator0");
final RoadUserCreator roadUserCreator1 = context.mock(RoadUserCreator.class, "roadUserCreator1");
final RoadUserCreationDaemon daemon = new RoadUserCreationDaemon(null);
daemon.addRoadUserCreator(roadUserCreator0);
daemon.addRoadUserCreator(roadUserCreator1);
context.checking(new Expectations() {{
oneOf(roadUserCreator0).create(); will(returnValue(roadUsers(roadUser0, roadUser1)));
oneOf(roadUserCreator1).create(); will(returnValue(roadUsers(roadUser2)));
}});
assertThat(daemon.createRoadUsers(), contains(roadUser0, roadUser1, roadUser2));
you will need these imports from hamcrest:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
If order is not important you could use containsInAnyOrder instead of contains
you would also need to create the utility method "roadUsers"
public static RoadUserContainer roadUsers(final RoadUser... roadUsers)
{
return new RoadUserContainerImpl(roadUsers);
}
An alternative design would be to change the interface of the RoadUserCreationDaemon
public void createRoadUsers(final RoadUserContainer roadUsers) {
for (final RoadUserCreator roadUserCreator : roadUserCreators) {
roadUsers.addAll(roadUserCreator.create());
}
}
Then you could write the tests like this:
final RoadUserContainer roadUserContainer0 = context.mock(RoadUserContainer.class, "roadUserContainer0");
final RoadUserContainer roadUserContainer1 = context.mock(RoadUserContainer.class, "roadUserContainer1");
final RoadUserContainer resultRoadUserContainer = context.mock(RoadUserContainer.class, "resultRoadUserContainer");
final RoadUserCreator roadUserCreator0 = context.mock(RoadUserCreator.class, "roadUserCreator0");
final RoadUserCreator roadUserCreator1 = context.mock(RoadUserCreator.class, "roadUserCreator1");
final RoadUserCreationDaemon daemon = new RoadUserCreationDaemon(null);
daemon.addRoadUserCreator(roadUserCreator0);
daemon.addRoadUserCreator(roadUserCreator1);
context.checking(new Expectations() {
{
oneOf(roadUserCreator0).create();
will(returnValue(roadUserContainer0));
oneOf(roadUserCreator1).create();
will(returnValue(roadUserContainer1));
oneOf(resultRoadUserContainer).addAll(roadUserContainer0);
oneOf(resultRoadUserContainer).addAll(roadUserContainer1);
}
});
daemon.createRoadUsers(resultRoadUserContainer);
If the order of the calls to "addAll" is important you can use a jmock sequence
I think I would mock the Creator but have it return real Containers. The idea of the test is to make sure that the Daemon invoked all of the creator's create methods, right? So your test condition would look like
RoadUserContainer result = daemon.createRoadUsers();
// Check that the result contains both creator's users
Assert.assertEquals(createdRoadUsers.size() + moreCreatedRoadUsers.size(), result.size());
for (RoadUser user : createdRoadUsers)
Assert.assertTrue(result.contains(user));
for (RoadUser user : moreCreatedRoadUsers)
Assert.assertTrue(result.contains(user));
Related
I am very new to Akka and using Java to program my system.
Problem definition
- I have a TenantMonitor which when receives TenantMonitorMessage(), starts a new actor DiskMonitorActor.
- The DiskMonitorActor may fail for various reasons and may throw DiskException. The DiskMonitorActor has been Unit Tested.
What I need?
- I want to test behavior TenantMonitorActor, so that when DiskException happens, it takes correct action like stop(), resume() or any (depending upon what my application may need)
What I tried?
Based on the documentation, the closest I could perform is the section called Expecting Log Messages.
Where I need help?
- While I understand the expecting the correct error log is important, it just asserts first part, that exception is thrown and is logged correctly, but does not help in asserting that right strategy is called
Code?
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
#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();
}};
}
UPDATE
The best I could write is to make sure that the DiskMonitor is stopped once the exception occurs
#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);
}};
}
Are there better ways?
I have the feeling that testing supervisor strategy is some sort of grey area -- it is up to personal opinion where we start testing Akka itself, instead of one's understanding of how the framework works. Testing validation of entities in ORM frameworks strikes me as a similar problem. We don't want to test whether email validation logic is correct (e.g. in Hibernate), but rather if our rule is correctly declared.
Following this logic, I would write the test as follows:
final TestActorRef<TenantMonitorActor> tenantTestActorRef =
getTenantMonitorActor(config);
SupervisorStrategy.Directive directive = tenantTestActorRef.underlyingActor()
.supervisorStrategy().decider().apply(new DiskException());
assertEquals(SupervisorStrategy.stop(), directive);
I must be missing something about Equivalence.wrap() but the following test fails for me - using guava 18.0 at the very last line only. Why? What am I doing wrong? My aim is to deduplicate equivalent objects by adding them to a set.
#Test
public void testEquivalenceWrap()
{
final Equivalence<Program2> eq = EquivalentIfIDsEven.INSTANCE;
Program2 p1 = new Program2();
p1.setId(2L);
Program2 p2 = new Program2();
p2.setId(4L);
//sanity-test equivalence impl
assertFalse(p1.equals(p2));
assertTrue(eq.equivalent(p1, p2));
assertTrue(eq.wrap(p1).equals(eq.wrap(p2)));
//dedupe in set
final Set<Equivalence.Wrapper<Program2>> set = new HashSet<>();
set.add(eq.wrap(p1));
set.add(eq.wrap(p2));
assertEquals(1, set.size()); //FAIL: size == 2
}
You'd get this behavior if your Equivalence doesn't implement hashing correctly. Without seeing more code, I can't suggest something more specific, but that's almost certainly what's happening.
If I were you, I try to use this class (example EqualsEquivalence from javadoc in Equivalent class) in you test:
static final class EquivalentIfIDsEven extends Equivalence<Program2>
implements Serializable {
static final EquivalentIfIDsEven INSTANCE = new EquivalentIfIDsEven();
#Override protected boolean doEquivalent(Program2 a, Program2 b) {
return a.equals(b);
}
#Override protected int doHash(Program2 o) {
return o.hashCode();
}
private Program2 readResolve() {
return INSTANCE;
}
private static final long serialVersionUID = 1;
}
I'm using TestNG and JMockit for testing. My code goes like this:
public boolean testMethod(String a, String b) {
//processing .....
mockClass.mockMethod(a);
//processing....
}
The mockMethod():
Class MockClass {
public void mockMethod(String a) {
//some operations to mock
}
}
I'm using MockUp according to this question: (How to mock public void method using jmockit?)
I'm still getting the NPE. What am I doing wrong? Also, is it because I'm using it like this?
#Test
public void test() {
new Expectations() {
{
//for statements preceding mockMethod()....
new MockUp<MockClass>(){
#Mock
public void mockMethod(String a) {
//do nothing
}
};
}
};
}
I've put it outside Expectations() & used NonStrictExpectations too. How do I fix this?
If the method to be mocked is not returning anything, you don't need to do anything special in expectations. You can define your class to be mocked using #Injectable or #Mocked annotations in usual way. Optionally you can add an expectation to verify the number of times the method is called. Also you can add verification step to capture the argument "a" and do assertions on that. Refer below code sample.
#Tested
private MyClassToBeTested myClassToBeTested;
#Injectable
private MockClass mockClass;
#Test
public void test() {
// Add required expectations
new Expectations() {{
...
..
}};
// Invoke the method to be tested with test values;
String expectedA = "testValueA";
String expectedB = "testValueB";
boolean result = myClassToBeTested.testMethod(expectedA, expectedB);
// Assert the return value of the method
Assert.assertTrue(result);
// Do the verifications and assertions
new Verifications() {{
String actualA;
mockClass.mockMethod(actualA = withCapture()); times = 1;
Assert.assertNotNull("Should not be null", actualA);
Assert.assertEquals(actualA, expectedA);
...
..
}};
}
For void method mocking You can make Expectations without any result as shown below :
#Tested
private MyClassToBeTested myClassToBeTested;
#Injectable
private MockClass mockClass;
#Test
public void test() {
new Expectations() {{
mockClass.mockMethod(anyString);
}};
String inputA = "testValueA";
String inputB = "testValueB";
boolean result = myClassToBeTested.testMethod(inputA, inputB);
assertEquals(true, result);
}
I used to rely on Mockito until today I have a final class with some static methods so I've to switched to JMockit. I knew nothing about it before so the question is: how can I apply the similar logic from Mockito to JMockit?
public final class ServiceData extends BaseData {
private List<Data> data;
public ServiceData(List<Data> data) {
this.data = data;
// something else
}
public static Container getContainer() {
return super.getContainer();
}
public Data getDataAt(Index index) {
return super.getContainer().get(index);
}
}
The test written in Mockito looks like:
#Test
public void test() {
ServiceData mockServiceData = mock(ServiceData.class);
Data mockData = mock(Data.class);
// only stubbing some of the methods
Container mockContainer = spy(Container.class);
doReturn(something).when(mockContainer.someMethod());
when(mockServiceData.getContainer()).thenReturn(mockContainer);
when(mockServiceData.getDataAt(any(Index.class)).thenReturn(mockData);
// some assertions
}
This won't work since Mockito cannot mock final classes nor static methods (getContainer)
So how can I write the same logic in JMockit? Thanks.
The following is the JMockit equivalent for the example test:
#Test
public void test(
#Mocked final ServiceData mockServiceData, #Mocked final Data mockData,
#Mocked final Container mockContainer)
{
// only stubbing some of the methods
new Expectations(mockContainer) {{
mockContainer.someMethod(); result = "something";
mockServiceData.getContainer(); result = mockContainer;
mockServiceData.getDataAt((Index) any); result = mockData;
}};
// some assertions
}
To mock static methods the syntax is the same, except that you would write ServiceData.getContainer(); result = mockContainer; instead.
I have the following method....
public void testa(Car car) {
em.persist(car);
car.setEngine(null);
}
in my test i have:
protected final Car mockCar = context.mock(Car.class);
#Test
public void testCar() {
context.checking(new Expectations() {
{
oneOf(em).persist(car);
oneOf(car).setEngine(null);
}
});
this.stacker.testa(mockCar);
context.assertIsSatisfied();
}
I run this and i keep getting :
unexpected invocation car.setEngine(null)...
If i remove the code that sets the engine in the code and from the test the tests passes... im totally confused as to why this is happening...
exception:
java.lang.AssertionError: unexpected invocation: car.setEngine(null)
no expectations specified: did you...
- forget to start an expectation with a cardinality clause?
- call a mocked method to specify the parameter of an expectation?
Your problem appears to be that you have two Car objects. You have a car, which you set the expectations on, and a mockCar, which you pass through. Without seeing the definitions of these objects, I can't say for sure, but this is probably the root of your problem.
If this isn't the issue, we're going to need more code. Preferably the entire file(s).
For reference, this compiles fine and passes the tests:
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
public class TestyMcTestTest {
private final Mockery context = new Mockery();
private final EntityManager em = context.mock(EntityManager.class);
private final Stacker stacker = new Stacker(em);
private final Car mockCar = context.mock(Car.class);
#Test
public void testCar() {
context.checking(new Expectations() {{
oneOf(em).persist(mockCar);
oneOf(mockCar).setEngine(null);
}});
this.stacker.testa(mockCar);
context.assertIsSatisfied();
}
public interface Car {
void setEngine(Engine engine);
}
public interface Engine { }
public class Stacker {
private final EntityManager em;
public Stacker(EntityManager em) {
this.em = em;
}
public void testa(Car car) {
em.persist(car);
car.setEngine(null);
}
}
private interface EntityManager {
void persist(Object o);
}
}