Categories
Software Technology

Java integration tests with Spring

Recently, we had to write integration tests because a connection with the database was required in almost all aspects of our business logic. Writing only unit tests without a DB connection didn’t make much sense – we were only able to check if the API returns a proper error message in cases like no records or unsuccessful authentication. Therefore, it was necessary to test using the database.

It’s important to test the DB connection without changing the structure of the production database – but how can this be done? The answer is quite simple – use two databases – the production one and the test one.

Our project is written in Java using the Spring 4 platform.
First, I created a test database using dump from the original one.
I prepared scripts to create, recreate and drop this database as needed, which were added to Makefile.

Now, how can you force the application to use the regular database in all cases except test, where it should use the test database? You just have to swap the database.properties file.
My original database.properties looked like this:

datasource.driver-class-name=org.postgresql.Driver datasource.url=jdbc:postgresql://127.0.0.1:5432/my_database datasource.username=my_username datasource.password=my_pass

I added a database-test.properties file:

datasource.driver-class-name=org.postgresql.Driver datasource.url=jdbc:postgresql://127.0.0.1:5432/test_db datasource.username=test_username datasource.password=test_pass

We can use a different property (not only for the database, but for everything else as well) using a Spring annotation from spring-test dependency.

The test class should have 3 annotations:
@RunWith (SpringJUnit4ClassRunner.class) – Indicates that the class should use Spring’s JUnit facilities
@ContextConfiguration (Application.class) – Indicates ApplicationContext.
My Application.class looks like this:

 @Configuration
 @PropertySources({
    @PropertySource("classpath:database.properties"),
    @PropertySource("classpath:application.properties")
 })
 @ComponentScan
 @EnableAutoConfiguration
 public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       return application.sources(Application.class);
    }
}

And to override original properties you should just use @TestPropertySource annotation. It is a class-level annotation, configuring the location of property files and inlined properties which should be added to set of PropertySources in the Environment. The test property sources have a higher priority than those added declaratively or programmatically using @PropertySource annotation. It works fine to override single classes and make them use different property sources than in the rest of application.
So, the entire test looked more less like this:

 @RunWith(SpringJUnit4ClassRunner.class)
 @TestPropertySource("classpath:database-test.properties")
 @ContextConfiguration(classes = Application.class)
 public class SomethingIntegrationTest {
    @Autowired
    private SomeRepo repo;
    @Test
    public void should_return_proper_something_list_size() {
       List allSomethings = repo.getAllSomethings();
       assertThat("Somethings list should return 7 records", allSomethings.size(), is(7));
    }
 }

I added this dependency to pom.xml file:

<dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-test</artifactId>  </dependency>

We’re skilled in Java programming, but we’re also experts in creating software in many different languages… if you’re interested, take a look at our services.
author: Iga Stępniak

Categories
Uncategorized

UserRecoverableAuthException and "don't keep Activities" option.

While working on a mobile application in Espeo I came across a strange error.

It appeared sometimes when I tried to sign in with Google Account. Typically, when you first log in to Google Services, you see the screen generated by Google with a request for access, e.g. for offline access.

Then another view is displayed, informing that signing in is in progress. After a moment you are signed in.

When you do it for the first time, there will be UserRecoverableAuthException thrown. This is how you should handle it according to official documentation:

catch (UserRecoverableAuthException e) {
// Requesting an authorization code will always throw
// UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
// because the user must consent to offline access to their data.&nbsp; After
// consent is granted control is returned to your activity in onActivityResult
// and the second call to GoogleAuthUtil.getToken will succeed.
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
}

Then you should also handle onActivityResult() method:

onActivityResult(int requestCode, int resultCode, Intent data) {
(...)
final Bundle extra = data.getExtras();
mAuthToken = extra.getString("authtoken");
(...)
}

This way you can get the authorization code, which can be replaced with the access token on the server side.
GoogleAuthUtil.getToken (MyActivity.this, account, scopes) returns only the authorization code, and not the correct access token. It is safer, because Android code is easy to extract. Retrieving access token is a task for the server.

When UserRecoverableAuthException is handled this way, application will return proper authorization code regardless whether the exception is thrown or not.

This solution worked perfectly on every device but one. One particular
Samsung Galaxy was throwing UserRecoverableAuthException in loop, so the signing in screen was shown repeatedly, rendering use of the application impossible. It turned out, that “don”t keep activities” option was enabled in developer options in this device.

What is this function actually for?

If your device needs memory it destroys activities which are not visible. So you have to consider that your activity can be recreated any time. “Don”t keep activities” option is there to simulate your application when your device needs memory and destroys your backstack activities. But the order of destroying Activities is not random – the previous activity being not available when your app is in the foreground is the rarest of rare use cases, almost impossible on newer devices.

What happens when it occurs anyway?

First we should consider what startActivityForResult() function actually does. You use this function to launch an activity for which you would like a result when it finishes. When this activity exits, your onActivityResult() method will be called with the given requestCode.

So we send an intent to start Google-provided sign in Activity, result of which is sent back to our Activity. But, our Activity does not exist any more! It was killed, because it had not been in foreground.
Our Activity”s state will be restored, and UserRecoverableAuthException will be thrown again and again.
That is why method onActivityResult() will never be invoked.

Even though this situation is highly unlikely there”s an approach that you should always take when handling situations like this, because every Activity that is not on the foreground can be killed any time.

To handle signing into Google with “don”t keep Activities” option enabled, you can for example use boolean variable, which persists information whether current Activity was killed before, and if it was, you won”t call signing in function again and let the onActivityResult() function to be called and handle response. Here”s a code example:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mUserRecoverableThrown = savedInstanceState.getBoolean(RECOVERABLE_FLAG);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SAVED_PROGRESS, mSignInProgress);
outState.putBoolean(RECOVERABLE_FLAG, mUserRecoverableThrown);
}

And at the point where downloading token AsyncTask was executed, you must first check whether the Activity was not already killed with UserRecoverableAuthException thrown:

if (!mUserRecoverableThrown) {
getProfileInformation();
}

To sum up, always test your application with the option “don”t keep activities” – it should also work fine in a situation in which all the other activities have been killed.

It is worth to disable it again after testing – apparently many developers forget about this option, and some of the popular apps installed on your phone may not work properly.
So if your device suddenly starts behaving strangely (often during signing in to apps) – check if the option is enabled.

author: Iga Stępniak