开发者

Dynamically adding JUnit tests to a test class

开发者 https://www.devze.com 2023-03-30 14:54 出处:网络
I find myself writing lots and lots of boiler plate tests these days and I want to optimize away a lot of these basic tests in a clean way that can be added to all the current test classes without a l

I find myself writing lots and lots of boiler plate tests these days and I want to optimize away a lot of these basic tests in a clean way that can be added to all the current test classes without a lot of hassle.

Here is a basic test class:

class MyClassTest {

    @Test
    public void doesWhatItDoes() {
        assertEquals("foo",new MyClass("bar").get());
    }

}

Lets say if MyClass implements Serializable, then it stands to reason we want to ensure that it really is serializable. So I built a class which you can extend which contains a battery of standard tests which will be run along side the other tests.

My problem is that if MyClass does NOT implement Serializable for instance, we still have a serialization test in the class. We can make it just succeed for non-serializable classes but it still sticks around in the test list and once this class starts to build it will get more and more cluttered.

What I want to do is find a way to dynamically add those tests which are relevant to already existing test classes where appropriate. I know some of this can be done with a TestSuit but then you have to maintain two test classes per class and that will quickly become a hassle.

If anyone knows of a way to do it which doesn't require an eclipse plug-in or something like that, then I'd be forever grateful.

EDIT: Added a brief sample of what I described above;

class MyClassTest extend AutoTest<MyClass> {

    public MyClassTest() {
        super(MyClass.class);
    }

    @Test
    public void doesWhatItDoes() {
        assertEquals("foo",new MyClass("bar").get());
    }

}

public abstract class AutoTest<T> {
    private final Class<T> clazz;
    protected AutoTest(Clazz<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Test
    public void serializes() {
        if (Arrays.asList(clazz.getInterfaces()).contains(Serializable.cl开发者_如何学Cass)) {
        /* Serialize and deserialize and check equals, hashcode and other things... */
        }
    }
}


Two ideas.

Idea 1: Use Assume

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.

@Test
public void serializes() {
    assumeTrue(Serializable.class.isAssignableFrom(clazz));
    /* Serialize and deserialize and check equals, hashcode and other things... */
}

Idea 2: implement your own test runner.

Have a look at @RunWith and Runner at http://junit.sourceforge.net/javadoc/


Most pragmatic solution within existing capabilities of JUnit is to have a single annotated test:

@Test
void followsStandardJavaLibraryProtocols() {
    if (implementsInterface(Serializable.class) {
         testSerialisableInterface       

...

Breaks various abstract principles of TDD, but works, with no unnecessary cleverness.

Perhaps, instead of a flat list of test cases, Junit could be extended to have more straightforward support for this kind of heirarchical test with subtests. Something like a @Subtest annotation that identified a test not to be invoked directly, instead adding a node to the result tree when it was, and with what arguments.


Your approach seems like a valid one to me. I don't have a problem with it.

I do this slightly differently. I would create another single test which tests all of your Serializable classes:

public class SerializablesTest {
    @Test
    public void serializes() {
        testSerializable(MyClass.class);
        testSerializable(MyClass2.class);
    }

    private testSerializable(Class clazz) {
        // do the real test here
        /* Serialize and deserialize and check equals, hashcode and other things... */
    }
}

What does this give you? For me, explicitness. I know that I am testing class MyClass for serializability. There isn't any magic involved. You don't need to pollute your other tests.

If you really need to test all your classes which implement Serializable, you can find all of your classes using reflection.

I use this approach a lot, using reflection to build objects. For instance, I can test that all fields are persisted to & reread from a database correctly. I use this sort of thing all of the time.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号