Java NegativeTest private method with Reflection to catch custom ApplicationException - java

So I'm testing a AccountService class with a mocked databaselayer.
In this AccountService class there is a private method that checks the input received from UI according to a regex.
The positive test I wrote is working fine:
#Test
public void testEmailPatroonCorrect() throws Exception{
//Correcte emails
List<String> emails = new ArrayList<>();
emails.add("user#domain.com");
emails.add("user#domain.co.in");
emails.add("user.name#domain.com");
emails.add("user_name#domain.com");
emails.add("username#yahoo.corporate.in");
Class<AccountService> foo = AccountService.class;
Method method = foo.getDeclaredMethod("checkEmailPatroon", String.class);
method.setAccessible(true);
assertThatCode(() -> {
for(String email : emails){
method.invoke(AccountService,email);
}}).doesNotThrowAnyException();
}
However for the negative test (a list with wrong email patterns) even with only one object in the list for simplicity
#Test
public void testEmailPatroonFout() throws Exception{
//Verkeerde emailpatronen
List<String> emails = new ArrayList<>();
emails.add(".username#yahoo.com");
Class<AccountService> foo = AccountService.class;
Method method = foo.getDeclaredMethod("checkEmailPatroon", String.class);
method.setAccessible(true);
assertThatThrownBy(()->{
for(String email : emails){
method.invoke(AccountService,email);
}
}).isInstanceOf(ApplicationException.class).hasMessage(ApplicationExceptionType.ONGELDIGE_EMAIL.getMsg());
}
The exception thrown during test is: java.lang.reflect.InvocationTargetException. In the application the ApplicationException gets caught just fine.
Question is how can I write a proper test for a list of wrong email patterns? (without using #VisibleForTesting functionality since it's a school project).
Many thanks!

The InvocationTargetException wraps the exception thrown within the reflectively invoked method. So you may catch the InvocationTargetException and rethrow its cause, but I’d put that into a utility method, like
public interface TestMethod<D,A> {
void invoke(D d, A a) throws Throwable;
}
static <D,A> TestMethod<D,A> method(
Class<D> declarer, String name, Class<A> argType) throws ReflectiveOperationException {
Method method = declarer.getDeclaredMethod(name, argType);
method.setAccessible(true);
return (d,a) -> {
try {
method.invoke(d, a);
} catch(InvocationTargetException ex) {
throw ex.getTargetException();
}
};
}
which you can use like
#Test
public void testEmailPatroonFout() throws Exception{
//Verkeerde emailpatronen
List<String> emails = new ArrayList<>();
emails.add(".username#yahoo.com");
TestMethod<AccountService, String> method
= method(AccountService.class, "checkEmailPatroon", String.class);
assertThatThrownBy(() -> {
for(String email : emails){
method.invoke(AccountService, email);
}
}).isInstanceOf(ApplicationException.class)
.hasMessage(ApplicationExceptionType.ONGELDIGE_EMAIL.getMsg());
}
The shape of the TestMethod interface allows the alternative implementation like
static <D,A> TestMethod<D,A> method(
Class<D> declarer, String name, Class<A> argType) throws ReflectiveOperationException {
Method method = declarer.getDeclaredMethod(name, argType);
method.setAccessible(true);
return MethodHandleProxies.asInterfaceInstance(
TestMethod.class, MethodHandles.lookup().unreflect(method));
}

Thanks to Holger I was able to write a working test for it's purpose.
#Test
public void testEmailPatroonFoutLoop() throws Throwable {
//Verkeerde emailpatronen
List<String> wrongEmails = new ArrayList<>();
wrongEmails.add(".username#yahoo.com");
wrongEmails.add("username#yahoo.com.");
wrongEmails.add("usernameyahoo.com");
wrongEmails.add("username#yahoo.c");
wrongEmails.add("use..rname#yahoo.com");
Class<AccountService> foo = AccountService.class;
Method method = foo.getDeclaredMethod("checkEmailPatroon", String.class);
method.setAccessible(true);
int countedWrongEmails = 0;
for(String email : wrongEmails){
try{
method.invoke(accServ,email);
}
catch (InvocationTargetException ie){
Exception e = (Exception) ie.getTargetException();
if(e.getMessage().equals(ApplicationExceptionType.ONGELDIGE_EMAIL.getMsg())){
countedWrongEmails++;
}
}
}
assertThat(countedWrongEmails).isEqualTo(wrongEmails.size());
}
Although I see the benefits and elegance of writing a TestMethod interface, I however do not yet possess the knowledge to grasp it's complexity. So I will stick to this test that I'll be able to explain on the verbal exam.

Related

What happens with the Executable in a JUnit assertThrows?

I understand that Junit5 Assertions.assertThrows accepts an Executable type object. So for a simple example where a Constructor may not take an empty string as a name parameter:
public Company(String aName, Calendar aFoundingDate)
{
if (aName == null || aName.length() == 0 || aName.length() > 50) {
throw new IllegalArgumentException("Invalid name");
}
this.name = aName;
foundingDate = aFoundingDate;
}
I can write a test like this:
// Company constructor test
#Test
void testCompanyConstructor() {
// Given
String emptyName = "aabbe";
Calendar validFoundingDate = Calendar.getInstance();
validFoundingDate.set(2000, 1, 1);
// Then
assertThrows(IllegalArgumentException.class, () -> new Company(emptyName, validFoundingDate));
}
What I wonder is, what happens to the executable, i.e. the Lambda Expression?
Does JUnit call execute() on the Lambda expression and in doing so, the anonymous company Object with the empty name is created and the exception is
Appendix:
These versions are equivalent:
// Company constructor test
#Test
void testCompanyConstructor() {
// Given
String emptyName = "";
Calendar validFoundingDate = Calendar.getInstance();
validFoundingDate.set(2000, 1, 1);
// Then
Executable executable = new Executable() {
public void execute() {
new Company(emptyName, validFoundingDate);
}
};
assertThrows(IllegalArgumentException.class, executable);
assertThrows(IllegalArgumentException.class, () -> new Company(emptyName, validFoundingDate));
}
When inspecting the code of assertThrows we can see that deeply there is such code placed in AssertThrows::assertThrows:
try {
executable.execute();
}
catch (Throwable actualException)
if (expectedType.isInstance(actualException)) {
return (T) actualException;
}
else {
BlacklistedExceptions.rethrowIfBlacklisted(actualException);
String message = buildPrefix(nullSafeGet(messageOrSupplier))
+ format(expectedType, actualException.getClass(), "Unexpected exception type thrown");
throw new AssertionFailedError(message, actualException);
}
}
so it basically invokes Executable and catches Throwable that might be thrown and returns it. If no exception was thrown or the type of exception is different than expected - the assertion fails.
Yes that's precisely what happens. JUnit runs the Executable inside a
try { ... } catch (Throwable t) {...} block. If the exception caught is of the specified type, then all is good. If not then it throws an AssertionError.

