Fragment testing, I'm missing something equivalent to ActivityScenarioRule - android-testing

I'm used to test my Fragments by launching the Activity containing it in an Espresso test. This has been working pretty nicely and I have been able to control the test environment/execution through some test rules (e.g. OkHttpIdlingResourceRule) that are added to ActivityScenarioRule (or IntentsTestRule) which is then used to launch the Activity.
Now that that I'm moving towards single Activity approach and utilizing also Navigation component I would like to start testing my Fragments in isolation. However, to be able to do that I would need similar capabilities on Fragment testing as do exists in Activity testing. ActivityScenarioRule implements TestRule but FragmentScenario does not and the is no FragmentScenarioRule.
Am I missing something?

It's actually pretty simple. When testing Activities I had a base test class containing the following:
#Rule #JvmField
val activityRule: IntentsTestRule<*>
#Rule #JvmField
val chain: RuleChain
init {
activityRule = IntentsTestRule(
activityClass,
true, // true if the Activity should be placed into "touch mode" when started
false) // launchActivity
var initChain = RuleChain.outerRule(firstRule)
.around(OkHttpIdlingResourceRule())
.around(activityRule)
for (rule in additionalTestRules) {
initChain = initChain.around(rule)
}
chain = initChain
}
In the test the Activity is launched with activityRule.launchActivity(null)
And now when testing Fragments this becomes:
#Rule
#JvmField
val chain: RuleChain
init {
var initChain = RuleChain.outerRule(firstRule)
.around(OkHttpIdlingResourceRule())
for (rule in additionalTestRules) {
initChain = initChain.around(rule)
}
chain = initChain
}
In the test Fragment is launched with launchFragmentInContainer()

Related

Jetpack Compose - UI Test Coil Fetched Image

I have a custom Image composable that uses Coil's rememberAsyncImagePainter.
However, I have another component that uses resources and has logic that is handled separately.
I successfully render the custom resource placeholder, however I'm not sure how I can write a test to check that the actual url image is loaded & visible.
Both the url image and the resource image have different testTags, however in the test, the node with the url image's tag never exists.
Does Coil have any solution to mock the ImageRequest.Builder so that I can guarantee that the URL image successfully loads?
I would prefer to not add any test-related code to the component itself, but if that's the only way, then I would prefer the component to be testable.
According to the official docs https://coil-kt.github.io/coil/image_loaders/#testing, you can create a FakeImageLoader class like this:
class FakeImageLoader(private val context: Context) : ImageLoader {
override val defaults = DefaultRequestOptions()
override val components = ComponentRegistry()
override val memoryCache: MemoryCache? get() = null
override val diskCache: DiskCache? get() = null
override fun enqueue(request: ImageRequest): Disposable {
// Always call onStart before onSuccess.
request.target?.onStart(request.placeholder)
val result = ColorDrawable(Color.BLACK)
request.target?.onSuccess(result)
return object : Disposable {
override val job = CompletableDeferred(newResult(request, result))
override val isDisposed get() = true
override fun dispose() {}
}
}
override suspend fun execute(request: ImageRequest): ImageResult {
return newResult(request, ColorDrawable(Color.BLACK))
}
private fun newResult(request: ImageRequest, drawable: Drawable): SuccessResult {
return SuccessResult(
drawable = drawable,
request = request,
dataSource = DataSource.MEMORY_CACHE
)
}
override fun newBuilder() = throw UnsupportedOperationException()
override fun shutdown() {}
}
And you UI test, you can write
#Test
fun testCustomImageComposable() {
Coil.setImageLoader(FakeCoilImageLoader())
setContent {
CutomImageComposable(...)
}
// ... assert image is displayed, etc.
}
This should guarantee the image to be shown every-time the test is executed.
Alternatively, you can mock ImageLoader and mimic the behavior above.
However, I would not recommend mocking ImageLoader, as we do not know whether rememberAsyncImagePainter uses all of the methods, but if you must mock it, it would be worth a try.

