Unit Testing Exceptions in Dart/Flutter - dart

I'm trying to unit test using the given code.
test('Given Employee When employeeName less than 5 then throws Exception', () async {
final employee = EmployeesCompanion.insert(
employeeName: 'Tony',
employeeCode: 'HR-121',
address: '5th Floor, Park Avenue',
contact: '1234567890',
hiringDate: DateTime.now());
expectLater(await employeesDB.createEmployee(employee),
throwsA((f) => f.toString().contains('employeeName: Must at least be 5 characters long')));
});
My unit test includes a predicate throwsA((f) => f.toString().contains('employeeName: Must at least be 5 characters long')) as given but dart fails this test with the exception:
package:moor/src/runtime/data_verification.dart 74:5 VerificationContext.throwIfInvalid
package:moor/src/runtime/query_builder/statements/insert.dart 197:51 InsertStatement._validateIntegrity
package:moor/src/runtime/query_builder/statements/insert.dart 96:5 InsertStatement.createContext
package:moor/src/runtime/query_builder/statements/insert.dart 64:17 InsertStatement.insert
package:moor_ex/src/db/employees.dart 79:76 EmployeesDB.createEmployee
test\employee_dept_test.dart 28:35 main.<fn>
InvalidDataException: Sorry, EmployeesCompanion(id: Value.absent(), employeeName: Value(Tony), employeeCode: Value(HR-121), address: Value(5th Floor, Park Avenue), contact: Value(1234567890), hiringDate: Value(2020-08-18 13:26:34.435349)) cannot be used for that because:
• employeeName: Must at least be 5 characters long.
So what's the correct way to write this test in Dart?