Testng test for specific exception with message

I am trying to test if a method throws a specific exception with it's message like this :
#DataProvider(name = "correctPairs")
public Object[][] expectedResultsTest() {
return new Object[][] {
{ null, new NullPointerException("Class cannot be null") },
};
}
#Test(dataProvider = "correctPairs")
public void ormDefinitionClassTest(final Class<?> value, final Throwable e) {
try {
DatabaseManager databaseManager = new DatabaseManager();
databaseManager.findAll(value);
} catch (Throwable ex) {
Assert.assertEquals(ex, e);
}
But it fails with the exception :
java.lang.AssertionError: expected [java.lang.NullPointerException: Class cannot be null] but found [java.lang.NullPointerException: Class cannot be null]
How can I make it check for equality of Exceptions ,by using #DataProvider?
I would use catchThrowable method from AssertJ fluent assertions. This way you can check the exception in test body, like
Throwable t = catchThrowable(() -> databaseManager.findAll(value));
assertThat(t).isInstanceOf(e);
EDIT: The problem here can be that you assume that these two exception objects are equal, but they are not equal objects. What you could do is to assert their type and message separately.
Your mistake lies in the equality test.
Assert.assertEquals(new NullPointerException(), new NullPointerException());
Returns false because the instances of the two exceptions are not the same.
You must compare the classes, then the messages.
class A implements ThrowingRunnable{
#Override
public void run() throws AuthenticationFailedException{
spy.processAuthenticationResponse(mockRequest, mockResponse, authenticationContext);
}
}
assertThrows(AuthenticationFailedException.class,new A());

How can I test exception in completable future?

I have been converting some code to be asynchronous. The original unit test used the annotation #Test(expected = MyExcpetion.class) but I don't think this will work because the exception I want to assert on is wrapped in java.util.concurrent.ExcutionException . I did try calling my future like this but my assertion is still failing and I don't love that I had to add in return null
myApiCall.get(123).exceptionally((ex) -> {
assertEquals(ex.getCause(),MyCustomException.class)
return null
}
I also tried this flavor but still not working
myApiCall.get(123).exceptionally((ex) -> {
assertThat(ex.getCause())
.isInstanceOF(MyException.class)
.hasMessage("expected message etc")
return null;
}
My API just throws exception if it can't find id. How should I be properly testing this? Can I use that original annotation in anyway?
my api call reaches out to db when run. In this test I am setting up my future to return an error so it doesn't actually try to communicate with anything. the code under test looks like this
public class myApiCall {
public completableFuture get(final String id){
return myService.getFromDB(id)
.thenApply(
//code here looks at result and if happy path then returns it after
//doing some transformation
//otherwise it throws exception
)
}
}
in the unit test I force myService.getFromDB(id) to return bad data so I can test exception and also keep this a unit test don't reach out to db etc.
Let's assume your API throws if called with 0:
public static CompletableFuture<Integer> apiCall(int id) {
return CompletableFuture.supplyAsync(() -> {
if (id == 0) throw new RuntimeException("Please not 0!!");
else return id;
});
}
You can test that it works as expected with the following code (I'm using TestNG but I suspect it won't be too difficult to translate into a JUnit test):
#Test public void test_ok() throws Exception {
CompletableFuture<Integer> result = apiCall(1);
assertEquals(result.get(), (Integer) 1);
}
#Test(expectedExceptions = ExecutionException.class,
expectedExceptionsMessageRegExp = ".*RuntimeException.*Please not 0!!")
public void test_ex() throws Throwable {
CompletableFuture<Integer> result = apiCall(0);
result.get();
}
Note that the second test uses the fact that the ExecutionException message will contain the original exception type and message and captures the expectation with a regex. If you can't do that with JUnit, you can call result.get() in a try/catch block and call throw e.getCause(); in the catch block. In other words, something like this:
#Test(expectedExceptions = RuntimeException.class,
expectedExceptionsMessageRegExp = "Please not 0!!")
public void test_ex() throws Throwable {
CompletableFuture<Integer> result = apiCall(0);
try {
result.get();
} catch (ExecutionException e) {
throw e.getCause();
}
}
You can try also alternative option:
import org.hamcrest.core.IsInstanceOf;
import org.junit.rules.ExpectedException;
public class Test() {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void myApiCallTest() {
thrown.expect(ExcutionException.class);
thrown.expectCause(IsInstanceOf.instanceOf(MyException.class));
thrown.expectMessage("the message you expected");
myApiCall.get("");
}
}
Assuming that:
public class myApiCall {
public completableFuture get(final String id) {
// ...
throw new ExcutionException(new MyException("the message you expected"))
}
}
Assume that you have a class and you want to test a method which returns a completable future:
public class A {
private final Api api;
public A(Api api) { this.api = api;}
public CompletableFuture<Void> execute(Integer input) {
final CompletableFuture<Void> future = api.execute(input)
.thenApplyAsync(result -> doSomething())
.exceptionally(ex -> doFailure());
return future;
}
}
To test the execution of "doSomething()" then you may use mockito and do the following:
// prepare test
final Api api = mock(Api.class)
final A a = new A(api);
when(api.execute(any(Integer.class)))
.thenReturn(CompletableFuture.completedFuture(null));
// execute
final CompletableFuture<Void> result = a.execute(input);
// validate
...
To test "doFailure" do the following:
when(api.execute(any(Integer.class))).thenAnswer(answer -> {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
});
// execute
final CompletableFuture<Void> result = a.execute(input);
// validate
assertTrue(result.isCompletedExceptionally());
that is easy thing doing in junit-4. Are you remember the #RunWith annotation? Yes, write your own TestRunner to intercept the exception before the junit expected exception processor is invoked, for example:
public class ConcurrentRunner extends BlockJUnit4ClassRunner {
public ConcurrentRunner(Class<?> klass) throws InitializationError {
super(klass);
}
#Override
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test,
Statement next) {
return super.possiblyExpectingExceptions(
method, test, throwingActualException(next)
);
}
private Statement throwingActualException(Statement next) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
next.evaluate();
} catch (ExecutionException | CompletionException source) {
throw theActualExceptionOf(source);
}
}
private Throwable theActualExceptionOf(Exception source) {
return source.getCause() != null ? source.getCause() : source;
}
};
}
}
just annotated with #RunWith(ConcurrentRunner.class) on the test, you needn't change your test code at all. for example:
#RunWith(ConcurrentRunner.class)
public class ConcurrentExpectedExceptionTest {
#Test(expected = IllegalArgumentException.class)
public void caughtTheActualException() throws Throwable {
myApiCall().join();
}
private CompletableFuture<Object> myApiCall() {
return CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
}
}

