I'm creating a database wrapper. This wrapper has a series of states:
it is created
you are signing in
you are signed id
you are connected to a specific table
Methods on my database wrapper can trigger the state transitions.
But the database interactions are asynchronous, so you don't get immediate feedback. Instead, you can register as a listener for onStateChanged events.
I'm using mockito to test the transitions, and so far I'm really amazed at how clean the use of InOrder and verify are.
But now I'm running into a problem. I'm trying to test a forbidden transition. My Database wrapper will catch that you're not in the correct state and throw an AssertionException. This Exception let's the testing crash. I'm trying to register this as an expected exception with Mockito, but I can't figure out how to do this in the correct way.
The following code is all in one function, the exception is expected by JUnit:
#Test(expected = AssertionError.class)
public void cantCreateTwice() throws Exception{
...
First I'm creating a connection, and I'm setting up a StateChangeListener which attempts the forbidden transition:
final FirebaseConnection connection = new FirebaseConnection();
StateChangeListener stateChangeListener = new StateChangeListener() {
#Override
public void onStateChanged(DBState newState) {
if(newState==DBState.SignedIn){
connection.createParty();
connection.createParty(); // forbidden
}
}
};
Next, I'm registering this listener and a mock:
StateChangeListener mockedListener = mock(StateChangeListener.class);
connection.addStateChangeListener(stateChangeListener);
connection.addStateChangeListener(mockedListener);
Then I tell Mockito about the expected exception and start the test:
doThrow(new AssertionError()).when(mockedListener).onStateChanged(DBState.SignedIn);
connection.signIn();
verify(mockedListener, times(1)).onStateChanged(DBState.SigninIn);
verify(mockedListener, times(1)).onStateChanged(DBState.SignedIn);
This will result in:
Test running failed: Instrumentation run failed due to 'Process
crashed.'
The exception that crashed the process is the expected AssertionError:
java.lang.AssertionError: the connection is not in the necessary state
I think the problem is that my exception is not triggered when a function is called on the mocked listener, but on the anonymous implementation.
But I need the anonymous implementation to trigger the forbidden functionality.
I also need the verify since my callbacks are asynchronous. If I remove the verify then the test will simply return immediately without any exception being thrown.
It seems to me that I somehow need to convert my anonymous implementation into a Mock object that can be supervised by Mockito. But mock() doesn't work on anonymous classes. And I think it's not correct to have an individual class for every snippet of logic in my test cases.
How can I tell Mockito to expect this exception ?
EDIT:
I think I've found a way, I'm using verify with a timeout:
verify(mockedListener, timeout(1000).times(1)).onStateChanged(DBState.SigninIn);
verify(mockedListener, timeout(1000).times(1)).onStateChanged(DBState.SignedIn);
connection.createParty();
connection.createParty();
This blocks until the sign-in was completed. Then it tries to create a party twice, which fails and throws the expected exception.
But I think I'm now basically misusing a feature of Mockito for synchronization. Is this the correct way to use Mockito for my problem ?
Related
We have an application with three databases. Two of them are only very seldomly updated. We tried JPA to create transactions around it and it worked for the databases, but grails then did not work on different places (gsp related I am told). This was tried quite a while ago (and not by me).
Due to delivery pressure we needed a solution that at least works for us, so I created a new aspect for the methods changing data in multiple databases. I got this to work, it is a fairly simple approach.
In the aspect we request to start a transaction for each data source, by calling getTransaction(TransactionDefinition def) with the propagation set to REQUIRES_NEW. We then proceed and finally rollback or commit depending on the outcome of the call.
However, one test flow failed. This is the scenario where the code requests a rollback by calling TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(). Of the three TransactionStatusses obtained initially, none actually returns isRollbackOnly() with true. However calling TransactionAspectSupport.currentTransationStatus().isRollbackOnly() does return true. So this seems to point to a different transaction status.
I have not been able to figure out how to make this work, other than checking this additional status. I could not find a way to change the currentTransactionStatus to the one of created TransactionStatus. Looking at the TransactionTemplate implementation, I seem to do things correctly (it also just calls getTransaction() on the datasource).
The code calling the decorated method has specified #Transactional(propagation=Propagation.NOT_SUPPORTED), so I expected no currentTransactionStatus, but one is there.
However, if it is not there the proxied code will not be able to request a rollback the standard way, which I want to be able to fix.
So the question is, how to start a transaction correctly from an Aspect so that the currentTransactionStatus is set correctly or how to set the currentTransactionStatus to what I think is the correct one.
Regards,
Wim Veldhuis.
I finally figured it out.
#Transactional leads to a different code path, where eventually TransactionAspectSupport.invokeWithinTransaction is invoked. This method will set up the current transaction correctly.
So in order to make my approach working, I needed to derive from TransactionAspectSupport, do a number of cast operations so I could get to the correct values for the invokeWithinTransaction call, and within the guarded function block use getTransaction(def) to obtain txns for the OTHER databases. I have choose the most important database to be the one used for invoke...
To make it work I had also to provide a TransactionAttributeSource, that returned my default transaction attributes.That one is stored into the TransactionAspectSupport base class during initialization.
#Around("#annotation(framework.db.MultiDbTransactional)")
public Object multiDbTransaction(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// Get class and method, needed for parent invocation. We need to cast to the actual
// implementation
MethodInvocationProceedingJoinPoint mipJoinPoint = (MethodInvocationProceedingJoinPoint) proceedingJoinPoint;
MethodSignature signature = (MethodSignature) mipJoinPoint.getSignature();
Class<?> clazz = mipJoinPoint.getTarget().getClass();
Method method = signature.getMethod();
return invokeWithinTransaction(method, clazz, new InvocationCallback() {
#Override
public Object proceedWithInvocation() throws Throwable {
// This class will create the other transactions, not of interest here.
MultiDbTxnContext ctx = new MultiDbTxnContext();
ctx.startTransactions();
/*
* We have started the transactions, so do the job. We mimic DEFAULT spring behavior
* regarding exceptions, so runtime exceptions roll back, the rest commits.
*/
try {
Object result = proceedingJoinPoint.proceed();
ctx.finishTransactions();
return result;
} catch (Error | RuntimeException re) {
ctx.rollbackTransactions();
throw re;
} catch (Throwable t) {
ctx.commitTransactions();
throw t;
}
}
});
}
The project I work on uses astyanax driver to access Cassandra. I want to implement an asynchronous operation:
MutationBatch m;
//…
ListenableFuture<OperationResult<Void>> lf = m.executeAsync();
lf.addListener(myRunnableCallback, myExecutor);
Question: assuming the exception was not thrown right away within executeAsync() call, how do I distinguish between successful and failed executions?
The only way I can think of is that when the completion callback is invoked lf.get() throws an exception in case of failure. If this is the right way, is there a document or lines in astyanax sources confirming that?
I found a workaround: instead of ListenableFuture's addListener method taking Runnable parameter I can use Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor). FutureCallback has 2 methods: onSuccess and onFailure. That solves my problem.
https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained
I am experiencing a certain bug in JUnit/JMock. I am trying to mock a couple of objects and then assert that all expectations is satisfied. I am running a simple test such as :
#Test
public void sellingPutOptionProductDoesNotCauseDisclosure() throws PositionVerificationException, DataLoadException, MissingPriceException {
final OptionProduct optionProduct = setupOptionProduct();
context.assertIsSatisfied();
}
private OptionProduct setupOptionProduct() {
final Option optionProduct = context.mock(Option.class);
context.checking(new Expectations() {
{
oneOf(optionProduct).getUnderlyingProduct();
will(returnValue(new Object()));
}
});
return optionProduct;
}
The Option is an object and I am using Mockery like this:
context = new Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
If I run the above test I am gettiing Test passed, where JVM does not terminate and the last print out in console is:
Exception in thread "main"
ANy ideas what might be causing this?
I'm a little late to the party, but I just had a similar problem and was able to track down the cause.
Normally, when a method is called that is not part of the expectations, JMock builds the log telling you what was expected that was not found. In my case, it tried to create that log message after encountering an unexpected call. The act of creating the log message threw an exception, which got JMock all confused and it reported that the test passed, when it had actually failed.
In my case, the reason that the exception was thrown was that one of the parameters being passed to the "unexpected" function call was an instance of a class. That class was initialized, in part, with mocked objects. When JMock was trying to build the error message to tell me about the unexpected invocation, it needed to describe the parameter. Usually it will say something like unexpected invocation: myobject.myMethod(param1,param2).
Because param1, in my case, contained member variables that were mock objects, and the param1 class did not define toString(), the default, Object.toString() was used.
Object.toString() is defined as: return getClass().getName() + "#" + Integer.toHexString(hashCode());
My implementation of hashcode for param1 was using some of those mocked objects. Those calls on the mocked objects to calculate the hashcode were 'unexpected' invocations themselves, leading JMock to throw an exception when trying to describe the test failure to me.
Unfortunately, instead of recognizing this condition and still reporting as much of the failure as it could, JMock seemed to give up altogether and report the test as passing and offhandedly mention Exception in thread "main."
To see if this is happening to you, I recommend you check the parameters involved in the offending function calls. If any of them are classes, not interfaces, you should see if their use of equals / hashcode / toString use any suspicious calls to member variables that may not be playing nice with how your scenario is being mocked.
The scenario is I am trying to validate a domain object and store the validation messages in another object. The class is Validator and the method looks like:
public void validate (Domain domain, ValidationInfo info)
And the ValidationInfo object has:
List<String> messages = new ArrayList<String> messages ()
The Validator class is called from a service class. Whatever validation fails for the domain object the failure messages will be stored in the list in the ValidationInfo class.
In the service test class in one of the test case i wrote the following to test the validation failure:
#Test
public void testvalidation () throws Exception {
Domain domain = createDomainWithInvalidData();
service.create (domain);
verify(mockValidator).validate(domain, validationInfo);
assertFalse (validationInfo.getMessages().isEmpty());
Now What I want is to get the failure messages from the validationInfo object so that I can write some asserts based on that. But it's not working.
Can anybody shed some light on this.
From what I gather you are unit testing the service.
Actually there's is no point in getting the messages from validationInfo as it is passed to a method of Mockito mock which does nothing by definition unless you write a stub (given(...).willReturn(...)). Even the last assertion in your code sample is not necessary as validationInfo won't be modified by the default mocked behavior of validate.
Mockito is designed and lead to test interactions between collaborators, which is already done with your verification verify(...), as you are testing the service and his collaboration with the Validator.
Testing the message that was written in ValidationInfo has nothing to do with the scope of service
However what you want is to unit test the Validator, in which you should write specific tests for validation messages. Look at the following snippet where I imaginated some part of the ValidationInfo API :
public class ValidatorTest {
Validator validator = new Validator(); // not a mock
#Test
public void when_<some_part_of_the_domain>_is_incorrect_report_validation_error() throws Exception {
// given
Domain domain = createDomainWithInvalidData();
// when
validator.validate(domain, validationInfo);
// then
assertThat(validationInfo.getMessages()).isNotEmpty()); // not really necessary
assertThat(validationInfo.getLastMessage().text()).contains("<some_part_of_the_domain> is wrong");
}
}
Note that I used the BDD keywords (given, when, then) to help me write the test. Alos I used FestAssert library which affer great assertion tools.
Hoep that helps.
I was able to achieve what i wanted after breaking my head for hours.
What we need to do is use doAnswer of mockito. Please find below the snippet:
doAnswer(new Answer<ValidationInfo> () {
#Override
public ValidationInfo answer (InvocationOnMock invocationOnMock) throws Throwable {
Object [] args = invocationOnMock.getArguments();
ValidationInfo vi = (ValidationInfo) args [1];
vi.getMessages.add("some message");
return vi;
}}).when(validator).validate (domain, validationInfo);
This way I was able to put custom failure messages in the list which made the validation fail in the service; which was the whole purpose of the test.
And earlier the test was not passing because in the service code there was a check on the size of the list. And the list size was always zero as verify will just verify whether the method gets called or not but wont actually change the state of the object.
Hope this helps. If there is any better way of achieving this please comment.
I've got a unit test for a Seam component that should fail if a value isn't provided in the context before the backing bean is created. I've been able to test this manually but would like to cover this scenario with a unit test.
I'm basically getting a org.jboss.seam.InstantiationException caused by a java.lang.IllegalArgumentException when Seam tries to create the backing bean. This is good and is what I'd expect to happen. The problem is that when I write the unit test, I can neither put a try/catch around the new FacesRequest(..) {}.run(); or use the expectedExceptions annotation. In both cases, the exception is not caught and causes the test to fail. I assume this is because of Seam's exception filter but I don't know enough about the filter to know what the correct pattern to test this..
My code with the annotation looks something like this:
// also tried IlligalArgumentException here
#Test( enabled = true, expectedExceptions = InstantiationException.class )
public void noDataTest() throws Exception
{
login( USERNAME );
// the stack trace says that the test fails on the next line.
// this is expected.
new FacesRequest( "/blah/blah/show.xhtml" ) {
#Override
protected void updateModelValues() {
}
#Override
protected void invokeApplication()
{
// we should never get here
// i'll put an failure here eventually
}
}.run();
}
I found the answer. Hopefully this helps someone else who was banging their head against the wall..
I was looking for a specific Exception but Seam was catching that Exception, asserting that an error had occurred, and then throwing a java.lang.AssertionError (java.lang.Error, not java.lang.Exception). Catching the correct Throwable and using the correct type in the annotation now work..
looks to me that your test case is expecting a empty no-arg constructor in backing bean whic is probably missing