Good way to repeat a test, inserting an extra action? - catch-unit-test

I like the way Catch has nested hierarchies of tests, and it works through the combinations. It feels more natural than the setup/teardown of xUnit frameworks.
I now have a set of tests. What I want to do, about halfway down is insert a load/save serialization test, and then repeat all the tests below that point, first without the load/save, then again using the data it loaded from the serialization process. I.e. to prove that the load/save was correct.
I cannot get my head around if Catch has anything that can help with this? If it was phpUnit, I would be thinking about a string of #depends tests, and use a #dataProvider with a boolean input. A bit ugly.
(If that does not make sense, let me know, and I'll try to work out a minimal example)

The issue here is that Catch is designed to descend a tree-like organisation of tests and it automatically discovers all of the leaf-nodes of the structure and calls back into the test cases with untested code paths until they're all tested. The leaf nodes (tests, sections) are meant to be independent.
It sounds like you want to test a repository - something that can persist some data and then load it back in.
To repeat the exact same tests in two different scenarios (before serialisation, after serialisation) you'd need to put the same tests into some common place and call into that place. You can still use the same Catch macros in a non-test-case function, as long as you call it from a test case.
One possible way to do this is:
struct TestFixture {
Data data;
Repository repository;
TestFixture() : data(), instance() { }
};
void fillUpData(Data& data) {
// ...
}
void isDataAsExpected(Data& data) {
// Verify that 'data' is what we expect it to be, whether we
// loaded it or filled it up manually
SECTION("Data has ...) {
REQUIRE(data...);
}
}
TEST_CASE_METHOD(TestFixture, "Test with raw data") {
fillUpData(data);
isDataAsExpected(data);
REQUIRE(repository.save(data));
}
TEST_CASE_METHOD(TestFixture, "Operate on serialised data") {
REQUIRE(repository.load(data));
isDataAsExpected(_data);
}
One possible alternative is to supply your own main and then use command-line arguments to control whether/not the data is first serialised.
There's a third way I can think of that uses a non-quite-ready-yet feature of Catch - Generators:
TEST_CASE("...") {
using Catch::Generators;
int iteration(GENERATE(values(0, 1)));
const bool doSave(iteration == 0);
const bool doLoad(iteration == 1);
Repository repository;
Data data;
if (doLoad) {
REQUIRE(repository.load(data));
} else {
// fill up data
}
REQUIRE(..data..test..);
if (doSave) {
REQUIRE(repository.save(data));
}
}
The advantage of this method is you can see the flow and the test runs twice (for the two values) but the major disadvantage is that Generators are incompatible with SECTIONs and BDD-style features.

Related

Recommended data flow (import/export)

I have a flutter application which (simply put) list some data on various screens and can be modified. My current data approach works, but I feel it is not a best practice or optimal.
Currently, when a object is saved, it is converted to JSON (using dart:convert) and stored in a file on the device (using dart.io), overriding the file if it exist. Every screen that needs to display these objects reads the file to get the objects. Every time there is a change that needs to be saved, it exports everything (overwrites) again then imports it again to display it.
The reason I chose JSON over S is because I want to add a web portion later. Does this approach of reading/writing a best practice? I feel this much reading/writing of all the data for most screens could cause some performance issues.
Any advice is appreciated!
This is a possible way to keep data in-memory and write to disk when changes are made to your datamodel/settings.
I use RxDart myself. You don't need it per se, although it does make life easier. I'll be simplifying the examples below, so you get to know the concept and apply it to your own needs.
Let say you keep track of data in your settings class:
#JsonSerializable()
class Settings {
String someData1;
String someData2;
// json seriazable functions
}
You need a "handler"1 or something similar that manages changes made to your Settings and also to read/write data:
class SettingsHandler {
Settings _settings;
StreamController<Settings> _settingsController = BehaviorSubject<Settings>();
StreamController<String> _data1Controller = BehaviorSubject<String>();
StreamSink<String> get data1Input => _data1Controller.sink;
Observable<String> get data1Output => Observable(_data1Controller.stream);
Future<Settings> _readFromDisk() async {
// do your thing
}
Future<Settings> _writeToDisk(Settings settings) async {
// do your thing
}
Future<void> init() async {
// read from disk
_settings = await _readFromDisk();
_settingsController.sink.add(_settings);
// initialize data
data1Input.add(_settings.someData1);
data1Output
.skip(1) // we skip because we just added our initialization data above.
.listen((value) =>
// we must propagate through the update function
// otherwise nothing gets written to disk
update((settings) => settings.someData1 = value)
);
// when changes are made, it needs to notify this stream
// so everything can be written to disk
_settingsSaver.output
// save settings every 2.5 seconds when changes occur.
.debounceTime(const Duration(milliseconds: 2500))
// get the changes and write to disk
.listen((settings) => _writeToDisk(settings));
}
// this function is crucial as it allows changes to be made via the [adjustFunc]
// and then propagates this into the _settingsSaver stream.
void update(void Function(Settings input) adjustFunc) {
adjustFunc(_settings);
_settingsSaver.sink.add(_settings);
}
}
So now you can do something like
var handler = SettingsHandler();
await handler.init();
// this
handler.data1Input.add('NewData');
// or this
handler.update((settings) {
settings.someData1 = 'NewData';
});
Remember, this code only shows how the concept can work. You need to change it for your situation. You could also decide to not expose data1Input or the update(...) function, this is up to your own design.
1 I personally use BloC, your situation might require a different way.

can i conditionally "merge" a Single with an Observable?

i'm a RxJava newcomer, and i'm having some trouble wrapping my head around how to do the following.
i'm using Retrofit to invoke a network request that returns me a Single<Foo>, which is the type i ultimately want to consume via my Subscriber instance (call it SingleFooSubscriber)
Foo has an internal property items typed as List<String>.
if Foo.items is not empty, i would like to invoke separate, concurrent network requests for each of its values. (the actual results of these requests are inconsequential for SingleFooSubscriber as the results will be cached externally).
SingleFooSubscriber.onComplete() should be invoked only when Foo and all Foo.items have been fetched.
fetchFooCall
.subscribeOn(Schedulers.io())
// Approach #1...
// the idea here would be to "merge" the results of both streams into a single
// reactive type, but i'm not sure how this would work given that the item emissions
// could be far greater than one. using zip here i don't think it would every
// complete.
.flatMap { foo ->
if(foo.items.isNotEmpty()) {
Observable.zip(
Observable.fromIterable(foo.items),
Observable.just(foo),
{ source1, source2 ->
// hmmmm...
}
).toSingle()
} else {
Single.just(foo)
}
}
// ...or Approach #2...
// i think this would result in the streams for Foo and items being handled sequentially,
// which is not really ideal because
// 1) i think it would entail nested streams (i get the feeling i should be using flatMap
// instead)
// 2) and i'm not sure SingleFooSubscriber.onComplete() would depend on the completion of
// the stream for items
.doOnSuccess { data ->
if(data.items.isNotEmpty()) {
// hmmmm...
}
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> /* onSuccess() */ },
{ error -> /* onError() */ }
)
any thoughts on how to approach this would be greatly appreciated!
bonus points: in trying to come up with a solution to this, i've begun to question the decision to use the Single reactive type vs the Observable reactive type. most (all, except this one Foo.items case?) of my streams actually revolve around consuming a single instance of something, so i leaned toward Single to represent my streams as i thought it would add some semantic clarity around the code. anybody have any general guidance around when to use one vs the other?
You need to nest flatMaps and then convert back to Single:
retrofit.getMainObject()
.flatMap(v ->
Flowable.fromIterable(v.items)
.flatMap(w ->
retrofit.getItem(w.id).doOnNext(x -> w.property = x)
)
.ignoreElements()
.toSingle(v)
)

