Android Roboelectric 3.0 testing next activity –choosing from multiple activities - robolectric

I have scenario like this:--
I have three activities ActivityA(launcher activity), ActivityB, ActivityC
Now in Activity A I read the Application context to decide whether to start ActivityB or ActivityC.
But even after setting the context value manual, the ActivityA is not updated and second test case fails. Any helps?
private ActivityA activityA;
private ShadowActivity shadowActivity;
#Before
public void setUp() {
activityA = Robolectric.setupActivity(ActivityA.class);
assertNotNull("ActivityA not intsantiated", activityA);
shadowActivity = Shadows.shadowOf(activityA);
}
#Test
public void shouldStartActivityB() throws Exception
{
Intent intent = shadowActivity.peekNextStartedActivity();;
assertEquals(ActivityB.class.getCanonicalName(), intent.getComponent().getClassName());
}
#Test
public void shouldStartMainActivity() throws Exception
{
ApplicationSettings.setWizardState(RuntimeEnvironment.application, 22);
ShadowLog.d("Wizard state value", "" +ApplicationSettings.getWizardState(RuntimeEnvironment.application));// it prints 22
activityA= Robolectric.setupActivity(ActivityA.class);
shadowActivity = Shadows.shadowOf(activityA);
Intent intent = shadowActivity.peekNextStartedActivity();
ShadowLog.d("target activity is", intent.getComponent().getClassName());// This prints ActivityB instead of ActivityC
assertEquals(ActivityC.class.getCanonicalName(), intent.getComponent().getClassName()); // this test case fails
}

Related

Multiple chained API calls to fetch data, but doOnNext of PublishSubject is never reached