Apache Beam Stateful DoFn Periodically Output All K/V Pairs

I'm trying to aggregate (per key) a streaming data source in Apache Beam (via Scio) using a stateful DoFn (using #ProcessElement with #StateId ValueState elements). I thought this would be most appropriate for the problem I'm trying to solve. The requirements are:
for a given key, records are aggregated (essentially summed) across all time - I don't care about previously computed aggregates, just the most recent
keys may be evicted from the state (state.clear()) based on certain conditions that I control
Every 5 minutes, regardless if any new keys were seen, all keys that haven't been evicted from the state should be outputted
Given that this is a streaming pipeline and will be running indefinitely, using a combinePerKey over a global window with accumulating fired panes seems like it will continue to increase its memory footprint and the amount of data it needs to run over time, so I'd like to avoid it. Additionally, when testing this out, (maybe as expected) it simply appends the newly computed aggregates to the output along with the historical input, rather than using the latest value for each key.
My thought was that using a StatefulDoFn would simply allow me to output all of the global state up until now(), but it seems this isn't a trivial solution. I've seen hintings at using timers to artificially execute callbacks for this, as well as potentially using a slowly growing side input map (How to solve Duplicate values exception when I create PCollectionView<Map<String,String>>) and somehow flushing this, but this would essentially require iterating over all values in the map rather than joining on it.
I feel like I might be overlooking something simple to get this working. I'm relatively new to many concepts of windowing and timers in Beam, looking for any advice on how to solve this. Thanks!
You are right that Stateful DoFn should help you here. This is a basic sketch of what you can do. Note that this only outputs the sum without the key. It may not be exactly what you want, but it should help you move forward.
class CombiningEmittingFn extends DoFn<KV<Integer, Integer>, Integer> {
#TimerId("emitter")
private final TimerSpec emitterSpec = TimerSpecs.timer(TimeDomain.PROCESSING_TIME);
#StateId("done")
private final StateSpec<ValueState<Boolean>> doneState = StateSpecs.value();
#StateId("agg")
private final StateSpec<CombiningState<Integer, int[], Integer>>
aggSpec = StateSpecs.combining(
Sum.ofIntegers().getAccumulatorCoder(null, VarIntCoder.of()), Sum.ofIntegers());
#ProcessElement
public void processElement(ProcessContext c,
#StateId("agg") CombiningState<Integer, int[], Integer> aggState,
#StateId("done") ValueState<Boolean> doneState,
#TimerId("emitter") Timer emitterTimer) throws Exception {
if (SOME CONDITION) {
countValueState.clear();
doneState.write(true);
} else {
countValueState.addAccum(c.element().getValue());
emitterTimer.align(Duration.standardMinutes(5)).setRelative();
}
}
}
#OnTimer("emitter")
public void onEmit(
OnTimerContext context,
#StateId("agg") CombiningState<Integer, int[], Integer> aggState,
#StateId("done") ValueState<Boolean> doneState,
#TimerId("emitter") Timer emitterTimer) {
Boolean isDone = doneState.read();
if (isDone != null && isDone) {
return;
} else {
context.output(aggState.getAccum());
// Set the timer to emit again
emitterTimer.align(Duration.standardMinutes(5)).setRelative();
}
}
}
}
Happy to iterate with you on something that'll work.
#Pablo was indeed correct that a StatefulDoFn and timers are useful in this scenario. Here is the with code I was able to get working.
Stateful Do Fn
// DomainState is a custom case class I'm using
type DoFnT = DoFn[KV[String, DomainState], KV[String, DomainState]]
class StatefulDoFn extends DoFnT {
#StateId("key")
private val keySpec = StateSpecs.value[String]()
#StateId("domainState")
private val domainStateSpec = StateSpecs.value[DomainState]()
#TimerId("loopingTimer")
private val loopingTimer: TimerSpec = TimerSpecs.timer(TimeDomain.EVENT_TIME)
#ProcessElement
def process(
context: DoFnT#ProcessContext,
#StateId("key") stateKey: ValueState[String],
#StateId("domainState") stateValue: ValueState[DomainState],
#TimerId("loopingTimer") loopingTimer: Timer): Unit = {
... logic to create key/value from potentially null values
if (keepState(value)) {
loopingTimer.align(Duration.standardMinutes(5)).setRelative()
stateKey.write(key)
stateValue.write(value)
if (flushState(value)) {
context.output(KV.of(key, value))
}
} else {
stateValue.clear()
}
}
#OnTimer("loopingTimer")
def onLoopingTimer(
context: DoFnT#OnTimerContext,
#StateId("key") stateKey: ValueState[String],
#StateId("domainState") stateValue: ValueState[DomainState],
#TimerId("loopingTimer") loopingTimer: Timer): Unit = {
... logic to create key/value checking for nulls
if (keepState(value)) {
loopingTimer.align(Duration.standardMinutes(5)).setRelative()
if (flushState(value)) {
context.output(KV.of(key, value))
}
}
}
}
With pipeline
sc
.pubsubSubscription(...)
.keyBy(...)
.withGlobalWindow()
.applyPerKeyDoFn(new StatefulDoFn())
.withFixedWindows(
duration = Duration.standardMinutes(5),
options = WindowOptions(
accumulationMode = DISCARDING_FIRED_PANES,
trigger = AfterWatermark.pastEndOfWindow(),
allowedLateness = Duration.ZERO,
// Only take the latest per key during a window
timestampCombiner = TimestampCombiner.END_OF_WINDOW
))
.reduceByKey(mostRecentEvent())
.saveAsCustomOutput(TextIO.write()...)

