36

I just discovered when creating some CRUD tests that you can't set data in one test and have it read in another test (data is set back to its initialization between each test).

All I'm trying to do is (C)reate an object with one test, and (R)ead it with the next. Does JUnit have a way to do this, or is it ideologically coded such that tests are not allowed to depend on each other?

skaffman
  • 398,947
  • 96
  • 818
  • 769
orbfish
  • 7,381
  • 14
  • 58
  • 75
  • [Here is a solution i came up with and an explanation of the drawbacks of using static variables][1] [1]: http://stackoverflow.com/questions/17885221/how-to-save-non-static-properties-state-between-junit-test-methods-answer – Jacob Ko Jul 26 '13 at 16:19
  • 1
    @JacobKo your link leads to page not found – OrwellHindenberg Mar 02 '15 at 18:31

5 Answers5

38

Well, for unit tests your aim should be to test the smallest isolated piece of code, usually method by method. So testCreate() is a test case and testRead() is another. However, there is nothing that stops you from creating a testCreateAndRead() to test the two functions together. But then if the test fails, which code unit does the test fail at? You don't know. Those kind of tests are more like integration test, which should be treated differently.

If you really want to do it, you can create a static class variable to store the object created by testCreate(), then use it in testRead().

As I have no idea what version of Junit you talking about, I just pick up the ancient one Junit 3.8:

Utterly ugly but works:

public class Test extends TestCase{

    static String stuff;

    public void testCreate(){
        stuff = "abc";
    }

    public void testRead(){
        assertEquals(stuff, "abc");
    }
}
Christian
  • 27,509
  • 17
  • 111
  • 155
Jason Wang
  • 629
  • 1
  • 8
  • 11
  • It *is* an integration test - duh, CRUD means accessing the database. – orbfish Jul 01 '10 at 15:11
  • 1
    By the way, good idea about the static variable, if it works. I'll have to try it. – orbfish Jul 01 '10 at 15:12
  • 9
    Can you guarantee this works ? Have you defined in what order JUnit would reliably execute these tests ? – Brian Agnew Nov 06 '13 at 17:20
  • @orbfish this is 6 years late but: JUnit is a **unit** testing framework, not an **integration** testing one – obataku Aug 19 '16 at 18:43
  • 3
    @oldrinb....not anymore...since its been adopted by every testing tool out there...they have added multiple features that now make it both a unit AND integration testing tool – Tim Boland Jul 13 '17 at 19:02
  • 1
    @BrianAgnew's concern is a valid one and is now [addressed](https://junit.org/junit5/docs/snapshot/user-guide/#writing-tests-test-execution-order) in recent versions of JUnit. – nurettin Jan 31 '19 at 11:59
  • 1. Setting static from method is not recommended, gives this bug warning "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD". 2. To make sure tests run in order, use @Order(1), @Order(2) tag on tests – Yash Jun 15 '23 at 16:43
12

JUnit promotes independent tests. One option would be to put the two logical tests into one @Test method.

