FakeXrmEasy is a state-based framework, with a data-driven design in mind. When testing logic that uses the Dataverse / Dynamics CRM, you can compare the state before and after the test execution by just querying its data.
When testing some scenarios, you would need to setup the initial state with some set of entity records that will be required by the system under test (plugin, code activity, azure function, custom action, mvc portal, etc.).
The way to initialize these entities is by calling the .Initialize() method. It has several overloads to help you setup the state with a single entity, or with a list of entities.
//Initialize the initial state with a single account record
_context.Initialize(new Account() {
Id = Guid.NewGuid(),
Name = "Dynamics Value SL"
});
//Initialize the initial state with an account record and a contact record
_context.Initialize(new List<Entity>()
{
new Account() { Id = Guid.NewGuid(), Name = "Dynamics Value SL" },
new Contact() { Id = Guid.NewGuid(), FirstName = "Tony" }
});
The Id property is required because each record needs to be uniquely identified in the In-Memory database.
// Just don't call .Initialize() ;)
This might happen when testing some logic that just creates brand new entity records without querying anything.
Now, how to setup an initial state with entity records and their respective associations? Let’s look into that…
The dataverse model N:1 and 1:N relationships as lookup fields. A lookup field is a field of type EntityReference. The EntityReference contains the minimum info to identify a unique entity record: the logical name, and a Guid (Global Unique Identifier).
A single lookup field can be used to model both sides of the relationship. For example, an Account that has a PrimaryContactId field (which is a lookup, or an EntityReference) stores an N:1 relationship against the Contact entity. The Account could be associated to up to one single Contact using that relationship, or none. On the other hand, one contact could be the primary contact of many different accounts.
The concept of a 1:1 relationship does not exist natively in the Dataverse / Dynamics CRM, but it can be modeled as a 1:N or N:1 relationship.
If you need to setup multiple entity records with associations between them, it might be a good idea declaring them at the class level so you could reuse such data to test different scenarios on them where only a slight subset of the attributes change. For example:
public class MyAccountTests: FakeXrmEasyTestsBase
{
private readonly Account _account;
private readonly Contact _contact;
public MyAccountTests()
{
_contact = new Contact() { Id = Guid.NewGuid() };
_account = new Account()
{
Id = Guid.NewGuid(),
PrimaryContactId = _contact.ToEntityReference() //the Account is associated with the above contact record
};
}
[Fact]
public void Should_have_a_primary_contact()
{
_context.Initialize(new List<Entity>() {
_account, _contact
});
//Execute whatever logic....
}
}
In the example aboved we have modelled the fact the _account has the _contact record as its primary contact. This pattern allows to modify the properties of the account and contact if needed before calling .Initialize() on each unit test without duplicating these declarations across each unit test.
Many to many relationships (N:N) can be modelled in the Dataverse / Dynamics CRM essentially in two different ways:
If you use the intersect entity, then you could treat the relationship and the entity like any other entity: you can add / update / remove relationships by just calling Create / Update / or Delete operations on them. The custom entity has a schema name too that could be generated in crmsvcutil.
However, if you use the out of the box N:N relationships, you need to use special SDK messages to operate on them, they’re the Associate and Dissassociate messages.
One could create multiple different relationships between any given two entities, and so relationships are identified by their relationship schema name.
Therefore, in order to use Associate and Dissassociate messages you’ll need to specify which is the relationship for which you want to establish the association / dissassociation.
One caveat is that the relationship metadata is not generated as part of crmsvcutil generation and so we need a different construct to tell FakeXrmEasy how to deal with these N:N relationships.
When using N:N relationships and Associate or Disassociate methods, you need to use the .AddRelationship method to tell FakeXrmEasy what is the intersect entity for that N:N relationship.
_context.AddRelationship("systemuserroles_association", new XrmFakedRelationship
{
IntersectEntity = "systemuserroles", //The logical name of the intersect entity used behind the scenes when calling .Associate or .Disassociate messages
Entity1LogicalName = SystemUser.EntityLogicalName,
Entity1Attribute = "systemuserid",
Entity2LogicalName = Role.EntityLogicalName,
Entity2Attribute = "roleid"
});
If you use a method similar to the one above, every time you execute an .Associate or Disassociate message against the relationship with schema name ‘systemuserroles_association’ (from the example), it’ll use the systemuserroles entity to store these associations.