How chain indefinite amount of flatMap operators in Reactor?

I have some initial state in my application and a few of policies that decorates this state with reactively fetched data (each of policy's Mono returns new instance of state with additional data). Eventually I get fully decorated state.
It basically looks like this:
public interface Policy {
Mono<State> apply(State currentState);
}
Usage for fixed number of policies would look like that:
Flux.just(baseState)
.flatMap(firstPolicy::apply)
.flatMap(secondPolicy::apply)
...
.subscribe();
It basically means that entry state for a Mono is result of accumulation of initial state and each of that Mono predecessors.
For my case policies number is not fixed and it comes from another layer of the application as a collection of objects that implements Policy interface.
Is there any way to achieve similar result as in the given code (with 2 flatMap), but for unknown number of policies? I have tried with Flux's reduce method, but it works only if policy returns value, not a Mono.
This seems difficult because you're streaming your baseState, then trying to do an arbitrary number of flatMap() calls on that. There's nothing inherently wrong with using a loop to achieve this, but I like to avoid that unless absolutely necessary, as it breaks the natural reactive flow of the code.
If you instead iterate and reduce the policies into a single policy, then the flatMap() call becomes trivial:
Flux.fromIterable(policies)
.reduce((p1,p2) -> s -> p1.apply(s).flatMap(p2::apply))
.flatMap(p -> p.apply(baseState))
.subscribe();
If you're able to edit your Policy interface, I'd strongly suggest adding a static combine() method to reference in your reduce() call to make that more readable:
interface Policy {
Mono<State> apply(State currentState);
public static Policy combine(Policy p1, Policy p2) {
return s -> p1.apply(s).flatMap(p2::apply);
}
}
The Flux then becomes much more descriptive and less verbose:
Flux.fromIterable(policies)
.reduce(Policy::combine)
.flatMap(p -> p.apply(baseState))
.subscribe();
As a complete demonstration, swapping out your State for a String to keep it shorter:
interface Policy {
Mono<String> apply(String currentState);
public static Policy combine(Policy p1, Policy p2) {
return s -> p1.apply(s).flatMap(p2::apply);
}
}
public static void main(String[] args) {
List<Policy> policies = new ArrayList<>();
policies.add(x -> Mono.just("blah " + x));
policies.add(x -> Mono.just("foo " + x));
String baseState = "bar";
Flux.fromIterable(policies)
.reduce(Policy::combine)
.flatMap(p -> p.apply(baseState))
.subscribe(System.out::println); //Prints "foo blah bar"
}
If I understand the problem correctly, then the most simple solution is to use a regular for loop:
Flux<State> flux = Flux.just(baseState);
for (Policy policy : policies)
{
flux = flux.flatMap(policy::apply);
}
flux.subscribe();
Also, note that if you have just a single baseSate you can use Mono instead of Flux.
UPDATE:
If you are concerned about breaking the flow, you can extract the for loop into a method and apply it via transform operator:
Flux.just(baseState)
.transform(this::applyPolicies)
.subscribe();
private Publisher<State> applyPolicies(Flux<State> originalFlux)
{
Flux<State> newFlux = originalFlux;
for (Policy policy : policies)
{
newFlux = newFlux.flatMap(policy::apply);
}
return newFlux;
}

F# XUnit test deadlocks when initializer has dependency

I am having problems with a test in a netcoreapp2.2 .net core test project.
Before the tests starts I need to fetch some data that will be shared between the tests.
However, when running the following test from command line it will hang.
Executing the test like this:
dotnet test --filter "Test async initialization"
The faulty code looks like this:
let c = new HttpClient (BaseAddress = (Uri "https://swapi.co/api/people/1/"))
let luke =
async {
return! c.GetStringAsync "" |> Async.AwaitTask
} |> Async.RunSynchronously
[<Fact>]
let ``Test async initialization`` () =
Assert.NotNull(luke)
While if I put the creation of the HttpClient inside the luke fetcher like this it works:
let luke =
let c = new HttpClient (BaseAddress = (Uri "https://swapi.co/api/people/1/"))
async {
return! c.GetStringAsync "" |> Async.AwaitTask
} |> Async.RunSynchronously
[<Fact>]
let ``Test async initialization`` () =
Assert.NotNull(luke)
This means I can't share the same HttpClient between different fetchers.
Anyone knows what is going on, and how to share the same client between multiple functions?
The problem is caused because the "initialization" code isn't really initialization code. Those are just two static fields that will be evaluated only when requested. If you debug the unit test you'll see that c and luke execute only when execution reaches the line
Assert.NotNull(luke)
If you use a decompiler like JustDecompile you'll see that the module's code is placed in a static class called Tests$ whose static constructor initializes its own c and luke properties. Test async initialization is placed in a Tests class with its own c and luke properties that delegate to the Tests$ class.
Long story sort, none of that "initialization" code runs until the value of luke is requested. I don't know why that ends up blocking the test, most likely there's a conflict with the test runner. It's enough that the initialization code doesn't run at initialization.
To make the initialization code run when it should, a "classic" test type can be used :
namespace MyTests
open System
open Xunit
open System.Net.Http
open Xunit.Abstractions
type Tests() =
static let c = new HttpClient (BaseAddress = (Uri "https://swapi.co/api/people/1/"))
static let luke =
async {
return! c.GetStringAsync "" |> Async.AwaitTask
} |> Async.RunSynchronously
static do
//Pity we can't actually print here
printfn "Even more initialization!"
[<Fact>]
let ``Test async initialization`` () =
Assert.NotNull(luke)
The static bindings in this case are executed before any of the tests, as they should, and the code doesn't block. This initialization will happen only once.
To capture output the test class constructor should accept an ITestOutputHelper parameter. That's easy to do now that we have a test class :
type Tests(output:ITestOutputHelper) =
...
[<Fact>]
let ``Test async initialization`` () =
Assert.NotNull(luke)
output.WriteLine "It worked!"
Per-test initialization should go in a do block :
type Tests(output:ITestOutputHelper) =
do
output.WriteLine "This prints before each test"

Using Vaadin components in a kotlin js project

This question is about a Kotlin JS project which uses the Kotlin Frontend Plugin.
I want to use some UI components from the Vaadin Components library.
I have two questions about this:
(1) What would be the best way to include web components in Kotlin JS
=> for my complete code, see the link to the source below. In summary the relevant details are:
build.gradle.kts
kotlinFrontend {
npm {
dependency("#vaadin/vaadin-grid")
}
}
vaadin.grid.Imports.kt
#file:JsModule("#vaadin/vaadin-grid")
#file:JsNonModule
package vaadin.grid
external class GridElement {
companion object
}
Why the companion object? I need it for the workaround (see below).
foo.kt
fun main() {
document.getElementById("container")!!.append {
vaadin_grid {
attributes["id"] = "grid"
}
}
initUI()
}
fun initUI() {
// Force the side-effects of the vaadin modules. Is there a better way?
console.log(GridElement)
val grid = document.querySelector("#grid") /* ?? as GridElement ?? */
}
The console.log is the ugly workaround trick I want to avoid. If I don't do anything with GridElement then it's just not included in my bundle.
The vaadin_grid DSL is defined as a custom kotlinx.html tag which is unrelated code.
(2) I want to keep my code as typed as possible to avoid asDynamic but when I cast the HTMLElement to a Vaadin Element I get ClassCastExceptions (because GridElement is undefined).
For example I want to write something like this:
val grid : GridElement = document.querySelector("#grid") as GridElement
grid.items = ... // vs grid.asDynamic().items which does work
Here is how I define the external GridElement
vaadin/button/Imports.kt
#file:JsModule("#vaadin/vaadin-grid")
#file:JsNonModule
package vaadin.grid
import org.w3c.dom.HTMLElement
abstract external class GridElement : HTMLElement {
var items: Array<*> = definedExternally
}
build/node_modules/#vaadin/vaadin-grid/src/vaadin-grid.js
...
customElements.define(GridElement.is, GridElement);
export { GridElement };
Source example
To run:
From the root of the git repo:
./gradlew 05-kt-frontend-vaadin:build && open 05-kt-frontend-vaadin/frontend.html
I found the answer(s)
For the first question
(1) What would be the best way to include web components in Kotlin JS
Instead of the console.log to trigger the side effects I use require(...)
external fun require(module: String): dynamic
fun main() {
require("#vaadin/vaadin-button")
require("#vaadin/vaadin-text-field")
require("#vaadin/vaadin-grid")
...
}
(credits to someone's answer on the kotlin-frontend-plugin list)
(2) I want to keep my code as typed as possible to avoid asDynamic
Instead of importing #vaadin/vaadin-grid I have to import the file which actually exposes the element. Then it seems to work and I can even add generics to my GridElement:
#file:JsModule("#vaadin/vaadin-grid/src/vaadin-grid")
#file:JsNonModule
package vaadin.grid
import org.w3c.dom.HTMLElement
abstract external class GridElement<T> : HTMLElement {
var items: Array<out T> = definedExternally
}
This way I was able to get rid of all the asDynamics
val firstNameField = document.querySelector("#firstName") as TextFieldElement?
val lastNameField = document.querySelector("#lastName") as TextFieldElement?
val addButton = document.querySelector("#addButton") as ButtonElement?
val grid = document.querySelector("#grid") as GridElement<Person>?
val initialPeople: Array<out Person> = emptyArray()
grid?.items = initialPeople
addButton?.addEventListener("click", {
// Read the new person's data
val person = Person(firstNameField?.value, lastNameField?.value)
// Add it to the items
if(grid != null){
val people = grid.items
grid.items = people.plus(person)
}
// Reset the form fields
firstNameField?.value = ""
lastNameField?.value = ""
})

Resources