How can I mock a void method to throw an exception? - java

I have a structure like this:
public class CacheWrapper {
private Map<Object, Object> innerMap;
public CacheWrapper() {
//initialize the innerMap with an instance for an in-memory cache
//that works on external server
//current implementation is not relevant for the problem
innerMap = ...;
}
public void putInSharedMemory(Object key, Object value) {
innerMap.put(key, value);
}
public Object getFromSharedMemory(Object key) {
return innerMap.get(key);
}
}
And my client class (you could say it looks like this):
public class SomeClient {
//Logger here, for exception handling
Logger log = ...;
private CacheWrapper cacheWrapper;
//getter and setter for cacheWrapper...
public Entity getEntity(String param) {
Entity someEntity = null;
try {
try {
entity = cacheWrapper.getFromSharedMemory(param);
} catch (Exception e) {
//probably connection failure occurred here
log.warn("There was a problem when getting from in-memory " + param + " key.", e);
}
if (entity == null) {
entity = ...; //retrieve it from database
//store in in-memory cache
try {
cacheWrapper.put(param, entity);
} catch (Exception e) {
//probably connection failure occurred here
log.warn("There was a problem when putting in in-memory " + param + " key.", e);
}
}
} catch (Exception e) {
logger.error(".......", e);
}
return entity;
}
}
I'm creating unit tests for SomeClient#getEntity method and have to cover all scenarios. For instance, I need to cover the scenario where there are exceptions thrown by cacheWrapper. The approach I'm following is to create a mock for CacheWrapper class, make the methods on CacheWrapper class to throw a RuntimeException, set this mock in an instance of SomeClient and test Someclient#getEntity. The problem is when trying to mock putInSharedMemory method because is void. I have tried lot of ways to do this but none of them work. The project has dependencies for PowerMock and EasyMock.
Here are my attempts:
Using EasyMock.<Void>expect. This raised a compiler error.
Tried to stub CacheWrapper#putInSharedMemory. Didn't worked because raised an exception with this error message:
java.lang.AssertionError: Unexpected method call putInSharedMemory("foo", com.company.domain.Entity#609fc98)
Added Mockito dependency to the project to make use of the functionality of PowerMockito class. But this raised an exception because it doesn't integrate with EasyMock. This is the exception raised:
java.lang.ClassCastException: org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl cannot be cast to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
Here's the code for this unit test sample:
#Test
public void getEntityWithCacheWrapperException() {
CacheWrapper cacheWrapper = mockThrowsException();
SomeClient someClient = new SomeClient();
someClient.setCacheWrapper(cacheWrapper);
Entity entity = someClient.getEntity();
//here.....................^
//cacheWrapper.putInSharedMemory should throw an exception
//start asserting here...
}
//...
public CacheWrapper mockThrowsException() {
CacheWrapper cacheWrapper = PowerMock.createMock(CacheWrapper.class);
//mocking getFromSharedMemory method
//this works like a charm
EasyMock.expect(cacheWrapper.getFromSharedMemory(EasyMock.anyObject()))
.andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();
//mocking putInSharedMemory method
//the pieces of code here were not executed at the same time
//instead they were commented and choose one approach after another
//attempt 1: compiler exception: <Void> is not applicable for <void>
EasyMock.<Void>expect(cacheWrapper.putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject()))
.andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();
//attempt 2: stubbing the method
//exception when executing the test:
//Unexpected method call putInSharedMemory("foo", com.company.domain.Entity#609fc98)
Method method = PowerMock.method(CacheWrapper.class, "putInSharedMemory", Object.class, Object.class);
PowerMock.stub(method).toThrow(new RuntimeException("Exception on purpose."));
//attempt 3: added dependency to Mockito integrated to PowerMock
//bad idea: the mock created by PowerMock.createMock() belongs to EasyMock, not to Mockito
//so it breaks when performing the when method
//exception:
//java.lang.ClassCastException: org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl
//cannot be cast to org.powermock.api.mockito.internal.invocationcontrol.MockitoMethodInvocationControl
//at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:54)
PowerMockito.doThrow(new RuntimeException("Exception on purpose."))
.when(cacheWrapper).putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject());
PowerMock.replay(cacheWrapper);
return cacheWrapper;
}
I cannot change the implementation of CacheWrapper because it comes from a third party library. Also, I cannot use EasyMock#getLastCall because I'm performing the test on SomeClient#getEntity.
How can I overcome this?

