Starting a test with ActivityScenarioRule - AndroidStudio - java

I'm trying to set up some basic instrumented tests which I am running with AndroidStudio on my physical device.
The test is fairly straightforward:
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
private ActivityScenarioRule<MyActivity> scenarioRule = new ActivityScenarioRule<>(MyActivity.class);
#Test
public void onFoo() {
ActivityScenario scenario = scenarioRule.getScenario();
}
}
When it runs, this happens:
java.lang.NullPointerException: throw with null exception
at androidx.test.internal.util.Checks.checkNotNull(Checks.java:34)
at androidx.test.ext.junit.rules.ActivityScenarioRule.getScenario(ActivityScenarioRule.java:118)
at com.me.sample.activities.MyActivityTest.onFoo(MyActivityTest.java:...)
According to the documents, NullPointerException is thrown if you use getScenario() when no test is running. I am running these tests with the AndroidStudio UI and the tests are appearing in the test running window, so it doesn't make sense to me that it's throwing that exception.
Do I need to add a before() method to set up the scenarioRule? Is there some setting I'm missing? Or could this be caused by a dependency or something in MyActivity?

I was missing annotations. This is the version which works:
#RunWith(AndroidJUnit4.class)
#SmallTest
public class MyActivityTest {
#Rule
public ActivityScenarioRule<MyActivity> activityRule = new ActivityScenarioRule<>(MyActivity.class);
#Test
public void foo() {
ActivityScenarioRule<MyActivity> scenario = activityRule.getScenario()
}
}

Related

Strange behavior of Mockito, a small portion of mock lost effect when ran in mvn test

I'm working on merging 2 legacy projects, every junit test works fine in IDE(Intellij). But some error happens when I execute mvn test. From the error stacktrace, it looks like mock lost effects at some point somewhere. (In a total of about 700 tests, 6 failed).
A failuire example looks like this.
#RunWith(MockitoJUnitRunner.class)
public class SomeHandlerTest {
#Mock
private Child child;
#InjectMocks
private SomeHandler handler;
#Before
public void setUp() throws Exception {
}
#Test
public void testCall() {
handler.call();
}
}
public class SomeHandler {
private Child child;
public String call() {
child.doA();
child.doB();
return "ccc";
}
}
public class Child extends Parent {
public void doB() {
System.out.println("bbb");
}
}
abstract class Parent {
void doA() {
System.out.println("aaa");
}
}
This is just an example, and works as expected(real doA and doB are not invoked during test).
But in real codebase, doA got invoked unexpecttedly and some error happened.
What I have tried:
run failed test class alone in mvn test -Dtest=xxx, failures disappeared.
add <forkMode>always</forkMode> to surefile plugin configuration, failures disappeared.
change doA() method in the Parent class from default to public, failures disappeared.
Does any of these behaviors looks familiar to your guys? How can I troubleshoot this problem so that it can just ran normally? I don't want to change source code just for unit test or change forkMode to always(too slow).
If you add add "always to surefile plugin configuration", failures disappeared.
I think your problem is that you have tests which have side effects which influence your failed tests.
Answering my own question.
It turns out some other test changed Classloader during test.
ClassLoader loader = xxx
Thread.currentThread().setContextClassLoader(loader);
Save the original classloader before that and restore it in the #After solved the problem.
public class XXXTest {
private static ThreadLocal<ClassLoader> threadLocalClassloader = new ThreadLocal<>() ;
#Before
public void setUp() {
threadLocalClassloader.set(Thread.currentThread().getContextClassLoader());
}
#After
public void tearDown() {
Thread.currentThread().setContextClassLoader(threadLocalClassloader.get());
}
}
This well explained
add <forkMode>always</forkMode> to surefile plugin configuration, failures disappeared.
As for the reason of
every junit test works fine in IDE(Intellij)
change doA() method in the Parent class from default to public, failures disappeared.
It's too complicated and I can't explain why, maybe they're just coincidences...