TestNG was partly created to allow these kinds of dependencies among tests. It enforces local declarations of test dependencies -- it runs tests in a valid order, and does not run tests that depend on a failed test. See http://testng.org/doc/documentation-main.html#dependent-methods for examples.

Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
  • So it *is* ideology! I was afraid of that. I did think, come to think of it, that there were some new annotations having to do with intertest dependency, but maybe I was reading about TestNG. – orbfish Jul 01 '10 at 15:01
  • It really is ideal for unit tests to be independent in both state and order. JUnit supports the ideal. TestNG supports both the ideal and pragmatic exceptions. Cedric Beust, the author of TestNG, discusses the issues in more detail in the sources below. He confirmed the intent of JUnit with Beck and Gamma, and found shortcomings with working around the JUnit approach with static members. * Beust's 2004 blog post http://beust.com/weblog/2004/02/08/junit-pain/ * The first several pages of Beust's book "Next Generation Java Testing: TestNG and Advanced Concepts," Addison-Wesley, 2008. – Andy Thomas Jul 01 '10 at 22:22
  • I agree with all that, for unit tests. But CRUD tests are database access and therefore not unit tests. It's a shame that JUnit, which is so flexible and pervasive, should be limited in any way that restricts it to unit tests only. – orbfish Jul 02 '10 at 17:10
  • Good point. In contrast, TestNG allows you to tag tests with a test category like "database" or "integration" via the 'groups' attribute; provides @BeforeGroup/@AfterGroup setup/teardown methods; and supports running or excluding-from-running a set of groups. More detail on Beust's slides 6-11 here: http://qconsf.com/sf2007/file?path=/QConSF2007/slides/public/CedricBeust_TestNG.pdf. – Andy Thomas Jul 02 '10 at 18:30
  • 1
    To make true CRUDy unit tests one would have to setup test state (database) and tear-it-down afterwords. Unfortunately this is very often complicated and error-prone which increases test development overhead while also increasing test failure noise. An example of a low error-prone approach to setting-up state would be running an SQL script whereas a high error-prone approach would be to use the same methods that are being tested. Unfortunately, the former is almost always impractical or even impossible depending on the project, so for the latter, test dependency seems to address this issue. – Coder Guy Dec 19 '12 at 02:02
  • it doesn't seem right to add state to a unit test. There might be unintended consequences.The order needs to be preserved and the second one cannot be executed by itself – CCC Jul 22 '20 at 20:44
7

JUnit is independent test. But, If you have no ways, you can use "static" instance to store it.

static String storage;
@Test
public void method1() {
    storage = "Hello"
}

@Test
public void method2() {
    Assert.assertThat(something, is(storage));
}
Long Nguyen
  • 9,898
  • 5
  • 53
  • 52
3

How much processing time do these tests take? If not a lot, then why sweat it. Sure you will create some object unnecessarily, but how much does this cost you?

@Test
void testCreateObject() {
    Object obj = unit.createObject();
}

@Test
void testReadObject() {
    Object obj = null;
    try {
        obj = unit.createObject(); // this duplicates tests aleady done
    } catch (Exception cause) {
        assumeNoException(cause);
    }
    unit.readObject(obj);
}
Enigo
  • 3,685
  • 5
  • 29
  • 54
emory
  • 10,725
  • 2
  • 30
  • 58
  • Good point. It's more that I have to think up test data to ensure uniqueness in the 2 createObject()s, but this may be the way to go. – orbfish Jul 01 '10 at 15:10
  • 1
    I guess part of me resists writing the same "create" code twice. I'm over-DRY, methinks. – orbfish Jul 01 '10 at 15:12
  • @orbfish This is as DRY as you can get without introducing dependencies between tests. @emory the call to `unit.readObject` should be executed within the `try` block. You rely on the contents of the `catch` clause to avoid a `NullPointerException` or something like that. – MauganRa Nov 28 '16 at 10:49
2

in this basic example, the variable is changed in the test A, and can be used in the test B

public class BasicTest extends ActivityInstrumentationTestCase2 {
    public BasicTest() throws ClassNotFoundException {
        super(TARGET_PACKAGE_ID, launcherActivityClass);        
    }

    public static class MyClass {    
        public static String myvar = null;              
        public void set(String s) {
            myvar = s;
        }               
        public String get() {
            return myvar;
        }
    }

    private MyClass sharedVar;

    @Override
    protected void setUp() throws Exception {
        sharedVar = new MyClass();
    }

    public void test_A() {
        Log.d(S,"run A");
        sharedVar.set("blah");
    }

    public void test_B() {
        Log.d(S,"run B");       
        Log.i(S,"sharedVar is: " + sharedVar.get());        
    }

}

output result is:

run A

run B

sharedVar is: blah

Franck
  • 512
  • 1
  • 9
  • 16
  • 1
    Your example doesn't contain a '@Test annotation'. I tried your code with the addition of '@Test' and didn't have luck. Have you tried this? I was under the impression that a JUnit tests must contain at least one such annotation. – OrwellHindenberg Mar 02 '15 at 16:51