Since none of your classes are final, you can use "pure mockito" without resorting to PowerMockito:
final CacheWrapper wrapper = Mockito.spy(new CacheWrapper());
Mockito.doThrow(something)
.when(wrapper).putInSharedMemory(Matchers.any(), Matchers.any());
Note that "method arguments" to a stub are in fact argument matchers; you can put specific values (if not "surrounded" by a specific method it will make a call to .equals()). So, you can guide the stub's behavior differently for different arguments.
Also, no need for any kind of .replay() with Mockito, which is very nice!
Finally, be aware that you can doCallRealMethod() as well. After that, it depends on your scenarios...
(note: last mockito version available on maven is 1.10.17 FWIW)

Are you using EasyMock or Mockito? Both are different frameworks.
PowerMockito is a superset (or more of a supplement) that can be used with both these frameworks. PowerMockito allows you to do things that Mockito or EasyMock don't.
Try this for stubbing void methods to throw exceptions:
EasyMock:
// First make the actual call to the void method.
cacheWrapper.putInSharedMemory("key", "value");
EasyMock.expectLastCall().andThrow(new RuntimeException());
Check:
http://easymock.org/api/org/easymock/internal/MocksControl.html#andVoid--
Getting EasyMock mock objects to throw Exceptions
Mockito:
// Create a CacheWrapper spy and stub its method to throw an exception.
// Syntax for stubbing a spy's method is different from stubbing a mock's method (check Mockito's docs).
CacheWrapper spyCw = spy(new CacheWrapper());
Mockito.doThrow(new RuntimeException())
.when(spyCw)
.putInSharedMemory(Matchers.any(), Matchers.any());
SomeClient sc = new SomeClient();
sc.setCacheWrapper(spyCw);
// This will call spyCw#putInSharedMemory that will throw an exception.
sc.getEntity("key");

Use expectLastCall, like:
cacheWrapper.putInSharedMemory(EasyMock.anyObject(), EasyMock.anyObject())
EasyMock.expectLastCall().andThrow(new RuntimeException("This is an intentional Exception")).anyTimes();

Related

Java unit test - exception not being thrown

Trying to write a test that will call my method, when that method makes a call to another method we will throw a custom exception i have made. Here i have simplified it all
2 functions
public MyJsonResponse hello() {
MyJsonResponse response = new MyJsonResponse();
response.setErrorMessage("1");
response.setStatus("some status");
response.setData("1");
response.setHttpResponse(200);
try{
hi();
return response;
}catch (MyServiceException e) {
response.setErrorMessage(e.getMessage());
response.setStatus("error creating");
response.setData("2");
response.setHttpResponse(e.getResponseStatus());
return response;
}
}
public String hi() throws MyServiceException{
LOG.error("Exception");
return "yea";
}
The test I have written is this
#Test
public void myTest() throws Exception {
given(service.hi()).willAnswer( invocation -> { throw new MyServiceException("abc msg",511); });
MyJsonResponse actual = service.hello();
Assert.assertNotNull(actual);
assertEquals(511, actual.getHttpResponse());
}
But unfortunately the result is as follows
java.lang.AssertionError:
Expected :511
Actual :200
Please, be sure that you are using a spy as you want to use the actual code for some methods of your mocked service and just stubbing specific methods of it. Please, see for instance this related SO question about the subject.
Also, consider modifying your test definition to use willThrow instead of willAnswer: as pointed out by #eis, you can still use the later, but the former is more straightforward.
Your code will look similar to this:
#Test
public void myTest() throws Exception {
MyService service = spy(MyService.class);
willThrow(new MyServiceException("abc msg",511))
.given(service)
.hi()
;
// As pointed out by #eis, you can still use willAnswer
// willAnswer(
// invocation -> { throw new MyServiceException("abc msg",511);}
// )
// .given(service)
// .hi()
// ;
MyJsonResponse actual = service.hello();
Assert.assertNotNull(actual);
assertEquals(511, actual.getHttpResponse());
}
regarding what you explain and what your code look like, I am not sure if I have well understood.
Thus, if you want that, your hi() : function throws an exception.
You have to make it first throws an exception. Take a look at code below!
public String hi() throws MyServiceException{
/*LOG.error("Exception");//No don't just log, throw a real exception as below*/
throw new MyServiceException("text here, if your constructor support it or nothing otherwise")
/*return "yea";//Nothing to return? we have just break the code by throwing the exception above*/
}
After that, please be very sure that your 'MyServiceException.getHttpResponse()' will really return 511
For this test to make sense, your hi() call should be done calling another service that you stub/mock in your test class. You're not doing that, so this approach won't work.
You wrote "the real method that hi represents does a lot", so it's about time you extract that to another service.