I have a problem to understand a chained "RXJava-Retrofit" API call. I got inspired by this and implement this class named ObservationLoader to load the data from the API bucket per bucket. When the end of data is reached the API sends a endOfRecords=true:
public Observable<PageObject<Observation>> getAllObservationDataByRegion(long taxonKey,
String regionId) {
final PublishSubject<PageObject<Observation>> subject = PublishSubject.create();
return subject.doOnSubscribe(disposable -> {
this.getData(taxonKey, regionId, 0).subscribe(subject);
})
.doOnNext(observationPageObject -> {
if (observationPageObject.isEndOfRecords()) {
// -> list is completely loaded
subject.onComplete();
} else {
int nextOffset = observationPageObject.getOffset() + 1;
this.getData(taxonKey, regionId, null, nextOffset).subscribe(subject);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<PageObject<Observation>> getData(long id,
String regionId,
int offset) {
// Get your API response value
return this.api.getObservations(id, regionId, ObservationLoader.PAGE_LIMIT, offset);
}
In my Android fragment HomeFragment I subscribe to the ObservationLoader:
ObservationLoader loader = new ObservationLoader(this.getApi());
Observable<PageObject<Observation>> observable = loader
.getAllObservationDataByRegion(this.getSelectedSpecies(), this.getSelectedRegion());
observable.subscribe(new Observer<PageObject<Observation>>() {
#Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "ON_SUBSCRIBE");
}
#Override
public void onNext(PageObject<Observation> observationPageObject) {
Log.i(TAG, "ON_NEXT");
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "ERROR = " + e.getMessage());
}
#Override
public void onComplete() {
Log.i(TAG, "COMPLETED");
}
});
I can see that the onSubscribe() and doOnSubscribe() are called and even the getData() is reached. I assume the API is responding correctly (a previous attempt attempt with recursion worked fine). But I never reached the doOnNext function. The observer goes straight to onComplete() and no data is received. What could be the reason?
When doOnSubscribe runs, the doesn't see any consumers yet so if getData is synchronous, there won't be any first results to trigger further results. Also if getData ends, it will complete the setup so the next getData call in doOnNext will push to an already terminated subject, ingoring all data.
You'll need a differently organized feedback loop:
// we loop back the nextOffset, in a thread-safe manner
Subject<Integer> subject = PublishSubject.<Integer>create()
.toSerialized();
// bootstrap with 0 and keep open for more offsets
subject.mergeWith(Observable.just(0))
// get the data for the current offset
.concatMap(nextOffset -> getData(taxonKey, regionId, nextOffset)
.subscribeOn(Schedulers.io())
)
// if the response is end of records, stop
.takeWhile(observationPageObject -> !observationPageObject.isEndOfRecords())
// otherwise not end of records, feedback the new offset
.doOnNext(observationPageObject ->
subject.onNext(observationPageObject.getOffset() + 1)
)
// get the data on the main thread
.observeOn(AndroidSchedulers.mainThread());

Re-running complete class and not just #Test in TestNG

I have been browsing stackoverflow for some days, trying to find how to re-run a whole test class, and not just an #Test step. Many say that this is not supported with TestNG and IRetryAnalyzer, whereas some have posted workarounds, that don't really work.
Has anyone manage to do it?
And just to clarify the reasons for this, in order to avoid answers that say that is not supported in purpose: TestNG is a tool not only for developers. Meaning that is also used from sw testers for e2e testing. E2e tests can have steps that depend each from the previous one. So yes it's valid to re-run whole test class, rather than simple #Test, which is easily can be done via IRetryAnalyzer.
An example of what I want to achieve would be:
public class DemoTest extends TestBase {
#Test(alwaysRun = true, description = "Do this")
public void testStep_1() {
driver.navigate().to("http://www.stackoverflow.com");
Assert.assertEquals(driver.getCurrentUrl().contains("stackoverflow)"));
}
#Test(alwaysRun = true, dependsOnMethods = "testStep_1", description = "Do that")
public void testStep_2() {
driver.press("button");
Assert.assertEquals(true, driver.elementIsVisible("button"));
}
#Test(alwaysRun = true, dependsOnMethods = "testStep_2", description = "Do something else")
public void testStep_3() {
driver.press("button2");
Assert.assertEquals(true, driver.elementIsVisible("button"));
}
}
Let's say that testStep_2 fails, I want to rerun class DemoTest and not just testStep_2
Okay, I know that you probably want some easy property you can specify in your #BeforeClass or something like that, but we might need to wait for that to be implemented. At least I couldn't find it either.
The following is ugly as hell but I think it does the job, at least in a small scale, it is left to see how it behaves in more complex scenarios. Maybe with more time, this can be improved into something better.
Okay, so I created a Test Class similar to yours:
public class RetryTest extends TestConfig {
public class RetryTest extends TestConfig {
Assertion assertion = new Assertion();
#Test( enabled = true,
groups = { "retryTest" },
retryAnalyzer = TestRetry.class,
ignoreMissingDependencies = false)
public void testStep_1() {
}
#Test( enabled = true,
groups = { "retryTest" },
retryAnalyzer = TestRetry.class,
dependsOnMethods = "testStep_1",
ignoreMissingDependencies = false)
public void testStep_2() {
if (fail) assertion.fail("This will fail the first time and not the second.");
}
#Test( enabled = true,
groups = { "retryTest" },
retryAnalyzer = TestRetry.class,
dependsOnMethods = "testStep_2",
ignoreMissingDependencies = false)
public void testStep_3() {
}
#Test( enabled = true)
public void testStep_4() {
assertion.fail("This should leave a failure in the end.");
}
}
I have the Listener in the super class just in the case I'd like to extend this to other classes, but you can as well set the listener in your test class.
#Listeners(TestListener.class)
public class TestConfig {
protected static boolean retrySuccessful = false;
protected static boolean fail = true;
}
Three of the 4 methods above have a RetryAnalyzer. I left the testStep_4 without it to make sure that what I'm doing next doesn't mess with the rest of the execution. Said RetryAnalyzer won't actually retry (note that the method returns false), but it will do the following:
public class TestRetry implements IRetryAnalyzer {
public static TestNG retryTestNG = null;
#Override
public boolean retry(ITestResult result) {
Class[] classes = {CreateBookingTest.class};
TestNG retryTestNG = new TestNG();
retryTestNG.setDefaultTestName("RETRY TEST");
retryTestNG.setTestClasses(classes);
retryTestNG.setGroups("retryTest");
retryTestNG.addListener(new RetryAnnotationTransformer());
retryTestNG.addListener(new TestListenerRetry());
retryTestNG.run();
return false;
}
}
This will create an execution inside of your execution. It won't mess with the report, and as soon as it finishes, it will continue with your main execution. But it will "retry" the methods within that group.
Yes, I know, I know. This means that you are going to execute your test suite forever in an eternal loop. That's why the RetryAnnotationTransformer. In it, we will remove the RetryAnalyzer from the second execution of those tests:
public class RetryAnnotationTransformer extends TestConfig implements IAnnotationTransformer {
#SuppressWarnings("rawtypes")
#Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
fail = false; // This is just for debugging. Will make testStep_2 pass in the second run.
annotation.setRetryAnalyzer(null);
}
}
Now we have the last of our problems. Our original test suite knows nothing about that "retry" execution there. This is where it gets really ugly. We need to tell our Reporter what just happened. And this is the part that I encourage you to improve. I'm lacking the time to do something nicer, but if I can, I will edit it at some point.
First, we need to know if the retryTestNG execution was successful. There's probably a million ways to do this better, but for now this works. I set up a listener just for the retrying execution. You can see it in TestRetry above, and it consists of the following:
public class TestListenerRetry extends TestConfig implements ITestListener {
(...)
#Override
public void onFinish(ITestContext context) {
if (context.getFailedTests().size()==0 && context.getSkippedTests().size()==0) {
successful = true;
}
}
}
Now the Listener of the main suite, the one you saw above in the super class TestConfig will see if it the run happened and if it went well and will update the report:
public class TestListener extends TestConfig implements ITestListener , ISuiteListener {
(...)
#Override
public void onFinish(ISuite suite) {
if (TestRetry.retryTestNG != null) {
for (ITestNGMethod iTestNGMethod : suite.getMethodsByGroups().get("retryTest")) {
Collection<ISuiteResult> iSuiteResultList = suite.getResults().values();
for (ISuiteResult iSuiteResult : iSuiteResultList) {
ITestContext iTestContext = iSuiteResult.getTestContext();
List<ITestResult> unsuccessfulMethods = new ArrayList<ITestResult>();
for (ITestResult iTestResult : iTestContext.getFailedTests().getAllResults()) {
if (iTestResult.getMethod().equals(iTestNGMethod)) {
iTestContext.getFailedTests().removeResult(iTestResult);
unsuccessfulMethods.add(iTestResult);
}
}
for (ITestResult iTestResult : iTestContext.getSkippedTests().getAllResults()) {
if (iTestResult.getMethod().equals(iTestNGMethod)) {
iTestContext.getSkippedTests().removeResult(iTestResult);
unsuccessfulMethods.add(iTestResult);
}
}
for (ITestResult iTestResult : unsuccessfulMethods) {
iTestResult.setStatus(1);
iTestContext.getPassedTests().addResult(iTestResult, iTestResult.getMethod());
}
}
}
}
}
}
The report should show now 3 tests passed (as they were retried) and one that failed because it wasn't part of the other 3 tests:
I know it's not what you are looking for, but I help it serves you until they add the functionality to TestNG.

