Test cases in inner classes with JUnit
Asked Answered
S

6

89

I read about Structuring Unit Tests with having a test class per class and an inner class per method. Figured that seemed like a handy way to organize the tests, so I tried it in our Java project. However, the tests in the inner classes doesn't seem to be picked up at all.

I did it roughly like this:

public class DogTests
{
    public class BarkTests
    {
        @Test
        public void quietBark_IsAtLeastAudible() { }

        @Test
        public void loudBark_ScaresAveragePerson() { }
    }

    public class EatTests
    {
        @Test
        public void normalFood_IsEaten() { }

        @Test
        public void badFood_ThrowsFit() { }
    }
}

Does JUnit not support this, or am I just doing it wrong?

Skirr answered 6/1, 2012 at 13:9 Comment(2)
I think if you declare the inner classes static, it should work.Graner
See here: #4150990Flop
E
32
public class ServicesTest extends TestBase {

   public static class TestLogon{

       @Test
       public void testLogonRequest() throws Exception {
         //My Test Code
       }
   }
}

Making the inner class static works for me.

Eos answered 6/1, 2012 at 13:27 Comment(3)
You can still only run 1 class at a time.Boycott
@Sridhar-Sarnobat What are you talking about? It works in JUnit 4.12 at least. Can't bother to test older versions though.Periodontics
Also, if using Gradle, doing a ./gradlew clean seemed to work for me (I think)Administrate
K
108

You should annontate your class with @RunWith(Enclosed.class), and like others said, declare the inner classes as static:

@RunWith(Enclosed.class)
public class DogTests
  {
  public static class BarkTests
  {
    @Test
    public void quietBark_IsAtLeastAudible() { }

    @Test
    public void loudBark_ScaresAveragePerson() { }
  }

  public static class EatTests
  {
    @Test
    public void normalFood_IsEaten() { }

    @Test
    public void badFood_ThrowsFit() { }
  }
}
Krystynakshatriya answered 1/3, 2013 at 13:10 Comment(5)
You can still only run 1 class at a time.Boycott
Works for me. At least in Intellij I can either run all tests, only tests from one child class, or a single test. With code folding this is really nice!Fabio
Runs all tests for me using Eclipse Neon and JUnit 4.12. Eclipse also prompts to run the outer fixture or one of the nested fixtures. The only problem I have is, when run from Gradle, tests from the nested fixtures are run twice. GRADLE-2843 was opened for this issue in 2013, but it has since been abandoned.Ekaterinburg
also note inner class must be public.Abound
This appears to be outdated - see https://mcmap.net/q/236121/-test-cases-in-inner-classes-with-junitTheine
E
32
public class ServicesTest extends TestBase {

   public static class TestLogon{

       @Test
       public void testLogonRequest() throws Exception {
         //My Test Code
       }
   }
}

Making the inner class static works for me.

Eos answered 6/1, 2012 at 13:27 Comment(3)
You can still only run 1 class at a time.Boycott
@Sridhar-Sarnobat What are you talking about? It works in JUnit 4.12 at least. Can't bother to test older versions though.Periodontics
Also, if using Gradle, doing a ./gradlew clean seemed to work for me (I think)Administrate
H
27

In JUnit 5, you simply mark non-static inner classes as @Nested:

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class DogTests {
    @Nested
    public class BarkTests {
        @Test
        public void quietBark_IsAtLeastAudible() { }

        @Test
        public void loudBark_ScaresAveragePerson() { }
    }

    @Nested
    public class EatTests {
        @Test
        public void normalFood_IsEaten() { }

        @Test
        public void badFood_ThrowsFit() { }
    }
}
Humbug answered 10/11, 2017 at 0:48 Comment(1)
This is the most up-to-date answer as of 2022. It is also the best way to bypass the annoying fact that Spring's @TestPropertySource can only be applied to classes.Liliuokalani
R
15

I think some of the answers might be for older versions of JUnit. In JUnit 4 this worked for me:

    @RunWith(Suite.class)
    @Suite.SuiteClasses({ DogTests.BarkTests.class, DogTests.EatTests.class })
    public class DogTests
    {
        public static class BarkTests
        {
            @Test
            public void quietBark_IsAtLeastAudible() { }

            @Test
            public void loudBark_ScaresAveragePerson() { }
        }

        public static class EatTests
        {
            @Test
            public void normalFood_IsEaten() { }

            @Test
            public void badFood_ThrowsFit() { }
        }
    }
Ritornello answered 20/3, 2014 at 22:25 Comment(0)
S
10

I've had success with Nitor Creation's Nested Runner as well.

How to use Nitor Creation's Nested Runner

There is a post explaining it here:

Add this dependency:

<dependency>
    <groupId>com.nitorcreations</groupId>
    <artifactId>junit-runners</artifactId>
    <version>1.2</version>
    <scope>test</scope>
</dependency>

And a @RunWith to your test:

import com.nitorcreations.junit.runners.NestedRunner
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
  
@RunWith(NestedRunner.class)
public class RepositoryUserServiceTest {
           
    public class RegisterNewUserAccount {
     
        public class WhenUserUsesSocialSignIn {
             
            public class WhenUserAccountIsFoundWithEmailAddress {
                 
                @Test
                public void shouldThrowException() {
                     assertTrue(true);
                }
            }
         
        }
    }
}

PS: The example code has been taken and modified from the above blog post

Sonneteer answered 27/10, 2015 at 13:24 Comment(2)
I was looking for a way to make a specification-like thing appear in JUnit...PERFECT! Thanks!Glasswork
This works with Kotlin as well (use @RunWith(NestedRunner::class in that case).Catechu
E
0

I just ran across this posting (11 years later) regarding the testing of inner classes. An inner class can be trivially converted to equivalent static form only if the class should have been static in the first place. Static inner classes are not really inner classes because there is no enclosing this. They have exactly the same semantics (except for visibility restrictions) as top-level classes.

To test a "true" inner class [one that depends on its enclosing instance] you need to use the interface that the Java language provides for creating inner class instances outside the scope of the enclosing class. That interface includes an extra parameter in each constructor which is the enclosing instance. In this way, the Java compiler converts an inner class to a special top-level class with a mangled name (lots of $ signs) and augmented constructors. The same transformation can be performed at the source level. In principle, these transformed classes can be tested but it is a complex process because the tested program has transformed syntax and the test code must construct a (mock) object that serves as the enclosing instance.

Another way to test true inner classes is to write an executable method contract for each method consisting of an executable logical pre-condition and an executable logical post-condition. Then these executable contracts can be evaluated in the course of running a conventional top-level test that invokes the inner class methods.

In practice, I typically settle for the indirect testing of inner class methods in the course of top-level testing. Writing and testing executable contracts for all methods is a more rigorous, albeit significantly more expensive, alternative.

Eba answered 22/1, 2023 at 22:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.