Laurent Meyer
Laurent Meyer

Reputation: 2881

Inheritance of TestCases on Android

I was wondering if it was good practice to subclass the test cases on Android. I mean, I need to test a lot of Parcelable objects and I could create a class like GenerericParcelableAndroidTestCase to test all these objects.

I also have a problem implementing it, I have something like this:

public class GenericParcelableTest extends AndroidTestCase {

    private Parcelable p = null;

    GenericParcelableTest(Parcelable p) {
        this.p = p;
    }

    public void testDescribeContents() throws Exception {
        assertEquals(0, p.describeContents());

    }
}

And that:

public class AttachmentTest extends GenericParcelableTest {

    public AttachmentTest() {
        super(new Attachment());
    }
}

Attachment implements Parcelable of course.

It returns me this error:

junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()

I mean, I know that I created no empty constructor but why would I need one?

And generally, is there some known issues with this approach? If not why is there very few article on this topic on the internet (and actually some say even that it's not a good idea).

Upvotes: 0

Views: 1039

Answers (2)

BrantApps
BrantApps

Reputation: 6472

I have this conversation quite often when introducing new team members to unit testing. The way I explain it is by stating that your tests are first class citizens of your code base (no pun intended), they are susceptible to the same technical debt as any other part of your code base and have equivalent (maybe more?!) importance as that of the runtime code.

With this mindset, the questions begins to answer itself; if it makes sense from an OO perspective to use inheritance (i.e. your subclass is a insert name of test superclass) then subclass away. However, like any abuse of inheritance ever, be careful...the minute you add a test case that doesn't rely upon that superclass behaviour you may have a code smell.

In this scenario, it's likely (perhaps 90% of the time?) it is a separation of concern issue within the code being placed under test, i.e. the "unit" under test isn't actually (one) unit but has combinatorial behaviour. Refactoring that code to do one thing would be a good way of allowing your super-class test case to live on. However, watch this super class test case like a hawk...the minute you see booleans being added to signatures to "allow that similar but not the same" test case to run under your once unpolluted super class then you have a problem, a tech debt problem that is no different to your runtime code.

At last check AndroidTestCase depends on an Activity context so it's likely best described as an integration test which tend to regularly have boilerplate super-class test behaviour. In this case, try to narrow the focus of your superclass to the use case under test...i.e. extends LoginUseCase or extends LoginScenario to better "bucket" those subclasses in the first instance. This will help guide would be extenders as to whether they should be using it for their non-login scenario. Hopefully, conversation will ensue and tech debt accumulation be avoided!

Regarding your error, in JUnit3 do what @Allen recommends, if moving to JUnit4 with something like Robolectric then explore using Rules as well as @BeforeClass.

Personal note

I have only felt the need to write test super classes for pseudo-unit tests that mock an API end point (akin to MockWebServer if you are familiar with that product) and DAO integration tests whereby an in-memory db is started and torn down over the lifecycle of each test (warning - slow (but useful) tests!)

Upvotes: 1

Allen Hair
Allen Hair

Reputation: 1050

junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()

You get this error because JUnit needs to be able to construct an instance of your test class. It only knows how to do this using no-arg, or single string constructors.

Instead of performing initialization in your constructor, you should put it in the setUp() method. This will let you use the default constructor while still initializing the object before the test method is called.

Upvotes: 1

Related Questions