Vault query for cash in flow test not working

Issuing some cash within a Flow test - the flow returns the transaction with the output showing the correct Cash state. However, when I vault query for cash states, nothing is returned. Am I missing something?
IssueTokensFlow
#StartableByRPC
public class IssueTokensFlow extends FlowLogic<SignedTransaction> {
private static Double amount;
public IssueTokensFlow(double amount) {
this.amount = amount;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException {
// We retrieve the notary identity from the network map.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// Issue cash tokens equal to transfer amount
AbstractCashFlow.Result cashIssueResult = subFlow(new CashIssueFlow(
Currencies.DOLLARS(amount), OpaqueBytes.of(Byte.parseByte("1")), notary)
);
return cashIssueResult.getStx();
} }
IssueTokenFlow Test
#Test
public void testIssueCash() throws Exception {
IssueTokensFlow flow =
new IssueTokensFlow(100.00);
SignedTransaction transaction = a.startFlow(flow).get();
network.waitQuiescent();
Cash.State state = (Cash.State) transaction.getTx().getOutputStates().get(0);
assertEquals(state.getOwner(), chooseIdentity(a.getInfo()));
assertEquals(state.getAmount().getQuantity(), Currencies.DOLLARS(100.00).getQuantity());
// Above assertions pass
QueryCriteria.VaultQueryCriteria criteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
Vault.Page<ContractState> results = a.getServices().getVaultService().queryBy(Cash.State.class, criteria);
assertTrue(results.getStates().size() > 0);
// ^ This assertion fails
}
In Corda 3, whenever you query a node’s database as part of a test (e.g. to extract information from the node’s vault), you must wrap the query in a database transaction, as follows:
node.transaction(tx -> {
// Perform query here.
}
So your test would become:
#Test
public void testIssueCash() throws Exception {
IssueTokensFlow2 flow = new IssueTokensFlow2(100.00);
SignedTransaction transaction = a.startFlow(flow).get();
network.waitQuiescent();
Cash.State state = (Cash.State) transaction.getTx().getOutputStates().get(0);
assertEquals(state.getOwner(), chooseIdentity(a.getInfo()));
assertEquals(state.getAmount().getQuantity(), Currencies.DOLLARS(100.00).getQuantity());
// Above assertions pass
QueryCriteria.VaultQueryCriteria criteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
a.transaction(() -> {
Vault.Page<ContractState> results = a.getServices().getVaultService().queryBy(Cash.State.class, criteria);
assertTrue(results.getStates().size() > 0);
// ^ This assertion doesn't fail :)
return null;
});
}

rxjava - Combine onerror and timout handling

I will start with what I want to achieve.
I want to call a method that returns an Observabe.
I do not know if the called method handles exceptions and timeouts
I want to combine observables in my call (merge/zip etc)
if one method fails, I want the answers from the methods that succeeded -
I don't want to break the flow.
In case of exception, I am capable of handling it and continuing with the flow,
but when I try to add timeoutmanagement I fail.
Here is my code
public static void main(String[] args) {
createObservables(false, true); // stalls for timeout
zip();
}
private static void createObservables(final boolean throwException,
final boolean stall) {
obs1 = Observable.just(1);
obs1 = obs1.map(new Func1<Integer, Integer>() {
#Override public Integer call(Integer integer) {
int i = 0;
if (throwException)
getObj().equals("");
if (stall)
zzz(10);
return ++integer;
}
});
obs2 = Observable.just(111);
}
private static void zip() {
System.out.println("**Zip**");
obs1 = obs1.onErrorReturn(new Func1<Throwable, Integer>() {
#Override public Integer call(Throwable throwable) {
return 999;
}
});
obs1 = obs1.timeout(5, TimeUnit.SECONDS);
Observable.zip(obs1, obs2, new Func2<Integer, Integer, ArrayList<Integer>>() {
#Override
public ArrayList<Integer> call(Integer integer1, Integer integer2) {
ArrayList<Integer> integers = new ArrayList<Integer>();
integers.add(integer1);
integers.add(integer2);
return integers;
}
}).subscribe(new Observer<Object>() {....}
);
}
Now, when I call
createObservables(false , false); // no exceptions and timeouts
I get onNext - [2, 111].
then I call
createObservables(true, false); // throw exception in one method only
I get onNext - [999, 111] - which is what I want. Exception and the result from the second method.
But when I call
createObservables(false, true); // stall on timeout
I get only onError.
But I want to get the other method answer.
Thanks.
Try creating an observable for your timeout value, in this case you want the same value as your error case:
Observable obs1Timeout = Observable.just(999);
Then in your timeout policy give it this observable as the fallback to use in the case of a timeout:
obs1 = obs1.timeout(5, TimeUnit.SECONDS, obs1Timeout);

