0

I have a project with some JUnit tests that have been working for a while. I finally got around to updating Android Studio (4.2.2 to 2022.2.1 (Flamingo)), and now my unit test configurations aren't recognized. Since I skipped a few versions, I'm not sure which one actually made the breaking change. Error running 'MyClassTests': Unknown run configuration type AndroidJUnit

I can right click on the unit test class and select run, but that runs very slowly. It took about 4 minutes despite the test results showing that the tests took just under 9 seconds. I saw that configuration was added in the Gradle section in the Run/Debug Configurations window. I made a new JUnit configuration since that is more similar to the original ones I made a while ago, and after some issues, I got one of my styles of tests to work, and it only took about 15 seconds to run. Why is the Gradle configuration so much slower, and is there anything I can do to speed it up? Assuming I can't fix that, I'm focusing on the JUnit configuration.

I can't figure out how to get my second style of tests to run successfully with the JUnit configuration.

For both styles, I'm using the answer from this stackoverflow post to mock a few Android classes like Log without third party libraries by creating the .java file with the fake in the appropriate folder under app/src/test/java/android. With my second style of tests, I haven't been able to figure out how to avoid getting java.lang.RuntimeException: Stub! for methods in those classes that are supposed to be mocked when I run the tests. I'm not completely sure how my fake versions end up replacing the original classes for running the tests, so I'm not sure what I need to do with the test configuration to ensure that still happens.

Style 1:

@RunWith(Enclosed.class)
public class MyClassTests1 {
    @RunWith(Parameterized.class)
    public static class FooTest {
        private final MyTestCase testCase;
        public FooTest(MyTestCase testCase) {
            this.testCase = testCase;
        }
        @Parameters(name = "{index}: {0}")
        public static Collection<Object[]> parameters() {
            // build parameterized test cases
        }
        @Test
        public void testA() {
            // perform the test
        }
        @Test
        public void testB() {
            // perform the test
        }
    }
    @RunWith(Parameterized.class)
    public static class BarTest {
        private final MyTestCase testCase;
        public BarTest(MyTestCase testCase) {
            this.testCase = testCase;
        }
        @Parameters(name = "{index}: {0}")
        public static Collection<Object[]> parameters() {
            // build parameterized test cases
        }
        @Test
        public void testC() {
            // perform the test
        }
    }
}

Style 2:

public class MyClassTests2 {
    @ParameterizedTest(name = "{index}: {0}")
    @MethodSource("foo_parameters")
    public void foo(final MyTestCase testCase) {
        // perform the test
    }
    private static Stream<MyTestCase> foo_parameters() {
        // build parameterized test cases
    }
    @ParameterizedTest(name = "{index}: {0}")
    @MethodSource("bar_parameters")
    public void bar(final MyTestCase testCase) {
        // perform the test
    }
    private static Stream<MyTestCase> bar_parameters() {
        // build parameterized test cases
    }
}

Original configuration for both styles from .idea/workspace.xml:

<configuration name="MyClassTests" type="AndroidJUnit" factoryName="Android JUnit" temporary="true" nameIsGenerated="true">
  <module name="MyProject.app" />
  <useClassPathOnly />
  <option name="PACKAGE_NAME" value="com.example.myproject" />
  <option name="MAIN_CLASS_NAME" value="com.example.myproject.MyClassTests" />
  <option name="METHOD_NAME" value="" />
  <option name="TEST_OBJECT" value="class" />
  <option name="PARAMETERS" value="" />
  <option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
  <option name="TEST_SEARCH_SCOPE">
    <value defaultName="wholeProject" />
  </option>
  <method v="2">
    <option name="Android.Gradle.BeforeRunTask" enabled="true" />
  </method>
</configuration>

Working configuration for my first style:

<configuration name="MyClassTests1" type="JUnit" factoryName="JUnit">
  <option name="MAIN_CLASS_NAME" value="" />
  <option name="METHOD_NAME" value="" />
  <option name="TEST_OBJECT" value="pattern" />
  <option name="TEST_SEARCH_SCOPE">
    <value defaultName="wholeProject" />
  </option>
  <patterns>
    <pattern testClass="com.example.myproject.MyClassTests1$*" />
  </patterns>
  <method v="2">
    <option name="Make" enabled="true" />
  </method>
</configuration>

One thing I noticed is that these versions of Android Studio handle the classpath differently. In 4.2.2, I could only select <no module>, MyProject, or MyProject.app, but now in 2022.2.1, I also have the options MyProject.app.androidTest, MyProject.app.main, and MyProject.app.unitTest. If I want to run tests from a specific class, I have to use MyProject.app.unitTest. If I select a different one, the class field shows an error like Class 'com.example.myproject.MyClassTests' not found in module 'MyProjectDirectory.app' or Module not specified. If I use MyProject.app.unitTest and specify the class for either style, I get the error java.lang.RuntimeException: Stub!, which is why I swapped to use the pattern for the first style. I couldn't figure out how to get a pattern to get around the error for the second style. The pattern solution for the first style shows the full com.example.myproject.MyClassTests1$FooTest name in the results, which is more verbose than it needs to be, but it's not a big deal.

I think it would make the most sense to use MyProject.app.unitTest for the codepath and run it for a specific test class. Is there something I need to do now to get it to use my fake class instead of the stubbed Android code?

ewittman
  • 11
  • 1

0 Answers0