Adding the class for which Junit is written in #PrepareForTest makes a Zero coverage in sonar

Sample Controller
public class SampleController {
public void sampleMethod() {
ClassAbc classAbc = new ClassAbc();
classAbc.abcMethod();
//doStuff
}
}
ClassAbc
public class ClassAbc {
public void abcMethod() {
//doStuff
}
}
Junit for SampleController
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PrepareForTest({SampleController.class})
public class SampleControllerTest {
#Autowired
SampleController sampleController;
public void setUp() {
ClassAbc classAbc = PowerMockito.mock(ClassAbc.class);
PowerMockito.whenNew(ClassAbc.class).withAnyArguments()
.thenReturn(classAbc);
doNothing().when(classAbc).abcMethod();
}
#Test
public void testsampleMethod() throws Exception {
sampleController.sampleMethod();
}
}
When Iam adding the "SampleController.class" in #PrepareForTest ie:
#PrepareForTest({SampleController.class})
Iam getting the following violation when I run Sonar job.
14 more branches need to be covered by unit tests to reach the minimum threshold of 65.0% branch coverage.
That is Iam getting zero coverage for that class.
I would like to like if it is necessary to put the class where the constructor is called into the #PrepareForTest annotation instead of the class which is being constructed.
Or can someone find me a solution to fix that Sonar violation.
Thanks in advance
As stated in PowerMockito wiki:
... right now there is NO WAY TO USE PowerMock with JaCoCo On-the-fly instrumentation.
... to get code coverage with JaCoCo - use offline Instrumentation ...
You may find example of using PowerMock with JaCoCo Offline Instrumentation and Maven in our repository: jacoco-offline example.

Why my JUnit error collector is not reporting the error?

I am trying to report error using JUnit error collector. Although my assertion is failing, error is not reported in JUnit. But I am getting the "error" message in console.
#Rule
public ErrorCollector errcol = new ErrorCollector();
#Then("^Business alert message on the screen$")
public void Business_alert_message_on_the_screen(Result_Update) throws Throwable {
if (userType.equals("Admin")) {
try {
Assert.assertEquals("Update button is not present for the admin user", true, Result_Update);
} catch (Throwable t) {
errcol.addError(t);
System.out.println("Error");
}
}
}
tl;dr : Make sure your test class doesn't extend TestCase.
I had a similar problem when I was using JUnit 4 with IntelliJ IDEA. I naïvely selected a base class of TestCase in the dialog, which was the default for JUnit 3, because I figured "it'd be nice to have those handy this#assert* methods" (the default for JUnit 4 is null). The bad code (which didn't work) is below:
public class SassCompilerTest extends TestCase {
#Rule
public ErrorCollector collector = new ErrorCollector();
#Test
public void testCompiler() throws IOException {
collector.checkThat(true, CoreMatchers.equalTo(false));
}
}
However, in JUnit 4, that prevented a lot of features from working. Removing the parent class fixed the test:
public class SassCompilerTest {
#Rule
public ErrorCollector collector = new ErrorCollector();
#Test
public void testCompiler() throws IOException {
collector.checkThat(true, CoreMatchers.equalTo(false));
}
}
The solution was suggested to me by a comment in the issue with Cucumber mentioned by #StefanBirkner in another answer. After reading that, I tried extending ErrorCollector to make the ErrorCollector#verify public and call it from an #After method, but the #After method wasn't getting called, which made me realize something was either wrong with the TestRunner (which was IntelliJ's default) or the Test itself.
According to JUnit:
The ErrorCollector rule allows execution of a test to continue after
the first problem is found
errcol.addError(t);//only adds the error to the ErrorCollector
This means that the test continues after collecting the error.
You should add:
errcol.checkThat(...); //will pass/fail the test
See examples:
https://junit.org/junit4/javadoc/4.12/org/junit/rules/ErrorCollector.html (Updated)
https://gist.github.com/cb372/2419626
The Cucumber runner does not support #Rule because it extends ParentRunner and not BlockJUnit4ClassRunner (see source code of the runner). There is already an issue for supporting ErrorCollector.

Mock/Stub super constructor invocation in Java for unit testing

I want to unit-test my class with JUnit and EasyMock. It extends android.location.Location. But I am always getting Stub! exception because most of Android methods are not available in JVM runtime.
public class MyLocation extends Location {
public MyLocation(Location l) {
super(l);
}
public boolean methodUnderTest() {
return true;
}
}
I've tried to mock constructor invocation using Powermock, but it looks like it does not work for super calls. My test:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Location.class)
public class MyLocationTest {
#Test
public void methodUnderTestReturnsTrue() throws Exception {
Location locationMock = EasyMock.createMock(Location.class);
expectNew(Location.class, Location.class).andReturn(locationMock);
MyLocation myLocation = new MyLocation(locationMock);
assertTrue(myLocation.methodUnderTest());
}
}
An exception I am getting:
java.lang.RuntimeException: Stub!
at android.location.Location.<init>(Location.java:6)
Obviously the solution is to execute this test in Android runtime (i.e. start Android Simulator). But I don't like this approach because it takes quite a few time to start such test suite. Is there a way to stub super invocation or probably there's better approach in testing such implementations?
Taken straight from the Powermocks documentation.
Testing can then be done without invoking the EvilParent constructor.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ExampleWithEvilParent.class)
public class ExampleWithEvilParentTest {
#Test
public void testSuppressConstructorOfEvilParent() throws Exception {
suppress(constructor(EvilParent.class));
final String message = "myMessage";
ExampleWithEvilParent tested = new ExampleWithEvilParent(message);
assertEquals(message, tested.getMessage());
}
}