Mockito mocking a new instance call with parameters

For school purposes I am creating an application that's working with a stock API.
I am trying to write a test for a method that gets all the stock data of the last 10 years. Instead of actually getting all that data, I want to throw an exception.
The method I Want to test:
#Override
public List<StockData> getAllTeslaStockData() throws AlphaVantageException {
List<StockData> stockData;
AlphaVantageConnector apiConnector = new AlphaVantageConnector(APIKEY, TIMEOUT);
TimeSeries stockTimeSeries = new TimeSeries(apiConnector);
try {
Daily responseDaily = stockTimeSeries.daily("TSLA", OutputSize.FULL);
stockData = responseDaily.getStockData();
} catch (AlphaVantageException e) {
LOGGER.log(Level.SEVERE, "something went wrong: ", e);
throw e;
}
return stockData;
}
The stockTimeSeries.daily(....) call can throw the AlphaVantageException.
I've mocked the TimeSeries class like this:
TimeSeries stockTimeSeries = mock(TimeSeries.class);
In my test class I want to mock this call, and return an exception instead of actual data.
when(stockTimeSeries.daily("TSLA", OutputSize.FULL)).thenThrow(new AlphaVantageException("No stock data available"));
Regardless of how I am trying to mock this bit of code, it'll never throw the exception. It will always just execute the code, and return valid stock data, instead of throwing the exception like i've tried to do.
How can I mock this bit of code, so that itll throw the exception I am expecting for my tests.
The AlphaVantageConnector, TimeSeries and Daily classes are part of a library used to access the stock API, so I can't change these classes.
I am using JUnit 4.12 and Mockito to try and achieve this.
You can use thenThrow() method. Below is the example
#Test(expected = NullPointerException.class)
public void whenConfigNonVoidRetunMethodToThrowEx_thenExIsThrown() {
MyDictionary dictMock = mock(MyDictionary.class);
when(dictMock.getMeaning(anyString()))
.thenThrow(NullPointerException.class);
dictMock.getMeaning("word");
The TimeSeries object is created in the method itself, so you can't mock it - mocking is intended to mock members.
What you can do is to do something like
class YourClass {
private Supplier<TimeSeries> seriesCreator = () -> {
return new TimeSeries(new AlphaVantageConnector(APIKEY, TIMEOUT));
}
which you use to create the series in your method
#Override
public List<StockData> getAllTeslaStockData() throws AlphaVantageException {
TimeSeries stockTimeSeries = seriesCreator.get();
Now you can mock that Supplier.
#Mock Supplier<TimeSeries> seriesCreatorMock;
#InjectMocks MyClass sut;
and in your test
#Test(expected = AlphaVantageException.class)
void testException() {
when(seriesCreatorMock.get()).thenThrow(new AlphaVantageException());
sut.getAllTeslaStockData()
}
EDIT: as suggested by Angkur in the comments, the clean way would be to
class SeriesCreator implements Supplier<TimeSeries> {
public TimeSeries get() {
return new TimeSeries(new AlphaVantageConnector(APIKEY, TIMEOUT));
}
}
class YourClass {
private Supplier<TimeSeries> seriesCreator = new SeriesCreator();
// ...
The code in the main class is creating a new instance of TimeSeries which it will use every time this method is called, so the mocked TimeSeries object is not getting used at all.
TimeSeries stockTimeSeries = new TimeSeries(apiConnector); // --> This is not getting mocked
try {
Daily responseDaily = stockTimeSeries.daily("TSLA", OutputSize.FULL);
stockData = responseDaily.getStockData();
}
You should create another method in your class (or even a separate class if it better satisfies the SOLID principles) which returns you the TimeSeries object. Something like:-
<access modifier> TimeSeries getTimeSeries(...) {
}
and then this method should be mocked in the Junit, and when mocked, it should return the Mocked TimeSeries reference (which is created in TimeSeries stockTimeSeries = mock(TimeSeries.class); ). You would need to use .spy() on the main class (unless you are using a different class to create TimeSeries object) in order to be able to mock the specific method getTimeSeries() but not the others.
MainClass mainObject = Mockito.spy(new MainClass());
Mockito.when(mainObject.getTimeSeries()).thenReturn(stockTimeSeries);
Then, the method call stockTimeSeries.daily() will get actually mocked by your existing code :
when(stockTimeSeries.daily("TSLA", OutputSize.FULL)).thenThrow(new AlphaVantageException("No stock data available"));
NOTE: you should also consider using .anyString() style methods provided by Mockito API while mocking.

First stub is called when adding an additional stub

I have the following object which I want to test:
public class MyObject {
#Inject
Downloader downloader;
public List<String> readFiles(String[] fileNames) {
List<String> files = new LinkedList<>();
for (String fileName : fileNames) {
try {
files.add(downloader.download(fileName));
} catch (IOException e) {
files.add("NA");
}
}
return files;
}
}
This is my test:
#UseModules(mockTest.MyTestModule.class)
#RunWith(JukitoRunner.class)
public class mockTest {
#Inject Downloader downloader;
#Inject MyObject myObject;
private final String[] FILE_NAMES = new String[] {"fail", "fail", "testFile"};
private final List<String> EXPECTED_FILES = Arrays.asList("NA", "NA", "mockContent");
#Test
public void testException() throws IOException {
when(downloader.download(anyString()))
.thenThrow(new IOException());
when(downloader.download("testFile"))
.thenReturn("mockContent");
assertThat(myObject.readFiles(FILE_NAMES))
.isEqualTo(EXPECTED_FILES);
}
public static final class MyTestModule extends TestModule {
#Override
protected void configureTest() {
bindMock(Downloader.class).in(TestSingleton.class);
}
}
}
I am overwriting the anyString() matcher for a specific argument. I am stubbing the download() method so that it returns a value for a specific argument and otherwise throws an IOException which gets handled by MyObject.readFiles.
The weird thing here is that the second stub (downloader.download("testFile")) throws the IOException set in the first stub (downloader.download(anyString())). I have validated that by throwing a different exception in my first stub.
Can someone explain me why the exception is thrown when adding an additional stub? I thought that creating a stub does not call the method/other stubs.
The problem is that when you write
when(downloader.download("testFile")).thenReturn("mockContent");
the first thing to be called is downloader.download, which you've already stubbed to throw an exception.
The solution is to use the slightly more versatile stubbing syntax that Mockito provides. This syntax has the advantage that it doesn't call the actual method when stubbing.
doThrow(IOException.class).when(downloader).download(anyString());
doReturn("mock content").when(downloader).download("test file");
I have listed other advantages of this second syntax, in my answer here
I thought that creating a stub does not call the method/other stubs.
This assumption is wrong, because stubbing is calling the mocks methods. Your test methods are still plain java!
Since stubbing for anyString will overwrite stubbing for any specific string you will either have to write two tests or stub for two specific arguments:
when(downloader.download("fail")).thenThrow(new IOException());
when(downloader.download("testFile")).thenReturn("mockContent");
Mockito is a very sophisticated piece of code that tries its best so that you can write
when(downloader.download(anyString())).thenThrow(new IOException());
which means “when the downloaders mock download method is called with anyString argument thenThrow an IOException” (i.e. it can be read from left to right).
However, since the code is still plain java, the call sequence actually is:
String s1 = anyString(); // 1
String s2 = downloader.download(s1); // 2
when(s2).thenThrow(new IOException()); // 3
Behind the scenes, Mockito needs to do this:
register an ArgumentMatcher for any String argument
register a method call download on the downloader mock where the argument is defined by the previously registered ArgumentMatcher
register an action for the previously registered method call on a mock
If you now call
... downloader.download("testFile") ...
the downloader mock checks whether there is an action register for "testFile" (there is, since there is already an action for any String) and accordingly throws the IOException.
Your 2nd mock statement is getting overriden by the first mock statement (because both mock statements are passing a String argument). If you want to cover try as well as catch back through your mock test then write 2 different test cases.

How can I mock a void method and leave everything else the same using mockito?

I'm using Mockito and want to do a hopefully simple thing. How do I mock a void method for a particular class? I tried ...
CacheService cs = mock(CacheService.class);
when(cs.startCache()).then( PopulateCache.addTestEntriesToCache() );
But I'm getting the compile error
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile (default-testCompile) on project cme-productplus-web: Compilation failure: Compilation failure:
[ERROR] \Documents and Settings\E18538\workspace\cme-productplus-web\src\test\java\com\cme\clearing\product\server\PopulateCacheServiceImpl.java:[32,65] 'void' type not allowed here
[ERROR] \Documents and Settings\E18538\workspace\cme-productplus-web\src\test\java\com\cme\clearing\product\server\PopulateCacheServiceImpl.java:[32,20] 'void' type not allowed here
My intention is instead of calling the normal code of CacheService.startCache, I want to call my own method, "PopulateCache.addTestEntriesToCache()". How can I do this?
Edit: Per the response given, I tried editing my class where I implement the mock, but the mock method (the doAnswer, presumably) isn't getting called ...
public class PopulateCacheServiceImpl extends RemoteServiceServlet implements PopulateCacheService {
/**
*
*/
private static final long serialVersionUID = 1L;
public Boolean initCache() {
boolean ret = false;
try {
setupMockCache();
CacheService.getInstance().startCache();
ret = true;
} catch (Exception e) {
e.printStackTrace(System.err);
ret = false;
} // try
return ret;
} // initCache
private void setupMockCache() {
CacheService cs = mock(CacheService.class);
try {
doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Throwable {
PopulateCache.addTestEntriesToCache();
return null;
}
}).when(cs).startCache();
} catch (SQLException e) {
e.printStackTrace();
}
} // setupMockCache
}
Thanks, - Dave
You are making a mock for the CacheService, but you are still not returning it and using it anywhere. Instead, you are calling the real static CacheService.instance() method which will not return your mock. Make you setupMockCache() return the CacheService and use it directly rather than going through the instance() method.
Also in the question title/summary, you said "leave everything else the same". If you mean you want the rest of CacheService to behave the same as it normaly would, then perhaps you want a partial mock, which you can do with Mockito's spy() instead of mock().
Put the call to your cache in the anwser-method of this http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#12
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Throwable {
PopulateCache.addTestEntriesToCache()
return null;
}
}).when(cs).startCache();
Of course it doesn't work : in setupMockCache you are creating a the cache mock CacheService cs = mock(CacheService.class); on which you define the stub. But the cs instance is never passed.
And in initCache you are calling the setup method, but you don't get the CacheService instance, right after you wrote this statement CacheService.getInstance().startCache(); that will certainly create a real CacheService instance and fo course it won't use the mocked instance.
I don't know what you want to do, this seems weird and wrong to mock partially a Cache in your production code! If I were you I would create my own set of classes that will return your custom cache backed by an inherited CacheService class if necessary (this class will explicitly overide the startCache method).
Hope that helps!