Android testing - start with clean database for every test

I'm testing my application with Android Instrumentation tests.
So I have a test-class extending ActivityInstrumentationTestCase2 which contains multiple tests. The code looks like this:
public class ManageProjectsActivityTestextends ActivityInstrumentationTestCase2<ManageProjectsActivity> {
public ManageProjectsActivityTest() {
super("eu.vranckaert.worktime", ManageProjectsActivity.class);
}
#Override
protected void setUp() throws Exception {
getInstrumentation().getTargetContext().deleteDatabase(DaoConstants.DATABASE);
super.setUp();
solo = new Solo(getInstrumentation(), getActivity());
}
#Override
protected void runTest() throws Throwable {
super.runTest();
getActivity().finish();
}
public void testDefaults() {
// My test stuff
}
public void testAddProject() {
// My test stuff
}
}
So the activity which is under test has a list of projects.
The list of projects is retrieved from the database. And when no database is available, so when the DB is created, I insert one default project.
So that means when the tests are run this is what I exepct:
The database, if available, is removed on the device
The first test is started (and thus the activity is launched which creates my DB with one project)
The tests uses the newly created DB, so meaning with only one project, during the test a second project is created
The first test is finished and the setUp() method is called again
The database, that should exist now, is again removed
The second test is started (and thus the activity is launched which creates my DB with one project)
The second test also finishes
But that's not quite what this test-suite does...
This is the result of my test-suite:
The database, if available, is removed on the device
The first test is started (and thus the activity is launched which creates my DB with one project)
The tests uses the newly created DB, so meaning with only one project, during the test a second project is created
The first test is finished and the setUp() method is called again
The database, that should exist now, is again removed
And here it comes: The second test is started (but my DB is not created again!!! I cannot see a file on the device either...) and the test should display only one project at the beginning but it does display already two!!!
The second test also finishes but fails because I have two projects at the beginning...
At the beginning I did not override the runTest() method but I thought that maybe I should end the activity myself to force the re-creation, but it doesn't make any difference.
So it seems that the DB is kept in memory (as even no new DB file is created on the device when I explicitly remove it). Or even the activity, because when putting a breakpoint a in the onCreate of the activity I only get in there once for both tests.
For the maintaining the DB I use ORMLite. You can see my helper class here: http://code.google.com/p/worktime/source/browse/trunk/android-app/src/eu/vranckaert/worktime/dao/utils/DatabaseHelper.java
So my question is how to force the tests to use a different DB all the time...?
A bit tangential to this problem, but I landed here when I was looking for help. Might be helpful to some folks. If you initialize your database with the RenamingDelegatingContext, it cleans the database between runs.
public class DataManagerTest extends InstrumentationTestCase {
private DataManager subject;
#Before
public void setUp() {
super.setUp();
RenamingDelegatingContext newContext = new RenamingDelegatingContext(getInstrumentation().getContext(), "test_");
subject = new DataManager(newContext);
}
// tests...
}
And the associated DataManagerClass.
public class DataManager {
private SQLiteDatabase mDatabase;
private SQLiteOpenHelper mHelper;
private final String mDatabaseName = "table";
private final int mDatabaseVersion = 1;
protected DataManager(Context context) {
this.mContext = context;
createHelper();
}
private void createHelper() {
mHelper = new SQLiteOpenHelper(mContext, mDatabaseName, null, mDatabaseVersion) {
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// createTable...
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
// upgrade table
}
};
}
...
}
mDb.delete(DATABASE_TABLE_NAME, null, null);
That is indeed the solution/way-to-go...
I changed the first line in my setUp(..) method to this:
cleanUpDatabase(tableList);
And then I added the method cleanUpDatabse(..) liek this:
private void cleanUpDatabase(List<String> dbTables) {
Log.i(LOG_TAG, "Preparing to clean up database...");
DatabaseHelper dbHelper = new DatabaseHelper(getInstrumentation().getTargetContext());
ConnectionSource cs = dbHelper.getConnectionSource();
SQLiteDatabase db = dbHelper.getWritableDatabase();
Log.i(LOG_TAG, "Dropping all tables");
for (String table : dbTables) {
db.execSQL("DROP TABLE IF EXISTS " + table);
}
Log.i(LOG_TAG, "Executing the onCreate(..)");
dbHelper.onCreate(db, cs);
Log.i(LOG_TAG, "Verifying the data...");
for (String table : dbTables) {
Cursor c = db.query(table, new String[]{"id"}, null, null, null, null, null);
int count = c.getCount();
if (count != 1 && (table.equals("project") || table.equals("task"))) {
dbHelper.close();
Log.e(LOG_TAG, "We should have 1 record for table " + table + " after cleanup but we found " + count + " record(s)");
throw new RuntimeException("Error during cleanup of DB, exactly one record should be present for table " + table + " but we found " + count + " record(s)");
} else if (count != 0 && !(table.equals("project") || table.equals("task"))) {
dbHelper.close();
Log.e(LOG_TAG, "We should have 0 records for table " + table + " after cleanup but we found " + count + " record(s)");
throw new RuntimeException("Error during cleanup of DB, no records should be present for table " + table + " but we found " + count + " record(s)");
}
}
Log.i(LOG_TAG, "The database has been cleaned!");
dbHelper.close();
}
This piece of code gets executed before every test, which makes all my tests independent from each other.
Caution: In order to retrieve a reference to your DatabaseHelper (your own implementation off course ;) ) you cannot call getActivity() because that will launch your activity (and thus do all your initial DB loading (if any..)
InstrumentationRegistry → getContext → deleteDatabase
android.support.test.InstrumentationRegistry's getTargetContext and, counterintuitively perhaps, getContext should do the trick:
Synopsis
Use getContext for deleting database
(not getTargetContext).
getContext().deleteDatabase(DbHelper.DATABASE_NAME);
Example
public class DbHelperTest {
private DbHelper mDb;
#Before
public void setUp() throws Exception {
getContext().deleteDatabase(DbHelper.DATABASE_NAME);
mDb = new DbHelper(getTargetContext());
}
#After
public void tearDown() throws Exception {
mDb.close();
}
#Test
public void onCreate() throws Exception {
mDb.onCreate(mDb.getWritableDatabase());
}
#Test
public void onUpgrade() throws Exception {
mDb.onUpgrade(mDb.getWritableDatabase(), 1, 2);
}
#Test
public void dropTable() throws Exception {
String tableName = "mesa";
mDb.getReadableDatabase().execSQL("CREATE TABLE "
+ tableName + "(_id INTEGER PRIMARY KEY AUTOINCREMENT)");
mDb.dropTable(mDb.getWritableDatabase(), tableName);
}
}
you try to start time all table data delete alter native solution.
mDb.delete(DATABASE_TABLE_NAME, null, null);
Since my db is encrypted I needed to remove the actual db file after each test case. I had trouble making deleteDatabase from a RenamingDelegatingContext since the RenamingDelegatingContext could not find the previously created db for some reason.
Solution for AndroidTestCase subclass:
#Override
protected void setUp() throws Exception {
super.setUp();
mContext = new RenamingDelegatingContext(getContext(), TEST_DB_PREFIX);
// create db here
}
#Override
protected void tearDown() throws Exception {
// Making RenamingDelegatingContext find the test database
((RenamingDelegatingContext) mContext).makeExistingFilesAndDbsAccessible();
mContext.deleteDatabase(DB_NAME);
super.tearDown();
}
before each test run it
val context: Context = ApplicationProvider.getApplicationContext()
context.deleteDatabase("myDatabaseName.db")
this can also be made a rule

Resources