JUnit: how to avoid "no runnable methods" in test utils classes

I have switched to JUnit4.4 from JUnit3.8. I run my tests using ant, all my tests run successfully but test utility classes fail with "No runnable methods" error. The pattern I am using is to include all classes with name *Test* under test folder.
I understand that the runner can't find any method annotated with #Test attribute. But they don't contain such annotation because these classes are not tests.
Surprisingly when running these tests in eclipse, it doesn't complain about these classes.
In JUnit3.8 it wasn't a problem at all since these utility classes didn't extend TestCase so the runner didn't try to execute them.
I know I can exclude these specific classes in the junit target in ant script. But I don't want to change the build file upon every new utility class I add. I can also rename the classes (but giving good names to classes was always my weakest talent :-) )
Is there any elegant solution for this problem?
Annotate your util classes with #Ignore. This will cause JUnit not to try and run them as tests.
My specific case has the following scenario. Our tests
public class VenueResourceContainerTest extends BaseTixContainerTest
all extend
BaseTixContainerTest
and JUnit was trying to run BaseTixContainerTest. Poor BaseTixContainerTest was just trying to setup the container, setup the client, order some pizza and relax... man.
As mentioned previously, you can annotate the class with
#Ignore
But that caused JUnit to report that test as skipped (as opposed to completely ignored).
Tests run: 4, Failures: 0, Errors: 0, Skipped: 1
That kind of irritated me.
So I made BaseTixContainerTest abstract, and now JUnit truly ignores it.
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
Assuming you're in control of the pattern used to find test classes, I'd suggest changing it to match *Test rather than *Test*. That way TestHelper won't get matched, but FooTest will.
To prevent JUnit from instantiating your test base class just make it
public abstract class MyTestBaseClass { ... whatever... }
(#Ignore reports it as ignored which I reserve for temporarily ignored tests.)
If this is your base test class for example AbstractTest and all your tests extends this then define this class as abstract
If it is Util class then better remove *Test from the class rename it is MyTestUtil or Utils etc.
Be careful when using an IDE's code-completion to add the import for #Test.
It has to be import org.junit.Test and not import org.testng.annotations.Test, for example. If you do the latter, you'll get the "no runnable methods" error.
Ant now comes with the skipNonTests attribute which was designed to do exactly what you seem to be looking for. No need to change your base classes to abstract or add annotations to them.
What about adding an empty test method to these classes?
public void avoidAnnoyingErrorMessageWhenRunningTestsInAnt() {
assertTrue(true); // do nothing;
}
In your test class if wrote import org.junit.jupiter.api.Test; delete it and write import org.junit.Test; In this case it worked me as well.
I was also facing a similar issue ("no runnable methods..") on running the simplest of simple piece of code (Using #Test, #Before etc.) and found the solution nowhere. I was using Junit4 and Eclipse SDK version 4.1.2. Resolved my problem by using the latest Eclipse SDK 4.2.2. I hope this helps people who are struggling with a somewhat similar issue.
I also faced the same issue once. In my case I was running my tests using Enclosed Runner of Junit. I created a class called SharedSetup to enable common features for my 2 test classes. But somehow facing the same issue.
#RunWith(Enclosed.class)
public class StructApprovalNodeTest {
abstract static class SharedSetup {
StructApprovalNode sut;
ExecutionContext ctx = mock(ExecutionContext.class);
DTDDAOService dtd = mock(DTDDAOService.class);
#Rule
public ExpectedException expectedException = ExpectedException.none();
#Before
public void before() throws Exception {
PowerMockito.mockStatic(ServiceHelper.class);
when(ServiceHelper.getService("dtd")).thenReturn(dtd);
when(ctx.getContextInstance()).thenReturn(mock(ContextInstance.class));
when(dtd.getLatestStructures(Matchers.anyInt(), Matchers.anyString(), Matchers.anyString())).thenReturn(
new ArrayList<Trade>());
sut = new StructApprovalNode();
spy(sut);
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ServiceHelper.class, StructApprovalNode.class })
#PowerMockIgnore("javax.management.*")
#PowerMockRunnerDelegate(Parameterized.class)
public static class ParamaterizedBatchTest extends SharedSetup {
private String batchName;
private String approvalStatus;
public ParamaterizedBatchTest(String batchName, String approvalStatus) {
this.batchName = batchName;
this.approvalStatus = approvalStatus;
}
#Parameterized.Parameters
public static Collection testValues() {
return Arrays.asList(new Object[][] {
{ "SDC_HK_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED },
{ "SDC_PB_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED },
{ "SDC_FX_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED }
});
}
#Test
public void test1_SDCBatchSourceSystems() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem(this.batchName);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(null);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(this.approvalStatus, trade.getApprovalStatus());
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ServiceHelper.class, StructApprovalNode.class })
#PowerMockIgnore("javax.management.*")
public static class NonParamaterizedBatchTest extends SharedSetup {
#Test
public void test2_PrevInvalidTrade() throws Exception {
expectedException.expect(Exception.class);
expectedException.expectMessage("External Id of STRUCTURE_TRADE cannot be changed.");
Trade trade = new Trade();
trade.setExternalId(123);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1234);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
}
#Test
public void test3_ValidPrevTrade() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1277402441);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals("APPROVED", trade.getApprovalStatus());
}
#Test
public void test4_ValidPrevTradeAutoApprpve() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML_AUTO_APPRV.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1277402441);
prevTrade.setApprovalStatus(Constants.APRVLSTATUS_NOTAPPROVED);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(prevTrade.getApprovalStatus(), trade.getApprovalStatus());
}
#Test
public void test5_tradeStatusDraft() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_DRAFT);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(null);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(Constants.APRVLSTATUS_NONE, trade.getApprovalStatus());
}
}
}
To solve the issue, I just removed the public modifier from the abstract super class SharedSetup and issue is fixed for good

Categories

Resources