Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unit tests using the realm #870

Closed
ffeliciodeveloper opened this issue Aug 24, 2022 · 18 comments · Fixed by #929
Closed

unit tests using the realm #870

ffeliciodeveloper opened this issue Aug 24, 2022 · 18 comments · Fixed by #929

Comments

@ffeliciodeveloper
Copy link

ffeliciodeveloper commented Aug 24, 2022

Hello everyone, I hope you are all well!

I would like to know if there is any way to integrate the realm with the unit testing features with mockito/mocktail.
I'm trying to test the realm mapping class, but I'm not having success.

Is there any way to mock the return results?

Thanks!

@nielsenko
Copy link
Contributor

I find it easier to open an actual in-memory realm for unit tests. Would that work for you?

@ffeliciodeveloper
Copy link
Author

Good morning @nielsenko!

Thank you very much for answering.

Sorry for taking so long to respond.

One question (sorry for my ignorance, I'm new to Flutter), in case, wouldn't this qualify as an integrated test?

As I only need a stub to know the return, would I need the realm in memory so I can test?

@nielsenko
Copy link
Contributor

Hi Fernando

Since you explicitly mentioned mockito let me quote the author on best practices:

Testing with real objects is preferred over testing with mocks - if you can construct a real instance for your tests, you should!

I totally agree. Don't stub unless you have to. Since you can easily spin up a realm in-memory, I suggest you do that for your tests.

Now "all" you need to stub/fake is the backend synchronisation, if you want to avoid the burden of doing an actual integration test with the backend.

One approach is to have a fake that implements Realm simply delegating to an actual in-memory realm, but faking Realm.subscriptions, Realm.syncSession, SubscriptionSet.waitForDownload, SubscriptionSet.waitForUpload and Session.getProgressStream to the extend you need it. For many test-cases that may not even be necessary.

@nielsenko
Copy link
Contributor

For the test of the SDK itself we actually spin-up a fresh backend and do proper integration tests whenever we test sync related functionality. It is not too hard to automate setup and teardown using realm-cli. That is one of the joys of SaaS 😄

@ffeliciodeveloper
Copy link
Author

Thanks for replying again, @nielsenko.

We are currently using mocktail for testing.

As mockito currently needs generated files, we chose to use mocktail even to not have this dependency.

Sorry to bother you, but I was wondering if you have any issues with the Realm framework and how it should be used (in this case, best practices for collection names and relationships)?

@nielsenko
Copy link
Contributor

nielsenko commented Aug 29, 2022

You are welcome.

I think it would work about the same with mocktail.

Regarding:

.. I was wondering if you have any issues with the Realm framework and how it should be used (in this case, best practices for collection names and relationships)?

I'm not sure I if I understand your question correctly, but there are known outstanding issues with the SDK. First of all we are still in beta, and there are stuff missing, such as embedded objects and some datatypes - map, set, and mixed comes to mind.

We also have at least one significant bug reported regarding crash during hot-restart #728

With regards to best practices, there are documentation available here https://www.mongodb.com/developer/products/realm/

I hope this helps

@ffeliciodeveloper
Copy link
Author

About your question regarding relationships, I have a structure like this in my collection:

{
    "_id": "object-id",
    "name": "name",
    "created_at": {
        "user_id": "object-id",
        "username": "name"
    },
    "updated_at": {
        "user_id": "object-id",
        "username": "username"
    }
}

And about the structure, would it be correct to have relationships where the collection has its name separated by an underscore?

I created 2 private classes (within my target class) defined as _created_at and _updated_at (following the template in the doc)
and when generating the g.dart file, the attribute is transformed like this in the get and set methods:

@override
createdat? get createdAt =>
    RealmObject.get<createdat>(this, 'created_at') as createdat?;
@override
set createdAt(covariant createdat? value) =>
    RealmObject.set(this, 'created_at', value);

@override
updatedat? get createdAt =>
    RealmObject.get<updatedat>(this, 'created_at') as createdat?;
@override
set createdAt(covariant updatedat? value) =>
    RealmObject.set(this, 'created_at', value);

And when running the project, it is informed that the above attributes were not found

The main class looks like this:

// main collection
@RealmModel()
@JsonSerializable()
@_ObjectIdConverter()
class _collection {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;
  @MapTo('name')
  late String name;
  @MapTo('created_at')
  late _created_at? createdAt;
  @MapTo('updated_at')
  late _updated_at? updatedAt;
}

// relationship classes
@RealmModel()
@JsonSerializable()
@_ObjectIdConverter()
class _created_at {
  @MapTo('id')
  late ObjectId userId;
  @MapTo('name')
  late String userName;
}

@RealmModel()
@JsonSerializable()
@_ObjectIdConverter()
class _updated_at {
  @MapTo('id')
  late ObjectId userId;
  @MapTo('name')
  late String userName;
}

Please forgive me if I'm being annoying, it's just that I would like to spend the possibilities of use without having to write anything native to be able to support (but if there is a need, I'll have to).

Thank you very much for your attention.

@nielsenko
Copy link
Contributor

nielsenko commented Aug 29, 2022

Hi Fernando

Unfortunately the above schema would require embedded objects, which we don't support yet. Also, adding json annotations at the model level will not do what you probably expect (see #683).

In general, whenever you use embedded objects (which are very common in MongoDB) you need to hoist out the object into a separate collection and make a link. That is until we support embedded objects 😒

I'm not sure what the @_ObjectIdConverter does in the above code, but if we forget about that and @JsonSerializable() then the following model might work for you (assuming you can change the schema on Atlas):

// main collection
import 'package:realm_dart/realm.dart';

part 'issue870.g.dart';

@RealmModel()
@MapTo('collection') // you need a collection in Atlas to match
class _Collection {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;

  // @MapTo('name') // <-- not needed, since field has the same name
  late String name;

  @MapTo('created_at')
  late _Change? createdAt;

  @MapTo('updated_at')
  late _Change? updateAt;
}

@RealmModel()
@MapTo('change') // you need a collection in Atlas to match
class _Change {
  @PrimaryKey()
  @MapTo('_id') // As this is hoisted into its own collection, it needs a _id field of its own
  late ObjectId id;

  @MapTo('user_id')
  late ObjectId userId;

  @MapTo('username')
  late String userName; // why not username both places? (or user_name on Atlas?)

  late DateTime at; // I assume you forgot the actual timestamp in your model
}

Notice I have introduced a new collection called change(on Atlas) and Change (in Dart) that is used for both the createdAt and updatedAt fields.

Is this a feasible way forward for you?

@nielsenko
Copy link
Contributor

BTW, unless you are already bound to a schema on Atlas, I suggest enabling developer mode under sync, which will allow the client to do additive changes to the schema.

Eventually you will do a destructive change, in which case the easier path forward is just to delete the old Atlas App Service application and start again.

I find that to be the fastest way to experiment with the schema - if you do greenfield development.

Also, getting familiar with realm_cli is time well spent. It will allow you to script setup and teardown of your apps on Atlas.

@ffeliciodeveloper
Copy link
Author

Thank you very much for replying.

You gave a great help.
I talked to my techlead and we'll set up a meeting with the backend team so we can rethink the structure.

As there is still no support for Map types, there would be a need to change to the model you presented me.

Again, thank you so much for your help and God bless you.

@nielsenko
Copy link
Contributor

You are welcome. I don't know about your timelines, but we are actively adding missing features. Unfortunately I cannot commit to any particular date, but embedded objects are high on our list of priorities.

@tushkaty
Copy link

@nielsenko Is there any example available for unit testing either with Mockito or without?

I've created a singleton class inside database_service.dart

class DatabaseService {
  static final DatabaseService _instance = DatabaseService._internal();

  factory DatabaseService() {
    _instance._openRealm();
    return _instance;
  }

  final Logger log = Logger('DatabaseService');

  DatabaseService._internal();

  late Realm _realm;

  Realm get realm => _realm;

  final Configuration _config =
      Configuration.local([Patient.schema]);

  _openRealm() {
    _realm = Realm(_config);
  }

  closeRealm() {
    if (!realm.isClosed) {
      realm.close();
    }
  }
}

from the tests if I try to call DatabaseService(), I'm getting this error

/test/data/repository/patient_repository_test.dart": Invalid argument(s): Failed to load dynamic library '/usr/local/Caskroom/flutter/3.0.4/flutter/bin/cache/artifacts/engine/darwin-x64/../Frameworks/realm.framework/Resources/librealm_dart.dylib': dlopen(/usr/local/Caskroom/flutter/3.0.4/flutter/bin/cache/artifacts/engine/darwin-x64/../Frameworks/realm.framework/Resources/librealm_dart.dylib, 0x0001): tried: '/usr/local/Caskroom/flutter/3.0.4/flutter/bin/cache/artifacts/engine/darwin-x64/../Frameworks/realm.framework/Resources/librealm_dart.dylib' (no such file)

@nielsenko
Copy link
Contributor

@tushkaty I can reproduce your issue wrt. loading librealm_dart.dylib when invoking flutter test to run widget tests etc. I will get back to you, when I know more.

@nielsenko nielsenko reopened this Sep 27, 2022
@wilsleygermano
Copy link

Just to add that I'm facing the same problem reported by @tushkaty when trying to uniting test my application. But one thing is different: I'm not even calling/using Realm in my test file.

I'm getting this:

Failed to load "[path]/X_controller_test.dart": Invalid argument(s): Failed to load dynamic library '/Users/germano/fvm/versions/stable/bin/cache/artifacts/engine/darwin-x64/../Frameworks/realm.framework/Resources/librealm_dart.dylib': dlopen(/Users/germano/fvm/versions/stable/bin/cache/artifacts/engine/darwin-x64/../Frameworks/realm.framework/Resources/librealm_dart.dylib, 0x0001): tried: '/Users/germano/fvm/versions/stable/bin/cache/artifacts/engine/darwin-x64/../Frameworks/realm.framework/Resources/librealm_dart.dylib' (no such file)

@tushkaty
Copy link

@wilsleygermano It's because some of your test cases using the Realm class object.

@wilsleygermano
Copy link

@tushkaty you are correct. I thought running a specific test file, that doesn't use realm, would avoid that error. Anyway, now you remind me that I'm facing problems with my realm tests the same way you have (using Mockito).

@desistefanova
Copy link
Contributor

Hey @tushkaty and @wilsleygermano,
It is a known issue #791. We have it in the backlog and hope we will find some solution soon.
The workaround is to copy the library inside the execution folder of flutter_tester.

@desistefanova
Copy link
Contributor

Hi @tushkaty and @wilsleygermano,
The new release is available here realm 0.10.0+rc.
You can upgrade the realm to 0.10.0+rc and then to run the new available command flutter pub run realm install from the project root folder. This will download the library and you will be able to run your tests.
I hope this will help.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants