问题
Edit: Not JUnit 4 available at this time.
Hi there,
I have a question about "smart" exception testing with JUnit. At this time, I do it like this:
public void testGet() {
SoundFileManager sfm = new SoundFileManager();
// Test adding a sound file and then getting it by id and name.
try {
SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav");
SoundFile sf = sfm.getSoundfile(addedFile.getID());
assertTrue(sf!=null);
System.out.println(sf.toString());
sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav");
assertTrue(sf!=null);
System.out.println(sf.toString());
} catch (RapsManagerException e) {
System.out.println(e.getMessage());
}
// Test get with invalid id.
try {
sfm.getSoundfile(-100);
fail("Should have raised a RapsManagerException");
} catch (RapsManagerException e) {
System.out.println(e.getMessage());
}
// Test get by name with invalid name
try {
sfm.getSoundfileByName(new String());
fail("Should have raised a RapsManagerException");
} catch (RapsManagerException e) {
System.out.println(e.getMessage());
}
}
As you can see, I need one try/catch block for each function that is supposed to throw an exception. It seems not to be a good way to do this - or is there no possibility to reduce the use of try/catch?
回答1:
I suggest that you need to break up testGet
into multiple separate tests. The individual try/catch blocks seem to be pretty independent of each other. You may also want to extract the common initialization logic into its own setup method.
Once you have that, you can use JUnit4's exception annotation support, something like this:
public class MyTest {
private SoundManager sfm;
@Before
public void setup() {
sfm = new SoundFileManager();
}
@Test
public void getByIdAndName() {
// Test adding a sound file and then getting it by id and name.
SoundFile addedFile =
sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav");
SoundFile sf = sfm.getSoundfile(addedFile.getID());
assertTrue(sf!=null);
System.out.println(sf.toString());
sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav");
assertTrue(sf!=null);
System.out.println(sf.toString());
}
@Test(expected=RapsManagerException.class)
public void getByInvalidId() {
// Test get with invalid id.
sfm.getSoundfile(-100);
}
@Test(expected=RapsManagerException.class)
public void getByInvalidName() {
// Test get with invalid id.
sfm.getSoundfileByName(new String());
}
}
回答2:
If you have an expected exception and you can't use an annotation to trap it, you need to catch it and assert that you've got what you expected. For example:
Throwable caught = null;
try {
somethingThatThrows();
} catch (Throwable t) {
caught = t;
}
assertNotNull(caught);
assertSame(FooException.class, caught.getClass());
If you can use an annotation instead, do that as it's much clearer. But that's not always possible (e.g., because you're testing a sequence of methods or because you're using JUnit 3).
回答3:
With JUnit 4, you can use annotations instead. However, you should separate your test into 3 distinct methods for this to work cleanly. Note that IMHO catching an exception in the first scenario should be a failure, so I modified the catch
block accordingly.
public void testGet() {
SoundFileManager sfm = new SoundFileManager();
// Test adding a sound file and then getting it by id and name.
try {
SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav");
SoundFile sf = sfm.getSoundfile(addedFile.getID());
assertTrue(sf!=null);
System.out.println(sf.toString());
sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav");
assertTrue(sf!=null);
System.out.println(sf.toString());
} catch (RapsManagerException e) {
fail(e.getMessage());
}
}
@Test(expected=RapsManagerException.class)
public void testGetWithInvalidId() {
SoundFileManager sfm = new SoundFileManager();
sfm.getSoundfile(-100);
}
@Test(expected=RapsManagerException.class)
public void testGetWithInvalidName() {
SoundFileManager sfm = new SoundFileManager();
sfm.getSoundfileByName(new String());
}
回答4:
The most concise syntax is provided by catch-exception:
public void testGet() {
SoundFileManager sfm = new SoundFileManager();
... // setup sound file manager
verifyException(sfm, RapsManagerException.class)
.getSoundfile(-100);
verifyException(sfm, RapsManagerException.class)
.getSoundfileByName(new String());
}
回答5:
In Java 8, you can use lambda expressions to get tighter control over when the exception is thrown. If you use the annotations method then you're only asserting that the exception is thrown somewhere in the test method. If you're executing more than one line of code in the test then you risk your test passing when it should fail. Java 8 solution is something like this.
static void <T extends Exception> expectException(Class<T> type, Runnable runnable) {
try {
runnable.run()
} catch (Exception ex) {
assertTrue(ex.getClass().equals(type));
return;
}
assertTrue(false);
}
Usage:
@Test
public void test()
MyClass foo = new MyClass();
// other setup code here ....
expectException(MyException.class, () -> foo.bar());
}
来源:https://stackoverflow.com/questions/3083161/junit-exception-testing