LY Corporation Tech Blog

We are promoting the technology and development culture that supports the services of LY Corporation and LY Corporation Group (LINE Plus, LINE Taiwan and LINE Vietnam).

This post is also available in the following languages. Japanese

Improving code quality - Session 42: Theoretical testing

The original article was published on September 12, 2024.

Hello, I'm Munetoshi Ishikawa, a mobile client developer for the LINE messaging app.

This article is the latest installment of our weekly series "Improving code quality". For more information about the Weekly Report, please see the first article.

Theoretical testing

Let's assume that a service account has several types (FREE, PREMIUM, ...) defined as follows.

enum class AccountType {
    FREE,
    PREMIUM,
    BUSINESS,
    ULTIMATE
}

Here, we want to display different icons for each account type. If AccountType is an enumeration used only by the UI, it is an option to have the icon image as a property.

enum class AccountType(val iconImage: IconImage) {
    FREE(Icons.FREE_ACCOUNT_TYPE),
    PREMIUM(Icons.PREMIUM_ACCOUNT_TYPE),
    BUSINESS(Icons.BUSINESS_ACCOUNT_TYPE),
    ULTIMATE(Icons.ULTIMATE_ACCOUNT_TYPE)
}

However, this method is only effective if AccountType is used by specific modules or layers. If AccountType is used more broadly, making values for specific modules properties can lead to bloat.

In such cases, you can prepare a separate conversion mechanism to obtain the icon image from AccountType. The following code uses a Map for this conversion.

internal val ACCOUNT_TYPE_TO_ICON_MAP: Map<AccountType, IconImage> = mapOf(
    AccountType.FREE to Icons.FREE_ACCOUNT_TYPE,
    AccountType.PREMIUM to Icons.PREMIUM_ACCOUNT_TYPE,
    AccountType.BUSINESS to Icons.BUSINESS_ACCOUNT_TYPE,
    AccountType.ULTIMATE to Icons.ULTIMATE_ACCOUNT_TYPE
)

If a new account type is added to AccountType, ACCOUNT_TYPE_TO_ICON_MAP[accountType] might return null. In the previous report "Architecture" under construction, we explained that writing unit tests is one way to prevent implementation oversights when adding new elements. The following test fails if there is an element that returns null.

@Test
fun testAccountIconCompleteness() {
    for (type in AccountType.entries) {
        assertNotNull(
            ACCOUNT_TYPE_TO_ICON_MAP[type],
            "Unknown type: $type. Update `ACCOUNT_TYPE_TO_ICON_MAP` to ..."
        )
    }
}

Is there anything that should be improved in this conversion code or test?

In a nutshell

Consider using static verification instead of tests or runtime assertions.

Keywords: static analysis, test, runtime assertion

List of articles on techniques for improving code quality