How to assert "N" exceptions have been thrown in this unit test?

I've got the following unit test, which works almost fine:
#Test(expectedExceptions = {IllegalArgumentException.class}, expectedExceptionsMessageRegExp =
PasswordValidator.INVALID_PASSWORD)
public void testInvalidPasswordsThrowException() throws Exception {
for (String invalidPassword: getInvalidPasswords()){
new LaxPasswordValidator(invalidPassword);
}
}
private String[] getInvalidPasswords() {
return new String[] {INVALID_PASSWORD_1, INVALID_PASSWORD_2, INVALID_PASSWORD_3, INVALID_PASSWORD_4,
INVALID_PASSWORD_5, INVALID_PASSWORD_6, INVALID_PASSWORD_7, INVALID_PASSWORD_8, INVALID_PASSWORD_9,
INVALID_PASSWORD_10};
}
As you can see, it is just asserting that new LaxPasswordValidator(invalidPassword) works as expected and throws an exception.
Problem: It is just taking INVALID_PASSWORD_1 into account, so it stops in the first iteration; it launches the exception but does not continue checking the other passwords. How can I make it test them all? Thanks
You must annotate the method as #DataProvider
#DataProvider(name = "invalid-passwords")
public Object[][] getInvalidPasswords() {
return new String[][]{
{"INVALID_PASSWORD_1"},
{"INVALID_PASSWORD_2"},
{"INVALID_PASSWORD_3"}
};
}
and annotate the #Test method to use this dataprovider for the parameters.
#Test(expectedExceptions = {IllegalArgumentException.class},
expectedExceptionsMessageRegExp = PasswordValidator.INVALID_PASSWORD,
dataProvider = "invalid-passwords")
public void testInvalidPasswords(String password) throws Exception {
new LaxPasswordValidator(password);
}

