Which is the best way to take a screenshot when one scenario fails using Robotium and Cucumber?
I have tried (without success, because it doesn't execute runTest method) with this:
import cucumber.api.CucumberOptions;
import cucumber.api.java.After;
import cucumber.api.java.Before;
#CucumberOptions(features = "features", tags = {"~#ignore"})
public class CustomInstrumentationTestCase extends ActivityInstrumentationTestCase2<LaunchActivity> {
protected Solo solo;
public CustomInstrumentationTestCase() {
super(LaunchActivity.class);
}
#Before
public void before() throws Exception {
//...
}
#After
public void after() throws Exception {
//...
}
#Override
protected void runTest() throws Throwable {
try {
super.runTest();
} catch (Throwable t) {
final String testCaseName = String.format("%s.%s", getClass().getName(), getName());
solo.takeScreenshot(testCaseName);
Log.w("Boom! Screenshot!", String.format("Captured screenshot for failed test: %s", testCaseName));
throw t;
}
}
}
And I have set the permissions in the manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Your testclass seems fine. The runTest() methode is used to catch errors and failures and as a result make a screenshot.
To your class simply add a test like this:
public void testFailsForScreenShot(){
fail();
}
Than run the test and you will find a screenshot.
Greetings
EDIT: Test for Screenshot:
import android.util.Log;
import com.robotium.solo.Solo;
public class TestScreenshot extends ActivityInstrumentationTestCase2<LauncherActivity> {
private Solo solo;
public TestScreenshot(){
super(LauncherActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
solo = new Solo(getInstrumentation(), getActivity());
}
//Test if "Test" fails and takes Screenshot
public void testFailForScreenshot(){
fail();
}
#Override
public void runTest() throws Throwable {
try {
super.runTest();
} catch (Throwable t) {
String testCaseName = String.format("%s.%s", getClass().getName(), getName());
solo.takeScreenshot(testCaseName);
Log.w("Screenshot taken.", String.format("Captured screenshot for failed test: %s", testCaseName));
throw t;
}
}
Related
I'm trying to figure out how to handle errors with Room. I have the following interactor that inserts a task in the database:
TaskInteractor.java
public class TaskInteractor extends AbstractInteractor implements TaskContract.Interactor {
final TaskRepository mRepository;
interface Callback {
void onSuccess();
void onFailure(Throwable t);
}
#Inject
public TaskInteractor(WorkerThread workerThread,
MainThread mainThread,
TaskRepository repository) {
super(workerThread, mainThread);
this.mRepository = repository;
}
#Override
public void insertTask(final Task task, final Callback callback)
throws SQLiteException {
mWorkerThread.get().execute(new Runnable() {
#Override
public void run() {
try {
mRepository.insertTask(task);
} catch (SQLiteException exeption) {
Timber.e("Insertion failed. Exception: " + exeption.getMessage());
callback.onFailure(exeption);
throw exeption;
}
Timber.d("Insertion succeeded.");
callback.onSuccess();
}
});
}
}
In insertTask I use a try-catch block to check if a SQLiteException happened. If it does, I throw the exception.
But is this a good way of handling errors or is there maybe a better way?
I am struggling to write unit tests for my API implementation in Android. I would like to test the Retrofit functionality but run into concurrency problems, where I do not know how to ensure that the async API calls get executed and finish before I start testing the Android internal database calls.
Here is my test function:
public class postPrintModeTest extends ActivityInstrumentationTestCase2<MainActivity> implements IConstants {
public MainActivity activity;
public postPrintModeTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
activity = getActivity();
String printModeName = "LSD Mode";
int parentId = 4;
Map<String, Object> payload = new HashMap<String, Object>();
payload.put("name", printModeName);
payload.put("parentId", parentId);
APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, payload); // <- this needs to finish before I execute the tests, so I have proper data in the database.
}
#SmallTest
public void testPrintModeCreated() {
DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
assertNotNull("Print Mode does not exist", printMode);
}
#SmallTest
public void testPrintModeName() {
DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
if(printMode != null)
{
assertTrue("Print Mode name is not correct", printMode.getName().equals("LSD Mode"));
}
}
}
and here is the async method in question:
public void postPrintMode(String authorization, final int userid, String deviceuid, final Map payload){
api.postPrintMode(authorization, userid, deviceuid, payload, new Callback<PrintMode>() {
#Override
public void success(PrintMode printMode, Response response) {
if (printMode.get_id() != 0) {
dbOps.writePrintMode(userid, printMode);
bus.getBus().post(new EVTNewPrintMode(printMode));
}
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace();
APIUtils.showAPIResponseBody(retrofitError);
}
});
}
Here's my service test code.
public class BackgroundTaskServiceTest extends ServiceTestCase<BackgroundTaskService> {
public BackgroundTaskServiceTest(Class<BackgroundTaskService> serviceClass) {
super(serviceClass);
}
public BackgroundTaskServiceTest() {
super(BackgroundTaskService.class);
}
#SmallTest
public void startServiceTest() {
assertEquals(0, 1);
}
#Override
public void setUp() {
try {
super.setUp();
System.err.println("setup called");
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void tearDown() {
try {
System.err.println("teardown called");
super.tearDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I right-click the project name and select "Run as Android Junit Test". The setUp and tearDown callbacks are called properly , but my startServiceTest is never run.
This is the screen shot of test results:
And another strange thing is that the "setup called" and "teardown called" both appear twice.
So anyone knows why? Thanks.
I think the way that the ADT plugin for Android JUnit works, you need to name your tests starting with 'test', e.g.:
public void testStartService() {
assertEquals(0, 1);
}
Try that and see if it runs.
In my Android application I have set up Volley.
Robolectric.application is initialized and all other tests runs smoothly.
I get this error when trying to get mocked HTTP response.
This is my test:
#RunWith(MyRobolectricTestRunner.class)
public class ApiTests {
#Inject
protected Api api;
#Before
public void setUp() {
ObjectGraph.create(new AndroidModule(Robolectric.application), new TestApplicationModule()).inject(this);
}
#Test
public void shouldGetErrorList() throws Exception {
Project project = new Project("test", "test", "test", DateTime.now());
addPendingProjectsErrorsResponse("response.json"); //adding response to FakeHttpLayer
api.getProjectErrors(project, new Listener<ProjectErrors>() {
#Override
public void onResponse(ProjectErrors response) {
assertNotNull(response);
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
throw new RuntimeException(error);
}
}
);
}
}
This is error I get:
Exception in thread "Thread-3" java.lang.NullPointerException
at org.robolectric.shadows.ShadowLooper.getMainLooper(ShadowLooper.java:59)
at android.os.Looper.getMainLooper(Looper.java)
at org.robolectric.Robolectric.getUiThreadScheduler(Robolectric.java:1301)
at org.robolectric.shadows.ShadowSystemClock.now(ShadowSystemClock.java:15)
at org.robolectric.shadows.ShadowSystemClock.uptimeMillis(ShadowSystemClock.java:25)
at org.robolectric.shadows.ShadowSystemClock.elapsedRealtime(ShadowSystemClock.java:30)
at android.os.SystemClock.elapsedRealtime(SystemClock.java)
at com.android.volley.VolleyLog$MarkerLog.add(VolleyLog.java:114)
at com.android.volley.Request.addMarker(Request.java:174)
at com.android.volley.CacheDispatcher.run(CacheDispatcher.java:92)
I had same error and avoid it by using my own (and ugly) SystemClock shadow.
shadow class:
#Implements(value = SystemClock.class, callThroughByDefault = true)
public static class MyShadowSystemClock {
public static long elapsedRealtime() {
return 0;
}
}
test code:
#Test
#Config(shadows = { MyShadowSystemClock.class, ... })
public void myTest() {
}
Another workaround would be to disable Volley logging by calling
VolleyLog.DEBUG = false;
in your setUp method.
is there a more elegant way to do an assert throws exception in Android then this?
public void testGetNonExistingKey() {
try {
alarm.getValue("NotExistingValue");
fail( );
} catch (ElementNotFoundException e) {
}
}
Something like this does not work?!
#Test(expected=ElementNotFoundException .class)
Thanks, Mark
Are you using a junit4 test runner? The #Test annotation won't work if you're running a junit3 test runner. Check the version that you're using.
Secondly, the recommended way to check for exceptions in your code is to use a Rule (introduced in junit 4.7).
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void throwsIllegalArgumentExceptionIfIconIsNull() {
// do something
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Icon is null, not a file, or doesn't exist.");
new DigitalAssetManager(null, null);
}
You can continue to use the #Test(expected=IOException.class), but the above has the advantage that if an exception is thrown before the exception.expect is called, then the test will fail.
I did something very similar to hopia's answer with a couple of improvements. I made it return the exception object so that you can check its message or any other properties, and I declared a Testable interface to replace Runnable because Runnable doesn't let your code under test throw checked exceptions.
public interface Testable {
public void run() throws Exception;
}
public <T extends Exception> T assertThrows(
final Class<T> expected,
final Testable codeUnderTest) throws Exception {
T result = null;
try {
codeUnderTest.run();
fail("Expecting exception but none was thrown.");
} catch(final Exception actual) {
if (expected.isInstance(actual)) {
result = expected.cast(actual);
}
else {
throw actual;
}
}
return result;
}
Here's an example of calling it.
InvalidWordException ex = assertThrows(
InvalidWordException.class,
new Testable() {
#Override
public void run() throws Exception {
model.makeWord("FORG", player2);
}
});
assertEquals(
"message",
"FORG is not in the dictionary.",
ex.getMessage());
If you're using Kotlin, you can take advantage of reified types to avoid passing the Exception subclass as an argument:
inline fun <reified T : Exception> assertThrows(runnable: () -> Any?) {
try {
runnable.invoke()
} catch (e: Throwable) {
if (e is T) {
return
}
Assert.fail("expected ${T::class.qualifiedName} but caught " +
"${e::class.qualifiedName} instead")
}
Assert.fail("expected ${T::class.qualifiedName}")
}
#Test
fun exampleTest() {
val a = arrayOf(1, 2, 3)
assertThrows<IndexOutOfBoundsException> {
a[5]
}
}
This is how I do it. I create a static method called assertThrowsException that takes in as arguments an expected exception class and a Runnable which contains the code under test.
import junit.framework.Assert;
public SpecialAsserts {
public void assertThrowsException(final Class<? extends Exception> expected, final Runnable codeUnderTest) {
try {
codeUnderTest.run();
Assert.fail("Expecting exception but none was thrown.");
} catch(final Throwable result) {
if (!expected.isInstance(result)) {
Assert.fail("Exception was thrown was unexpected.");
}
}
}
}
This is the sample code to use the special assert in your test class (that extends AndroidTestCase or one of its derivatives):
public void testShouldThrowInvalidParameterException() {
SpecialAsserts.assertThrowsException(InvalidParameterException.class, new Runnable() {
public void run() {
callFuncThatShouldThrow();
}
});
}
Yes, there's a lot of work, but it's better than porting junit4 to android.
With junit3 the following might help.
public static void assertThrows(Class<? extends Throwable> expected,
Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
if (!expected.isInstance(t)) {
Assert.fail("Unexpected Throwable thrown.");
}
return;
}
Assert.fail("Expecting thrown Throwable but none thrown.");
}
public static void assertNoThrow(Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
Assert.fail("Throwable was unexpectedly thrown.");
}
}