Try removing the await, or move it outside/before the expectLater.
The only difference between expect and expectLater is that the latter returns a future, but you ignore that future.
The expect/throwsA needs either a closure or a Future as the actual value, but you await that future so the await ... expression throws before you even call expectLater. Remove the await so you pass the Future to expectLater and the throwsA will then catch and match the error of that future.
So, I recommend changing it to:
await expectLater(employeesDB.createEmployee(employee), ...

Related

Pausing a stream in dart null safety

I'm converting dart code to nnbd.
I have the following code.
var subscription = response.listen(
(newBytes) async {
/// if we don't pause we get overlapping calls from listen
/// which causes the [writeFrom] to fail as you can't
/// do overlapping io.
subscription.pause();
/// we have new data to save.
await raf.writeFrom(newBytes);
subscription.resume();
});
The problem is I get the following error:
The non-nullable local variable 'subscription' must be assigned before it can be used.
Try giving it an initializer expression, or ensure that it's assigned on every execution path.
I've had a similar problem solved here:
dart - correct coding pattern for subscription when using null saftey?
which was answered by #lrn
However the pattern solution pattern doesn't seem to work in this case.
raf.writeFrom is an async operation so I must use an 'async' method which means I can't use the 'forEach' solution as again I don't have access to the subscription object.
If you really want to use listen, I'd do it as:
var subscription = response.listen(null);
subscription.onData((newBytes) async {
subscription.pause();
await raf.writeFrom(newBytes);
subscription.resume();
});
or, without the async:
var subscription = response.listen(null);
subscription.onData((newBytes) {
subscription.pause(raf.writeFrom(newBytes));
});
which will pause the subscription until the future returned by raf.writeFrom completes (it shouldn't complete with an error, though).
If using listen is not a priority, I'd prefer to use an asynchronous for-in like:
await for (var newBytes in subscription) {
await raf.writeFrom(newBytes);
}
which automatically pauses the implicit subscription at the await and resumes it when you get back to the loop.
Both with stream.listen and the StreamController constructor, null safety has made it nicer to create them first without callbacks, and then add the callbacks later, if the callback needs to refer to the subscription/controller.
(That's basically the same nswer as in the linked question, only applied to onData instead of onDone. You have to pass a default onData argument to listen, but it can be null precisely to support this approach.)
I don't think your code, as written, was legal before null-safety either; you can't reference a variable (subscription) before it's declared, and the declaration isn't complete until after the expression you initialize it with (response.listen(...)) is evaluated. You will need to separate the declaration from the initialization to break the circular dependency:
StreamSubscription<List<int>> subscription;
subscription = response.listen(...);

"Guid should contain 32 digits" serilog error with sql server sink

I am getting this error occasionally with the MSSQLServer sink. I can't see what's wrong with this guid. Any ideas? I've verified in every place I can find the data type of the source guid is "Guid" not a string. I'm just a bit mystified.
Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).Couldn't store <"7526f485-ec2d-4ec8-bd73-12a7d1c49a5d"> in UserId Column. Expected type is Guid.
The guid in this example is:
7526f485-ec2d-4ec8-bd73-12a7d1c49a5d
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
seems to match the template to me?
Further details:
This is an occasional issue, but when it arises it arises a lot. It seems to be tied to specific Guids. Most Guids are fine, but a small subset have this issue. Our app logs thousands of messages a day, but these messages are not logged (because of the issue) so it is difficult for me to track down exactly where the specific logs that are causing this error come from. However, we use a centralized logging method that is run something like this. This test passes for me, but it mirrors the setup and code we use for logging generally, which normally succeeds. As I said, this is an intermittent issue:
[Fact]
public void Foobar()
{
// arrange
var columnOptions = new ColumnOptions
{
AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn {DataType = SqlDbType.UniqueIdentifier, ColumnName = "UserId"},
},
};
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.LogEvent);
columnOptions.Properties.ExcludeAdditionalProperties = true;
var badGuid = new Guid("7526f485-ec2d-4ec8-bd73-12a7d1c49a5d");
var connectionString = "Server=(localdb)\\MSSQLLocalDB;Database=SomeDb;Trusted_Connection=True;MultipleActiveResultSets=true";
var logConfiguration = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.MSSqlServer(connectionString, "Logs",
restrictedToMinimumLevel: LogEventLevel.Information, autoCreateSqlTable: false,
columnOptions: columnOptions)
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information);
Log.Logger = logConfiguration.CreateLogger();
// Suspect the issue is with this line
LogContext.PushProperty("UserId", badGuid);
// Best practice would be to do something like this:
// using (LogContext.PushProperty("UserId", badGuid)
// {
Log.Logger.Information(new FormatException("Foobar"),"This is a test");
// }
Log.CloseAndFlush();
}
One thing I have noticed since constructing this test code is that the "PushProperty" for the UserId property is not captured and disposed. Since behaviour is "undefined" in this case, I am inclined to fix it anyway and see if the problem goes away.
full stack:
2020-04-20T08:38:17.5145399Z Exception while emitting periodic batch from Serilog.Sinks.MSSqlServer.MSSqlServerSink: System.ArgumentException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).Couldn't store <"7526f485-ec2d-4ec8-bd73-12a7d1c49a5d"> in UserId Column. Expected type is Guid.
---> System.FormatException: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
at System.Guid.GuidResult.SetFailure(Boolean overflow, String failureMessageID)
at System.Guid.TryParseExactD(ReadOnlySpan`1 guidString, GuidResult& result)
at System.Guid.TryParseGuid(ReadOnlySpan`1 guidString, GuidResult& result)
at System.Guid..ctor(String g)
at System.Data.Common.ObjectStorage.Set(Int32 recordNo, Object value)
at System.Data.DataColumn.set_Item(Int32 record, Object value)
--- End of inner exception stack trace ---
at System.Data.DataColumn.set_Item(Int32 record, Object value)
at System.Data.DataRow.set_Item(DataColumn column, Object value)
at Serilog.Sinks.MSSqlServer.MSSqlServerSink.FillDataTable(IEnumerable`1 events)
at Serilog.Sinks.MSSqlServer.MSSqlServerSink.EmitBatchAsync(IEnumerable`1 events)
at Serilog.Sinks.PeriodicBatching.PeriodicBatchingSink.OnTick()
RESOLUTION
This issue was caused because someone created a log message with a placeholder that had the same name as our custom data column, but was passing in a string version of a guid instead of one typed as a guid.
Very simple example:
var badGuid = "7526f485-ec2d-4ec8-bd73-12a7d1c49a5d";
var badGuidConverted = Guid.Parse(badGuid); // just proving the guid is actually valid.
var goodGuid = Guid.NewGuid();
using (LogContext.PushProperty("UserId",goodGuid))
{
Log.Logger.Information("This is a problem with my other user {userid} that will crash serilog. This message will never end up in the database.", badGuid);
}
The quick fix is to edit the message template to change the placeholder from {userid} to something else.
Since our code was centralized around the place where the PushProperty occurs, I put some checks in there to monitor for this and throw a more useful error message in the future when someone does this again.
I don't see anything obvious in the specific code above that would cause the issue. The fact that you call PushProperty before setting up Serilog would be something I would change (i.e. set up Serilog first, then call PushProperty) but that doesn't seem to be the root cause of the issue you're having.
My guess, is that you have some code paths that are logging the UserId as a string, instead of a Guid. Serilog is expecting a Guid value type, so if you give it a string representation of a Guid it won't work and will give you that type of exception.
Maybe somewhere in the codebase you're calling .ToString on the UserId before logging? Or perhaps using string interpolation e.g. Log.Information("User is {UserId}", $"{UserId}");?
For example:
var badGuid = "7526f485-ec2d- 4ec8-bd73-12a7d1c49a5d";
LogContext.PushProperty("UserId", badGuid);
Log.Information(new FormatException("Foobar"), "This is a test");
Or even just logging a message with the UserId property directly:
var badGuid = "7526f485-ec2d-4ec8-bd73-12a7d1c49a5d";
Log.Information("The {UserId} is doing work", badGuid);
Both snippets above would throw the same exception you're having, because they use string values rather than real Guid values.

