4

I am trying to write integration tests and want to make it in a best way regarding programming best practices, etiquette, etc.

First I had a question on how to reuse existing tests in another test class and I found the answer in topic How to reuse existing JUnit tests in another test class?

After moving the base test functionality and relevant member fields into the base class I found out that I need to make the member fields protected or I need to have their appropriate getters and setters in order I could use them in inherited test classes. Here is an example:

public abstract class BaseTests {

    private Class classObj;      // or protected without getter/setter?

    protected void somethingHelper() {
        // Test something
    }

    protected void somethingElseHelper() {
        // Test something for classObj
    }

    public Class getClassObj() {
        return classObj;
    }

    public void setClassObj(Class other) {
        classObj = other;
    }
}

public class TestClass1 extends BaseTests {
    @Test
    public void testSomething() {
        somethingHelper();
    }
}

public class TestClass2 extends BaseTests {
    @Test
    public void testSomethingAndSomethingElse() {
        somethingHelper();

        ClassObj obj = new ClassObj();
        // set obj member fields
        // and then test something else
        setClassObj(obj);   // or classObj = obj;?
        somethingElseHelper();
    }
}

My questions are:

  • Is it a good practice to have member fields in base test class and use them in inherited test classes?
  • If yes, what is better: protected member fields or getters/setters?
  • If no, is there another way to achieve what I wanted in example?
Community
  • 1
  • 1
Armine
  • 1,469
  • 2
  • 18
  • 35
  • 1
    Nothing terribly wrong with either option, the standard rules of field visibility apply to abstract test classes just like any other class. – dimo414 Jun 06 '16 at 17:17

4 Answers4

5

It is not necessarily bad practice to have getters, as long as you keep in mind that unit testing is about isolating behavior and testing specific units of work. Following that, you should make sure that your getters are always returning new instances of objects, instead of dishing out the same copy to all of your unit testing methods.

Sharing objects can cause a lot of issues in unit tests. Most test runners execute tests in parallel, and don't guarantee order execution. Your tests will most likely be modifying the objects, so sharing objects can break other tests in unpredictable ways.

I often create small factory methods for creating objects that I use in all my tests. Especially for the specific class that is being tested.

public ClassUnderTest GetNewClassUnderTest()
{
    return new ClassUnderTest("My favorite string", 1234);
}

As far as breaking test functionality into helper methods, I prefer not to do that. It makes test code difficult to scan over quickly. If it is something simple like one or two lines of code, I always include it inline in my tests. Anything more than that and I start to think about refactoring my tests into something more simple, if possible.

EDIT: I would also highly recommend reading The Art of Unit Testing. It's a great book that has a lot of great advice for building and structuring your unit tests.

Danny
  • 402
  • 5
  • 14
  • That isn't really a getter but more a factory. – 5gon12eder Jun 06 '16 at 14:53
  • I called it a factory method. – Danny Jun 06 '16 at 14:56
  • Thank you very much for the detailed answer. I also do not prefer breaking the test functionality into helper methods, but if I don't do that my test methods may grow to hundreds of lines due the the reason that my objects under test have too many fields which need to be set and the test also have some sub-test steps some of which may be used in other tests. And I try to follow the DRY principles as well :) – Armine Jun 07 '16 at 12:34
2

Jay Fields ( se-radio podcast ) points out there are somehow different rules when writing unit tests.

Of course you should still try to avoid code duplication. But the primary thing that determines whether a unit test is helpful ... is the amount of time that you need in order to understand a failing test.

In other words: ideally, any test method within your test classes or suites are independent of each other. You just read that one method; and you understand what it is doing; you look at the failure; and you found all you need.

Compare that to a solution where (complicated) coupling exists; for example because your concrete test method relies on a lot of things that come even from outside of that class; for example via inheritance.

But as others have said: this very much depends on you and your team. You need a lot of experience in order to create really helpful tests; and there are no simply rules that guarantee great results when blindly following them.

GhostCat
  • 127,190
  • 21
  • 146
  • 218
1

I would avoid state in your unit test altogether, if possible. Instead of messing with attributes, have the derived test override a factory function that produces whatever thing is needed for the test and have the generic test call that function.

If you cannot avoid attributes, think whether you can set them in the constructor. Then the derived test needs to invoke the super constructor with the appropriate values.

If that won't work either, using protected setters seems like the next solution to try. Or refactoring your tests such that one of the above method works.

5gon12eder
  • 21,864
  • 5
  • 40
  • 85
0

Is it good practice...

No see 2nd answer of referenced question.

If no, is there another way to achieve what I wanted in example?

Avoid inherited state and refactor the helper methods to a class with static helper methods. i.e.

  • CUnitTestHelper.createTestCustomer()
  • CUnitTestHelper.assertEqual(Customer customrer1, Customer customrer2) .

The priciple behind this is called Composition_over_inheritance

Community
  • 1
  • 1
k3b
  • 13,724
  • 6
  • 47
  • 81
  • At first, I wanted to use a helper (utility) class with static methods but it is also considered as bad practice regarding OOP. [OOP Alternative to Utility Classes.](http://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html) [Utility classes are evil](http://stackoverflow.com/questions/3340032/utility-classes-are-evil) – Armine Jun 07 '16 at 12:11