Problem with unit testing of Billing Library 5 in Android - unit-testing

I'm using wrapper for BillingClient and after migration from Billing Library 4.0 to 5.0 there is an issue with unit testing of specific builder - BillingFlowParams.SubscriptionUpdateParams builder.
Specific wrapper's method that is having issue looks like this:
fun upgradeSubscription(sub: Purchase, product: Product, activity: Activity) {
val newOfferToken = product.productDetails.getOffer()?.offerToken ?: return
val updateParams = BillingFlowParams.SubscriptionUpdateParams.newBuilder()
.setOldPurchaseToken(sub.purchaseToken)
.setReplaceProrationMode(MODE)
.build()
val productDetailsParamsList = listOf(
ProductDetailsParams.newBuilder()
.setProductDetails(product.productDetails)
.setOfferToken(newOfferToken)
.build()
)
val billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(updateParams)
.build()
client.launchBillingFlow(activity, billingFlowParams)
}
and my test method looks like this:
#Test
fun `should test upgradeSubscription`() {
mockkStatic(BillingFlowParams::class)
val params = slot<BillingFlowParams>()
val activity = mockkClass(Activity::class)
val sub = getMockedPurchase()
billingWrapper.upgradeSubscription(sub, product, activity)
verify { client.launchBillingFlow(storeActivity, capture(params)) }
}
Test fails at .build() line of BillingFlowParams.SubscriptionUpdateParams's builder with such exception:
java.lang.IllegalArgumentException: Please provide Old SKU purchase
information(token/id) or original external transaction id, not both.
Meanwhile in real-life usage it works fine. So I'm wondering how to fix this problem with the unit test.
I've found the same question, but unfortunately it still has no answer and no solution.

Related

Unit tests for Ktor with KMongo

How to make unit tests in Ktor with KMongo? How to mock database and make test on it? Let's say that I made simplest API like this:
private val client = KMongo.createClient().coroutine
private val database = client.getDatabase("dbName")
val people = database.getCollection<Person>()
suspend fun addPerson(person: Person): Boolean =
people.insertOne(person).wasAcknowledged()
fun Route.addPersonRouting()
{
route("/add")
{
post {
if (addPerson(Person("Name", "Surname")))
{
call.respond(HttpStatusCode.OK, "ADDED")
}
else
{
call.respond(HttpStatusCode.OK, "NOT ADDED")
}
}
}
}
#Test
fun `add person successfully`() = withTestApplication(
{
install(ContentNegotiation){ json() }
routing { addPersonRouting() }
}
) {
val c = handleRequest(HttpMethod.Post, "/add")
assertEquals(HttpStatusCode.OK, c.response.status())
assertEquals("ADDED", c.response.content)
}
Now I can write a unit test but the problem is that database used for this test is not clean so before every test I have to clean it. I was thinking if there is any built-in database so the Test class can use it and with every run, it gives me a new clean DB. If it is possible I can modify Routing so it takes interface/database and in the application, I can pass normal database and to the tests, I can use test database. Probably something very similar is used in Android Room Room.inMemoryDatabaseBuilder.
Would be nice if someone would show me step by step solution how to do this test with the clean mocked database without needing to clean it every time before running the test.

JUnit 5 - Running Test Classes in Order to Preserve System State

I'm designing a platform which could be described as a trading platform for buying and selling collectibles, such as trading cards, stamps, coins, etc.
There are several processes which change the state of assets being traded through the platform; for example:
Advertising an asset for sale.
Receiving purchase offers for the asset.
Accepting a purchase offer.
Receiving payment.
For each of these processes, there is a unit of logic which performs each action, and are unit tested using JUnit 5; for example, testing that an asset can be advertised looks like this:
class AdvertiseAssetTests : TestBase() {
private lateinit var asset: Asset
override fun initialize() {
asset = systemUnderTest.advertise(EXAMPLE_TRADING_CARD)
}
#Test
fun `asset should be adverised with the correct status`() {
assertEquals(AssetStatus.ADVERTISED, asset.status)
}
}
In the above class, the final state of the system under test represents advertisement of a single asset against which assertions are made.
The next test would be to assert that offers can be created for the asset; for example:
class CreateAssetOfferTests : TestBase() {
private lateinit var asset: Asset
private lateinit var offer: Offer
override fun initialize() {
asset = systemUnderTest.advertise(EXAMPLE_TRADING_CARD)
offer = systemUnderTest.offer(asset, EXAMPLE_TRADING_CARD_OFFER)
}
#Test
fun `offer should be created for the specified asset`() {
assertEquals(asset.id, offer.assetId)
}
#Test
fun `offer should be created with the correct status`() {
assertEquals(OfferStatus.OFFERED, offer.status)
}
}
...and so on until the final test, which would be to test that the payment for an asset has been received; for example:
class ReceiveAssetPaymentTests : TestBase() {
private lateinit var asset: Asset
private lateinit var offer: Offer
override fun initialize() {
val advertisedAsset = systemUnderTest.advertise(EXAMPLE_TRADING_CARD)
val initialOffer = systemUnderTest.offer(advertisedAsset, EXAMPLE_TRADING_CARD_OFFER)
offer = systemUnderTest.acceptOffer(initialOffer)
asset = systemUnderTest.receivePayment(advertisedAsset)
}
#Test
fun `asset status should be payment received`() {
assertEquals(AssetStatus.PAYMENT_RECEIVED, asset.id)
}
}
As you can see, the further down the process that needs testing, the more test code needs to be added; for example, testing that a payment has been received requires the test setup to advertise, offer, accept an offer and receive payment before any assertions can be made.
This seems like a lot of duplication, since the former processes have already been tested. The problem is that the setup of each test takes longer and longer; for example:
Asset advertisement: 30sec
Offer creation: 30sec (advertisement) + 25sec (offer)
Offer acceptance: 30sec (advertisement) + 25sec (offer) + 25sec (acceptance)
Payment received: 30sec (advertisement) + 25sec (offer) + 25sec (acceptance) + 10sec (payment received)
Is there a way to organise the tests such that the state of the system can allow test classes to execute in order; for example, run AdvertiseAssetTests and then run CreateAssetOfferTests and finally run ReceiveAssetPaymentTests and maintain the state of the system after each test class run?
Is this possible?