Mark unit test as an expected failure in JUnit

How can I mark a test as an expected failure in JUnit 4?
In this case I want to continue to run this test until something is patched upstream. Ignoring the test goes a little too far, as then I might forget about it. I may be able to add an #expected annotation and catch the exception thrown by assertThat, but that also seems to lie about the expected behavior.
Here's what my current test looks like:
#Test
public void unmarshalledDocumentHasExpectedValue()
{
doc = unmarshaller.unmarshal(getResourceAsStream("mydoc.xml"));
final ST title = doc.getTitle();
assertThat(doc.getTitle().toStringContent(), equalTo("Expected"));
}
That assert should succeed, but because of an upstream bug it doesn't. Yet, that test is correct; it should succeed. Virtually all the alternatives that I've found are misleading. Right now I think #Ignore("This test should pass once fixed upstream") is my best bet, but I still have to remember to come back to it. I'd prefer that the test run.
In Python I can use the expectedFailure decorator:
class ExpectedFailureTestCase(unittest.TestCase):
#unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
With Qt's QTestLib in C++, you can use QEXPECT_FAIL:
QEXPECT_FAIL("", "Will be fixed next version", Continue);
QCOMPARE(i, 42);
In both cases above, the unit test runs which is what I'm hoping to have happen. Am I missing something in JUnit?
I'm not quite getting the specifics of your scenario, but here's how I generally test for expected failure:
The slick new way:
#Test(expected=NullPointerException.class)
public void expectedFailure() {
Object o = null;
o.toString();
}
for older versions of JUnit:
public void testExpectedFailure() {
try {
Object o = null;
o.toString();
fail("shouldn't get here");
}
catch (NullPointerException e) {
// expected
}
}
If you have a bunch of things that you want to ensure throw an exception, you may also want to use this second technique inside a loop rather than creating a separate test method for each case. If you were just to loop through a bunch of cases in a single method using expected, the first one to throw an exception would end the test, and the subsequent cases wouldn't get checked.
What about explicitly expecting an AssertionError?
#Test(expected = AssertionError.class)
public void unmarshalledDocumentHasExpectedValue() {
// ...
}
If you're reasonably confident that only the JUnit machinery within the test would raise AssertionError, this seems as self-documenting as anything.
You'd still run the risk of forgetting about such a test. I wouldn't let such tests into version control for long, if ever.
I'm assuming here that you want the test to pass if your assert fails, but if the assert succeeds, then the test should pass as well.
The easiest way to do this is to use a TestRule. TestRule gives the opportunity to execute code before and after a test method is run. Here is an example:
public class ExpectedFailureTest {
public class ExpectedFailure implements TestRule {
public Statement apply(Statement base, Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (Throwable e) {
if (description.getAnnotation(Deprecated.class) != null) {
// you can do whatever you like here.
System.err.println("test failed, but that's ok:");
} else {
throw e;
}
}
}
};
}
}
#Rule public ExpectedFailure expectedFailure = new ExpectedFailure();
// actually fails, but we catch the exception and make the test pass.
#Deprecated
#Test public void testExpectedFailure() {
Object o = null;
o.equals("foo");
}
// fails
#Test public void testExpectedFailure2() {
Object o = null;
o.equals("foo");
}
}
First, note that the first method is marked as #Deprecated. I'm using this as a marker for the method for which I want to ignore any assertion failures. You can do whatever you like to identify the methods, this is just an example.
Next, in the ExpectedFailure#apply(), when I do the base.evaluate(), I'm catching any Throwable (which includes AssertionError) and if the method is marked with the annotation #Deprecated, I ignore the error. You can perform whatever logic you like to decide whether you should ignore the error or not, based on version number, some text, etc. You can also pass a dynamically determined flag into ExpectedFailure to allow it to fail for certain version numbers:
public void unmarshalledDocumentHasExpectedValue() {
doc = unmarshaller.unmarshal(getResourceAsStream("mydoc.xml"));
expectedFailure.setExpectedFailure(doc.getVersionNumber() < 3000);
final ST title = doc.getTitle();
assertThat(doc.getTitle().toStringContent(), equalTo("Expected"));
}
For further examples, see ExternalResource, and ExpectedException
Ignoring an expected failure test rather than passing it
If you want to mark you tests as Ignored rather than Success, it becomes a bit more complex, because tests are ignored before they are executed, so you have to retrospectively mark a test as ignored, which would involve constructing your own Runner. To give you a start, see my answer to How to define JUnit method rule in a suite?. Or ask another question.
One option is mark the test as #Ignore and put text in there that is a bug perhaps and awaiting a fix. That way it won't run. It will then become skipped. You could also make use of the extensions to suit your need in a potentially different way.
I've taken Matthew's answer a step further and actually implemented an #Optional annotation you could use instead of the #Deprecated marker annotation he mentions in his answer. Although simple, I'll share the code with you, maybe it's of help for someone:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Optional {
/**
* Specify a Throwable, to cause a test method to succeed even if an exception
* of the specified class is thrown by the method.
*/
Class<? extends Throwable>[] exception();
}
With a simple alteration of Matt's ExpectedFailure class:
public class ExpectedFailure implements TestRule {
#Override
public Statement apply(final Statement base, final Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (Throwable e) {
// check for certain exception types
Optional annon = description.getAnnotation(Optional.class);
if (annon != null && ArrayUtils.contains(annon.exception(), e.getClass())) {
// ok
} else {
throw e;
}
}
}
};
}
}
You can now annotate your test method with #Optional and it will not fail, even if the given type of exception is raised (provide one or more types you would like the test method to pass):
public class ExpectedFailureTest {
#Rule public ExpectedFailure expectedFailure = new ExpectedFailure();
// actually fails, but we catch the exception and make the test pass.
#Optional(exception = NullPointerException.class)
#Test public void testExpectedFailure() {
Object o = null;
o.equals("foo");
}
}
[UPDATE]
You could also rewrite your tests using JUnit's org.junit.Assume instead of the tradtional org.junit.Assert, if you want your tests to pass even if the assumption does not hold.
From Assume's JavaDoc:
A set of methods useful for stating assumptions about the conditions in which a test is meaningful.A failed assumption does not mean the code is broken, but that the test provides no useful information. The default JUnit runner treats tests with failing assumptions as ignored.
Assume is available since JUnit 4.4
Use mocked upstream class if possible. Stub it with correct result. Optionally, replace mock with real object after bug is fixed.

Categories