Can I test for multiple thrown exceptions in one test method?

I have a well specified interface and against that I write my JUnit tests:
public interface ShortMessageService {
/**
* Creates a message. A message is related to a topic
* Creates a date for the message
* #throws IllegalArgumentException, if the message is longer then 255 characters.
* #throws IllegalArgumentException, if the message ist shorter then 10 characters.
* #throws IllegalArgumentException, if the user doesn't exist
* #throws IllegalArgumentException, if the topic doesn't exist
* #throws NullPointerException, if one argument is null.
* #param userName
* #param message
* #return ID of the new created message
*/
Long createMessage(String userName, String message, String topic);
[...]
}
As you can see the implementation can throw various exceptions for which I have to write tests. My current approach is to write one test method for one possible exception specified in the interface like this:
public abstract class AbstractShortMessageServiceTest
{
String message;
String username;
String topic;
/**
* #return A new empty instance of an implementation of ShortMessageService.
*/
protected abstract ShortMessageService getNewShortMessageService();
private ShortMessageService messageService;
#Rule
public ExpectedException thrown = ExpectedException.none();
#Before
public void setUp() throws Exception
{
messageService = getNewShortMessageService();
message = "Test Message";
username = "TestUser";
topic = "TestTopic";
}
#Test
public void testCreateMessage()
{
assertEquals(new Long(1L), messageService.createMessage(username, message, topic));
}
#Test (expected = IllegalArgumentException.class)
public void testCreateMessageUserMissing() throws Exception
{
messageService.createMessage("", message, topic);
}
#Test (expected = IllegalArgumentException.class)
public void testCreateMessageTopicMissing() throws Exception
{
messageService.createMessage(username, message, "");
}
#Test (expected = IllegalArgumentException.class)
public void testCreateMessageTooLong() throws Exception
{
String message = "";
for (int i=0; i<255; i++) {
message += "a";
}
messageService.createMessage(username, message, topic);
}
#Test (expected = IllegalArgumentException.class)
public void testCreateMessageTooShort() throws Exception
{
messageService.createMessage(username, "", topic);
}
#Test (expected = NullPointerException.class)
public void testCreateMessageNull() throws Exception
{
messageService.createMessage(username, null, topic);
}
[...]
}
So for now I have to define a lot of test methods for that one method defined in the interface and that feels awkward. Can I combine all these exception tests in one test method or what is the best practice?
Unfortunately, the #Test annotation doesn't allow for catching multiple exception types (api reference http://junit.sourceforge.net/javadoc/org/junit/Test.html).
As a first option, I would advocate moving to TestNG. If your team won't allow that, there are few things you can do in JUnit.
Definitely use parameterized test cases so that you don't have to write one test function per test case (http://junit.sourceforge.net/javadoc/org/junit/runners/Parameterized.html). From here, there are a few options.
Group your test data by exception types.
#Test (expected = IllegalArgumentException.class)
public void testIllegalArgumentException(String username, String message, String topic) {}
#Test (expected = NullPointerException.class)
public void testNullPointerException(String username, String message, String topic) {}
Combine the exception types in your method signature. (This is what I recommend) Rough outline below ...
public void testException(String username, String message, String topic, Class<? extends Exception>[] expectedExceptionClasses) {
try {
// exception throwing code
} catch (Exception e) {
boolean found = false;
for (Class<?> expectedException : expectedExceptions) {
if (e instanceof expectedException) {
found = true;
}
}
if (found) {
return;
}
}
Assert.fail();
}
Put all of your tests under the umbrella Exception class (I have a feeling you don't want to do that.).
#Test (expected = Exception.class)
public void testException(String username, String message, String topic) {}
It might not be the best idea to combine them all in one method, since you wouldn't really know which test case threw which exception.
For example, if you had the line
messageService.createMessage(username, null, topic);
which should throw a NullPointerException, but instead it threw an IllegalArgumentException, you don't want that to count as a success.
If you'd like to test all the exceptions of that method in one test case, then a good alternative would be to wrap each exception test in a try..catch block.
For example, you could have
#Test
public void testCreateMessageExceptions() {
// test #1: a null message
try {
messageService.createMessage(username, null, topic);
// if it got this far, that's a problem!
fail();
} catch(NullPointerException e) {
// great, that's what it's meant to do! continue testing
} catch(Exception e) {
// if it threw the wrong type of exception, that's a problem!
fail();
}
// test #2: an empty user
try {
messageService.createMessage("", message, topic);
fail();
} catch(IllegalArgumentException e) {
} catch(Exception e) {
fail();
}
// ...
}

Categories

Resources