Mock Elasticsearch Aggregation

I have a problem that should be relatively straight-forward but I find myself going into a deep rabbit hole
I would like to Unit Test my call to Elasticsearch - with the search request including the aggs. What is a good way to go about mocking the response?
Elasticsearch RestHighLevelClient is very complex ... one has to deal with the complex web of XContentType/XContentType parser call chains
Is there a simple way to mock the call? I have sample JSON responses that one would receive if we called ES from Kibana Devtools
private fun searchResponseFromContent(content: String): SearchResponse {
val xContentType = XContentType.JSON
val parser = xContentType.xContent().createParser(
NamedXContentRegistry.EMPTY, // this would not handle aggrgations
null,
content
)
return SearchResponse.fromXContent(parser)
}
Generally speaking do people just not test Elasticsearch calls in their unit test? There doesn't seem to be any good solutions to mock calls to ES
The answer is just simplify what RestHighLevelClient is doing internally:
private fun searchResponseFromContent(content: String): SearchResponse {
val xContentType = XContentType.JSON
val parser = xContentType.xContent().createParser(
NamedXContentRegistry(namedXContentRegistry()),
null,
content
)
return SearchResponse.fromXContent(parser)
}
private fun namedXContentRegistry(): List<NamedXContentRegistry.Entry> {
// add as needed from RestHighLevelClient:1748 on version 7.3.2
// static List<NamedXContentRegistry.Entry> getDefaultNamedXContents()
return listOf(
NamedXContentRegistry.Entry(Aggregation::class.java, ParseField(HistogramAggregationBuilder.NAME), ContextParser { p, c ->
ParsedHistogram.fromXContent(p, c as String)
})
)
}

Grails Spock unit test requires to mock transaction manager

In Grails 3.1.12, I want to unit test a service:
#Transactional
class PlanService {
List<Plan> getPlans(Map params) {
def currentUser = (User)springSecurityService.getCurrentUser()
return Plan.findAllByCompany(currentUser.employer, params)
}
}
Like this:
#TestFor(PlanService)
#Mock([Plan, User, Company])
class PlanServiceSpec extends Specification {
void "Retrieve plan from the current user"() {
setup:
// create and save entities here
when: "the plans are retrieved"
def params = null
def plans = service.getPlans(params)
then: "the result should only include plans associated to the current user's company"
plans.size() == 2
}
Running the test from the console:
grails> test-app my.PlanServiceSpec -unit
Fails with:
my.FundingPlanServiceSpec > Retrieve plan from the current user FAILED
java.lang.IllegalStateException at PlanServiceSpec.groovy:48
and in the test report (HTML):
java.lang.IllegalStateException: No transactionManager was specified.
Using #Transactional or #Rollback requires a valid configured transaction manager.
If you are running in a unit test ensure the test has been properly configured
and that you run the test suite not an individual test method.
Now if I comment out the #Transactional annotation in the service, the test passes, but that's not the intended implementation. I am able to work around the problem by mocking the transaction manager:
service.transactionManager = Mock(PlatformTransactionManager) {
getTransaction(_) >> Mock(TransactionStatus)
}
But this seems very awkward, if not wrong.
Is there some incantation I forgot to invoke?
EDIT: looks similar to an old bug, but it's been closed more than a year.
Have you tried what a comments says that fixes the problem? If not, try to annotate the test class with:
#TestMixin(DomainClassUnitTestMixin)
and then:
service.transactionManager = getTransactionManager()
Was getting the same error in grails 3.3.2 when trying to test transactional service.
adding DataTest interface solved the issue for me.
class HelloServiceSpec extends Specification implements ServiceUnitTest<HelloService>, DataTest {
}

how to mock/stub out a specific custom validator when doing unit testing in grails?

Lets say i have a domain A which has a custom validator for property P
static constraints = {
P validator: { val, obj ->
(A.executeQuery("SELECT COUNT(*) FROM A cei WHERE cei.event.id = ?", [val.id])[0] <= 1)
}
In the unit test how can i mock the P property of domain A so that i don't get the error i am getting when running unit test. The error i get when running unit test is shown below. The setup code instantiates domain A as shown below.
void setUp(){
inv = new A(P: rg).save(flush: true)
Error is
java.lang.UnsupportedOperationException: String-based queries like [executeQuery] are currently not supported in this implementation of GORM. Use criteria instead.
Note: These are fake code.
The best option for me, is encapsulate the inner code of the validator inside a service. And then, mock that service in your unit test.
But the error thrown is that executeQuery is not available, but criteria is.
So change your code for using a criteria instead.
P validator: { val, obj ->
A.withCriteria{
eq('event', Event.findById(val.id))
projections{
count('id')
}
}[0] < = 1
}