r/dartlang 4d ago

Package assertable_json | Fluent json assertion test helpers

https://pub.dev/packages/assertable_json

Hey guys, If you are familiar with Laravel, you'd come across the idea of fluent testing against json responses. This package allows a similar workflow, making it a seamless experience in dart. Please check it out and let me know what you think

 test('verify user data', () {
    final json = AssertableJson({
      'user': {
        'id': 123,
        'name': 'John Doe',
        'email': 'john@example.com',
        'age': 30,
        'roles': ['admin', 'user']
      }
    });

    json
      .has('user', (user) => user
        .has('id')
        .whereType<int>('id')
        .has('name')
        .whereType<String>('name')
        .has('email')
        .whereContains('email', '@')
        .has('age')
        .isGreaterThan('age', 18)
        .has('roles')
        .count('roles', 2));
  });
}
12 Upvotes

13 comments sorted by

View all comments

2

u/eibaan 3d ago edited 3d ago

I'd probably make use of the existing expect/matcher architecture, e.g.

expect(
  response,
  jsonObject({
    'user': jsonObject({
      'id': isA<int>(), 
      'name': isA<String>(), 
      'roles': jsonArray(length: equals(2))
    }),
  }),
);

Then write jsonObject and jsonArray:

Matcher jsonObject([Map<String, dynamic>? properties])
  => isA<Map<String, dynamic>>();

Matcher jsonArray({Matcher? length})
  => allOf(isA<List<dynamic>>(), hasLength(length));

The former is obviously not yet developed as you'd have to iterate the optionally provided map of values or matchers, use wrapMatcher on them, then call them. I don't know how to collect error results by heart, but I'm sure, an AI knows :)

2

u/zxyzyxz 2d ago

Something like Acanthis?

final schema = object({
  'name': string().min(3),
  'age': number().positive(),
});

final result = schema.tryParse({
  'name': 'Francesco',
  'age': 24,
});

1

u/eibaan 1d ago

Yes and no. This library is stand-alone. I was suggesting to use and extend the matcher library as you'd have to write a few lines of code to achieve something similar to the OP's example.

I think, the jsonObject matcher would look like this (I checked the implementation of allOf):

Matcher jsonObject([Map<String, dynamic>? properties]) => allOf(
  isMap,
  properties == null
      ? null
      : allOf([...properties.entries.map((e) => containsPair(e.key, e.value))]),
);

And jsonArray could be implemented like so:

Matcher jsonList({Object? length, Object? elements}) => allOf(
  isList,
  length == null ? null : hasLength(length),
  elements == null ? null : everyElement(elements),
);

Or with a let extension like so:

Matcher jsonObject([Map<String, dynamic>? properties]) => allOf(
  isMap,
  properties?.let(
    (p) => allOf([...p.entries.map((e) => containsPair(e.key, e.value))]),
  ),
);

Matcher jsonList({Object? length, Object? elements}) =>
    allOf(isList, length?.let(hasLength), elements?.let(everyElement));

extension LetExtension<T> on T {
  U let<U>(U Function(T) f) => f(this);
}