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?