How can I chain promises in Swift inside a loop?

I'm building a Swift-based iOS application that uses PromiseKit to handle promises (although I'm open to switching promise library if it makes my problem easier to solve). There's a section of code designed to handle questions about overwriting files.
I have code that looks approximately like this:
let fileList = [list, of, files, could, be, any, length, ...]
for file in fileList {
if(fileAlreadyExists) {
let overwrite = Promise<Bool> { fulfill, reject in
let alert = UIAlertController(message: "Overwrite the file?")
alert.addAction(UIAlertAction(title: "Yes", handler: { action in
fulfill(true)
}
alert.addAction(UIAlertAction(title: "No", handler: { action in
fulfill(false)
}
} else {
fulfill(true)
}
}
overwrite.then { result -> Promise<Void> in
Promise<Void> { fulfill, reject in
if(result) {
// Overwrite the file
} else {
// Don't overwrite the file
}
}
}
However, this doesn't have the desired effect; the for loop "completes" as quickly as it takes to iterate over the list, which means that UIAlertController gets confused as it tries to overlay one question on another. What I want is for the promises to chain, so that only once the user has selected "Yes" or "No" (and the subsequent "overwrite" or "don't overwrite" code has executed) does the next iteration of the for loop happen. Essentially, I want the whole sequence to be sequential.
How can I chain these promises, considering the array is of indeterminate length? I feel as if I'm missing something obvious.
Edit: one of the answers below suggests recursion. That sounds reasonable, although I'm not sure about the implications for Swift's stack (this is inside an iOS app) if the list grows long. Ideal would be if there was a construct to do this more naturally by chaining onto the promise.
One approach: create a function that takes a list of the objects remaining. Use that as the callback in the then. In pseudocode:
function promptOverwrite(objects) {
if (objects is empty)
return
let overwrite = [...] // same as your code
overwrite.then {
do positive or negative action
// Recur on the rest of the objects
promptOverwrite(objects[1:])
}
}
Now, we might also be interested in doing this without recursion, just to avoid blowing the call stack if we have tens of thousands of promises. (Suppose that the promises don't require user interaction, and that they all resolve on the order of a few milliseconds, so that the scenario is realistic).
Note first that the callback—in the then—happens in the context of a closure, so it can't interact with any of the outer control flow, as expected. If we don't want to use recursion, we'll likely have to take advantage of some other native features.
The reason you're using promises in the first place, presumably, is that you (wisely) don't want to block the main thread. Consider, then, spinning off a second thread whose sole purpose is to orchestrate these promises. If your library allows to explicitly wait for a promise, just do something like
function promptOverwrite(objects) {
spawn an NSThread with target _promptOverwriteInternal(objects)
}
function _promptOverwriteInternal(objects) {
for obj in objects {
let overwrite = [...] // same as your code
overwrite.then(...) // same as your code
overwrite.awaitCompletion()
}
}
If your promises library doesn't let you do this, you could work around it by using a lock:
function _promptOverwriteInternal(objects) {
semaphore = createSemaphore(0)
for obj in objects {
let overwrite = [...] // same as your code
overwrite.then(...) // same as your code
overwrite.always {
semaphore.release(1)
}
semaphore.acquire(1) // wait for completion
}
}

Handling errors in an observable sequence using Rx

Is there a way to have an observable sequence to resume execution with the next element in the sequence if an error occurs?
From this post it looks like you need to specify a new observable sequence in Catch() to resume execution, but what if you needed to just continue processing with the next element in the sequence instead? Is there a way to achieve this?
UPDATE:
The scenario is as follows:
I have a bunch of elements that I need to process. The processing is made up of a bunch of steps. I have
decomposed the steps into tasks that I would like to compose.
I followed the guidelines for ToObservable() posted here
to convert by tasks to an observables for composition.
so basically I'm doing somethng like so -
foreach(element in collection)
{
var result = from aResult in DoAAsync(element).ToObservable()
from bResult in DoBAsync(aResult).ToObservable()
from cResult in DoCAsync(bResult).ToObservable()
select cResult;
result.subscribe( register on next and error handlers here)
}
or I could something like this:
var result =
from element in collection.ToObservable()
from aResult in DoAAsync(element).ToObservable()
from bResult in DoBAsync(aResult).ToObservable()
from cResult in DoCAsync(bResult).ToObservable()
select cResult;
What is the best way here to continue processing other elements even if let's say the processing of
one of the elements throws an exception. I would like to be able to log the error and move on ideally.
Both James & Richard made some good points, but I don't think they have given you the best method for solving your problem.
James suggested using .Catch(Observable.Never<Unit>()). He was wrong when he said that "will ... allow the stream to continue" because once you hit an exception the stream must end - that is what Richard pointed out when he mentioned the contract between observers and observables.
Also, using Never in this way will cause your observables to never complete.
The short answer is that .Catch(Observable.Empty<Unit>()) is the correct way to change a sequence from one that ends with an error to one that ends with completion.
You've hit on the right idea of using SelectMany to process each value of the source collection so that you can catch each exception, but you're left with a couple of issues.
You're using tasks (TPL) just to turn a function call into an observable. This forces your observable to use task pool threads which means that the SelectMany statement will likely produce values in a non-deterministic order.
Also you hide the actual calls to process your data making refactoring and maintenance harder.
I think you're better off creating an extension method that allows the exceptions to be skipped. Here it is:
public static IObservable<R> SelectAndSkipOnException<T, R>(
this IObservable<T> source, Func<T, R> selector)
{
return
source
.Select(t =>
Observable.Start(() => selector(t)).Catch(Observable.Empty<R>()))
.Merge();
}
With this method you can now simply do this:
var result =
collection.ToObservable()
.SelectAndSkipOnException(t =>
{
var a = DoA(t);
var b = DoB(a);
var c = DoC(b);
return c;
});
This code is much simpler, but it hides the exception(s). If you want to hang on to the exceptions while letting your sequence continue then you need to do some extra funkiness. Adding a couple of overloads to the Materialize extension method works to keep the errors.
public static IObservable<Notification<R>> Materialize<T, R>(
this IObservable<T> source, Func<T, R> selector)
{
return source.Select(t => Notification.CreateOnNext(t)).Materialize(selector);
}
public static IObservable<Notification<R>> Materialize<T, R>(
this IObservable<Notification<T>> source, Func<T, R> selector)
{
Func<Notification<T>, Notification<R>> f = nt =>
{
if (nt.Kind == NotificationKind.OnNext)
{
try
{
return Notification.CreateOnNext<R>(selector(nt.Value));
}
catch (Exception ex)
{
ex.Data["Value"] = nt.Value;
ex.Data["Selector"] = selector;
return Notification.CreateOnError<R>(ex);
}
}
else
{
if (nt.Kind == NotificationKind.OnError)
{
return Notification.CreateOnError<R>(nt.Exception);
}
else
{
return Notification.CreateOnCompleted<R>();
}
}
};
return source.Select(nt => f(nt));
}
These methods allow you to write this:
var result =
collection
.ToObservable()
.Materialize(t =>
{
var a = DoA(t);
var b = DoB(a);
var c = DoC(b);
return c;
})
.Do(nt =>
{
if (nt.Kind == NotificationKind.OnError)
{
/* Process the error in `nt.Exception` */
}
})
.Where(nt => nt.Kind != NotificationKind.OnError)
.Dematerialize();
You can even chain these Materialize methods and use ex.Data["Value"] & ex.Data["Selector"] to get the value and selector function that threw the error out.
I hope this helps.
The contract between IObservable and IObserver is OnNext*(OnCompelted|OnError)? which is upheld by all operators, even if not by the source.
Your only choice is to re-subscribe to the source using Retry, but if the source returns the IObservable instance for every description you won't see any new values.
Could you supply more information on your scenario? Maybe there is another way of looking at it.
Edit: Based on your updated feedback, it sounds like you just need Catch:
var result =
from element in collection.ToObservable()
from aResult in DoAAsync(element).ToObservable().Log().Catch(Observable.Empty<TA>())
from bResult in DoBAsync(aResult).ToObservable().Log().Catch(Observable.Empty<TB>())
from cResult in DoCAsync(bResult).ToObservable().Log().Catch(Observable.Empty<TC>())
select cResult;
This replaces an error with an Empty which would not trigger the next sequence (since it uses SelectMany under the hood.

Entity Framework and Nested Lambda Expressions

I've just started using Lambda expressions, and really like the shortcut. I also like the fact that I have scope within the lambda of the encompassing method. One thing I am having trouble with is nesting lambdas. Here is what I am trying to do:
public void DoSomeWork()
{
MyContext context = new MyDomainContext();
context.GetDocumentTypeCount(ci.CustomerId, io =>
{
if (io.HasError)
{
// Handle error
}
// Do some work here
// ...
// make DB call to get data
EntityQuery<AppliedGlobalFilter> query =
from a in context.GetAppliedGlobalFiltersQuery()
where a.CustomerId == ci.CustomerId && a.FilterId == 1
select a;
context.Load<AppliedGlobalFilter>(query, lo =>
{
if (lo.HasError)
{
}
**// Do more work in this nested lambda.
// Get compile time error here**
}
}, null);
}, null);
}
The second lambda is where I get the following compile time error:
Cannot convert Lambda expression to type 'System.ServiceModel.DomainService.Client.LoadBehavior' because it is not a delegate type
The compiler is choosing the wrong overload for the Load method even though I am using the same override I did in the previous Lambda.
Is this because I am trying to nest? Or do I have something else wrong?
Thanks,
-Scott
Found the problem as described in my comment above. I'll head back to work now - red face and all....
I realize this is not the answer you want, but I suggest caution about lengthy and/or nested lambdas. They work, but they often make code harder to read / maintain by other developers. I try to limit my lambdas in length to three statements, with no nesting.