What is the purpose of `FutureOr`?

Dart offers a FutureOr class, that allows writing:
FutureOr<int> future;
future = 42; // valid
future = Future.value(42); // also valid
I would assume that FutureOr would be useful to remove the unnecessary delay caused by the event loop if the value can be read synchronously.
But that doesn't seem to be the case, as showcased by:
import 'dart:async';
void main() async {
print('START');
futureOrExample();
print('END');
}
void futureOrExample() async {
FutureOr<int> futureOr = 42;
print('before await');
await futureOr;
print('end await');
}
which prints:
START
before await
END
end await
when I would expect:
START
before await
end await
END
In that case, why does FutureOr (or more generally await 42) work this way?
Similarly, what's the purpose of FutureOr in that situation since it produces the same result as Future?
I know that I could use SynchronousFuture to achieve the desired result, but I'm just trying to understand what's the use of FutureOr.
The use of FutureOr, as introduced with Dart 2, is to allow you to provide either a value or a future at a point where the existing Dart 1 API allowed the same thing for convenience, only in a way that can be statically typed.
The canonical example is Future.then. The signature on Future<T> is Future<R> then<R>(FutureOr<R> action(T value), {Function onError}).
The idea is that you can have an action on the future's value which is either synchronous or asynchronous. Originally there was a then function which took a synchronous callback and a chain function which took an asynchronous callback, but that was highly annoying to work with, and in good Dart 1 style, the API was reduced to one then method which took a function returning dynamic, and then it checked whether it was a future or not.
In Dart 1 it was easy to allow you to return either a value or a future. Dart 2 was not as lenient, so the FutureOr type was introduced to allow the existing API to keep working. If we had written the API from scratch, we'd probably have done something else, but migrating the existing asynchronous code base to something completely different was not an option, so the FutureOr type was introduced as a type-level hack.
The await operation was also originally defined to work on any object, long before FutureOr existed. For consistency and smaller code, an await e where e evaluated to a non-future would wrap that value in a future and await that. It means that there is only one quick and reusable check on a value (is it a future, if not wrap it), and then the remaining code is the same. There is only one code-path.
If the await worked synchronously on non-Future values, there would have to be a synchronous code path running through the await, as well as an asynchronous path waiting for a future. That would potentially double the code size, for example when compiling to JavaScript (or worse, if there were more awaits in the same control flow, you could get exponential blow-up for a naive implementation). Even if you avoided that by just calling the continuation function synchronously, it would likely be confusing to some readers that an await would not introduce an asynchronous gap. A mistake around that can cause race conditions or things happening in the wrong order.
So, the original design, predating FutureOr, was to make all await operations actually wait.
The introduction of FutureOr did not change this reasoning, and even if it did, it would now be a breaking change to not wait in places where people expect their code to actually give time for other microtasks to run.
The await keyword always lock the function execution.
Writing:
await 42
Is equivalent to:
await Future.value(42)
The reason being:
This is how await works in Javascript
it makes the behavior of await consistent.
Now, what's the purpose of FutureOr then?
FutureOr was never intended as a way to potentially make await synchronous.
Instead, it is an implementation detail of Future.
Without FutureOr, writing the following would not compile:
Future(() {
return 42; // compile error, not a Future
});
Future<int> future;
future.then((value) {
return value * 2; // compile error, not a Future
});
Instead, we would have to wrap all values in a Future.value like so:
Future<int> future;
future.then((value) {
return Future.value(value * 2);
});
for those who are still confused
I found good explanation https://itnext.io/what-is-futureor-in-dart-flutter-681091162c57 without diving into details
This piece of code could explain the target and real use cases of FutureOr
abstract class IDBService {
FutureOr<String> fetch();
}
class FirebaseRemoteService extends IDBService {
#override
Future<String> fetch() async => await 'data';
}
class LocalHiveDbService extends IDBService {
#override
String fetch() => 'data';
}
so in implementations of IDBService
the return type can be Future or String at the same time now!
Coming late to the discussion.
Updating my Dart comprehension - pardon my C++/JS -ish approach.
Seems like this would be useful for singleton initiation. Consider following:
import 'dart:async';
class AClass {
static String _info = '';
static FutureOr<String> get info async {
if (_info.isEmpty) {
print('--> is empty...');
_info = await Future.delayed(Duration(seconds:2),
() => "I'm alive!!");
}
else {
print('--> not empty');
}
return _info;
}
}
Future<void> main() async {
String info = await AClass.info;
print('Fist call: ' + info);
info = await AClass.info;
print('Second call: ' + info);
}
It works as expected - in either case, whether the _info member has been instantiated or not, the getter returns a valid string.
It works fine if I just use a Future<String> specifier in the getter, too. The current implementation makes FutureOr seem mostly like a self-documentation exercise (can return a Future<String> or a String...)
But, even if await currently always locks the execution, a future update may allow it to work as expected, in which case using the FutureOr construct would anticipate updates.
(Aside: I imagine this example could be condensed using an Optional wrapping the _info member, but that's a different exercise...)
I needed to use FutureOr today. I wanted to call a function that might be asynchronously (not always).
String callbackOne() => "hello";
Future<String> callbackTwo() async => (await Future.delayed(Duration(seconds: 1),() => "This is a sentence"));
Problem
I can do getLengthOfResult(callbackOne), but not getLengthOfResult(callbackTwo). Conversely, if accept an async callback, I can't use the sync callback.
DartPad
Future<int> getLengthOfResult(String Function() callback) async {
return callback().length;
}
Solution
DartPad
Future<int> getLengthOfResult(FutureOr<String> Function() callback) async {
// I can await on callbackOne, even though it returns a String.
final result = await callback();
return result.length;
}
main() {
getLengthOfResult(callbackOne);
getLengthOfResult(callbackTwo);
}

Why an explicit ".cast<>()" function in Dart instead of "as <>"

In my question Dart 2.X List.cast() does not compose the answer requires converting a List<dynamic> to a List<String> as such:
List<String> ls = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();
My experience from other languages had me write this first:
List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();
Note that this compiles but fails at runtime in Dart 2.
Why does Dart typecasting for a List require a function as List).cast<String>() as opposed to simply using the Dart as "typecast operator" such as as List<String>?
---- Edit ----
I am using the most recent Dart 2.0.0-dev.43.0 and get inconsistent runtime behavior with as typecasts/assertions. Isn't the .cast<>() function creating a new iterable the same as a .map()? Changing my code to this works:
List<String> ls = (json['data'] as List).map((s) => (s as String).toUpperCase()).toList();
This seems to take advantage that the first cast to List is a List<dynamic>. Thus the .map function parameter is also a dynamic.
My second example above with to as List<String> works in some places in our code but not others. Note that IntelliJ correctly infers the types in all of the above examples - it's the runtime where the failure happens. I'm guessing that the inconsistent behavior is due to Dart 2.x being still in development.
---- 2nd Edit ----
Here are my test cases that I have in one of my class constructors:
Map<String, dynamic> json = { "data": ["a", "b", "c"] };
//List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();
// Sometimes works - sometimes gives "Ignoring cast fail from JSArray to List<String>" at runtime!!!
List<String> wonky = (json["data"] as List<String>).map( (s) => s.toUpperCase() ).toList();
print("Wonky $wonky");
List<String> fix1 = (json["data"] as List).cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> fix2 = (json["data"] as List).map( (s) => (s as String).toUpperCase() ).toList();
List<String> explicit2 = (json["data"] as List<dynamic>).map( (dynamic s) => (s as String).toUpperCase() ).toList();
// From accepted answer of the linked question - compile error because .cast() doesn't take parameters
// error: Too many positional arguments: 0 expected, but 1 found.
//List<String> notBroken = (json['data'] as List).cast<String>((s) => s.toUpperCase()).toList();
List<String> notBrokenFixed = (json['data'] as List<String>).cast<String>().map((String s) => s.toUpperCase()).toList();
The problem is the warning Ignoring cast fail from JSArray to List<String> sometimes given by the wonky assignment. When I say sometimes it's because it changes unpredictably as I make changes to the main application that uses the library that contains this code - without making changes to this class or even the library.
At the time I wrote the first edit above, wonky wasn't working. I just tried it again now and it's working. I have not changed any code in this library - I have been working in the main application which has a dependency on this code's library.
Some background, this is a multi-library project being converted from Angular/Typescript. These test cases are based on the processing we do to deserialize JSON into Dart classes. We map JSON (dynamic) strings into various data structures such as enums, Option<> and Either<> (from dartz) using class constructor initializers.
A couple weeks ago the runtime warning started happening I believe because of Breaking Change: --preview-dart-2 turned on by default.
I understand that this warning will soon be an error. So I traced the warning back to these conversions that map from JSON dynamic data (Yes, dynamic data is an edge case in Dart but it's what dart:convert provides).
We are developing on Mac using DDC with the most recent Dart 2.0.0-dev.43.0, angular 5.0.0-alpha+8, build_runner 0.8.0, IntelliJ 2018.1 and running on Chrome 65.0.3325.181.
---- Final Edit ----
There is an instability in the current development build/runtime that is behind this issue. No, I don't have a reproducible example. Changing and rebuilding our main app will cause this code in an unmodified library dependency to sometimes give the runtime warning Ignoring cast fail from JSArray to List<String>.
The suspect code from the original part of this question (also wonky above)
List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();
casts the dynamic JSON data to a List<String>. The types are fully constrained and the Dart analyzer/IntelliJ infers s to be Static type: String.
The runtime warning that sometimes occurs and related answers to use .cast() is what led to this question. At this time I'll believe the analyzer and ignore the runtime warning.
In Dart 2 generic types are reified.
as ... is more like an assertion, if the values type doesn't match as causes a runtime exception.
cast<T>() is a method introduced in Dart 2 on Iterable that actually creates a new iterable of type Iterable<T> (or in your case the subclass List<T>) filled with the values of the original interable.
Update
You can use print('wonky: ${wonky.runtimeType}'); to see what the actual type is.
If the type matches your requirement, you can use as to communicate it to the analyzer that it's safe to assume this type.
If the type doesn't match, for example because it is List instead of List<String>, then you can use .cast<String>() to actually make it a List<String>.
List<String> broken = (json['data'] as List)
.cast<String>((s) => s.toUpperCase()).toList();
Here you seem to try to use cast for casting and mapping, but that is not valid.
map() can do both though
List<String> notBroken = (json['data'] as List)
.map<String>((s) => s.toUpperCase()).toList();

In Dart, if I use Future.wait with a list of Futures and an error is thrown on one of the Futures, what happens to the other Futures?

If I have the following code:
Future<int> a = new Future(() { print('a'); return 1; });
Future<int> b = new Future.error('Error!');
Future<int> c = new Future(() { print('c'); return 3; });
Future.wait([a, b, c])
.then((List<int> values) => print(values))
.catchError((e) => print(e));
What is printed?
Does the error caught by catchError have any idea of what Future died? Or which Futures remain?
Determining what is printed is pretty easy by running it (do you not have a Dart VM handy?):
a
c
AsyncError: 'Error!'
The documentation for Future.wait() says:
Wait for all the given futures to complete and collect their values.
Returns a future which will complete once all the futures in a list are complete. If any of the futures in the list completes with an error, the resulting future also completes with an error. Otherwise the value of the returned future will be a list of all the values that were produced.
e.error is the value that was created with ('Error!', in this case), so by setting that to something indicating which Future has the error, you can see which Future "died". Of course, if you're not the one creating the error, that doesn't help.
In general, I think you use Future.wait() on a List like this when an error in any of them would be handled similarly. Needing to know which one failed might be a sign that you don't want to wait() on all of them together.
Update: I just realized the question title asks a question not asked in the question body: What happens to the other Futures?
The other Futures keep running. The Future returned by wait() throws whichever error it receives as soon as it receives it, so all return values are lost, and if any of the other Futures throws an error, it isn't caught, but each Future still runs until returning or throwing an error.
AFAIK, you won't know precisely which Future generated the error. But, you can query the error object and get some useful information:
Future.wait([a, b, c])
.then((List<int> values) => print(values))
.catchError((e) {
print(e.error);
print(e.stackTrace);
print(e.runtimeType);
print(e.error.runtimeType);
});
Running this prints:
a
c
Error!
null
AsyncError
String
This tells us that the original error was a String. I don't know how useful that is to you, but its something.

Resources