Unit testing a query to a CRM database with Nunit - c#

I have the following method that I am trying to write unit tests for:
public Entity GetContactByEmail(string email)
{
var queryExpression = new QueryExpression(CRMFieldNames.Contact.EntityName);
queryExpression.ColumnSet = new ColumnSet(CRMFieldNames.Contact.EmailAddress1);
queryExpression.Criteria.AddCondition(CRMFieldNames.Contact.EmailAddress1, ConditionOperator.Equal, email);
var entities = _crmExecutor.Execute(service => service.RetrieveMultiple(queryExpression));
if (entities.Entities.Count > 0)
{
return entities.Entities[0];
}
return entities.Entities.FirstOrDefault();
}
I have attempted the following unit test:
[Test]
public void GetContactByEmail_AnyCase_ReturnsEntity()
{
var email = "lorem#ipsum.com";
var repository = CreateEmailSendRepository();
var query = new QueryExpression("contact");
var entityCollection = new EntityCollection();
entityCollection.Entities.Add(new Entity());
_crmExecutor.Execute(_organizationService => _organizationService.RetrieveMultiple(query)).Returns(entityCollection);
var result = repository.GetContactByEmail(email);
Assert.IsInstanceOf<Entity>(result);
}
private EmailSendRepository CreateEmailSendRepository()
=> new EmailSendRepository(_crmExecutor);
It is failing on the conditional in the GetContactByEmail method saying that entities = null. Can someone please point me in the right direction of giving entities a value here?

Related

Getting a null when returning a Generic.List in a unit test, from in-memory data

I've added some unit tests to an ASP.NET MVC app I've written using .NET 6. I'm using an in-memory database for the unit tests. One of the unit tests fails with the following error:
Xunit.Sdk.IsAssignableFromException HResult=0x80131500
Message=Assert.IsAssignableFrom() Failure Expected:
typeof(System.Collections.Generic.List<PharmacyWarehouseV2.Models.SiteToSite>)
Actual: (null) Source=xunit.assert StackTrace: at
Xunit.Assert.IsAssignableFrom(Type expectedType, Object object) at
Xunit.Assert.IsAssignableFrom[T](Object object) at
PharmacyWarehouseV2.Test.SiteToSiteControllerTest.IndexTest() in
D:\Repos\PW\PharmacyWarehouseV2\PharmacyWarehouseV2.Test\SiteToSiteControllerTest.cs:line
29
The strange thing is I've got another unit test I wrote in the same way, which works fine.
Here is the code for the in-memory data I'm using with Moq (note: the SiteToSite class is large, so I'll be removing most of the properties for brevity's sake. The only properties which are required are the first two):
public static IEnumerable<SiteToSite?> GetSiteToSites()
{
// Construct some SiteToSites first
var siteToSites = new List<SiteToSite>()
{
new SiteToSite()
{
SiteToSiteID = 1,
DateReceived = new DateTime(2020, 7, 1),
OrderedByName = "John Doe",
OrderedByID = 1,
// other properties removed for brevity
},
new SiteToSite()
{
SiteToSiteID = 2,
DateReceived = new DateTime(2021, 3, 1),
OrderedByName = "Teresa",
OrderedByID = 2,
// other properties removed for brevity
}
};
// Now construct SiteToSiteItems
var ss1 = new SiteToSiteItem()
{
SiteToSiteItemID = 1,
SiteToSiteID = 1,
ProgramName = "Program One",
Notes = "First note"
};
var ss2 = new SiteToSiteItem()
{
SiteToSiteItemID = 2,
SiteToSiteID = 2,
ProgramName = "Program Two",
Notes = "Second note"
};
var ss3 = new SiteToSiteItem()
{
SiteToSiteItemID = 3,
SiteToSiteID = 2,
ProgramName = "Program Two",
Notes = "Third note"
};
// Now assing SiteToSiteItems to their parent SiteToSites
siteToSites[0].SiteToSiteItems = new List<SiteToSiteItem>() { ss1 };
siteToSites[1].SiteToSiteItems = new List<SiteToSiteItem>() { ss2, ss3 };
return siteToSites;
}
I use a service/repository class. This is the method that is used in the unit test:
public IEnumerable<SiteToSite?> GetAll()
{
var tmp = _context.SiteToSite.OrderBy(s => s.SiteToSiteID);
return tmp;
}
And here's the unit test that's failing:
[Fact]
public void IndexTest()
{
// arrange
var mockRepo = new Mock<ISiteToSiteService>();
mockRepo.Setup(m => m.GetAll()).Returns(SiteToSiteMockData.GetSiteToSites());
var emptyDbContext = new PharmacyDBContext(); //won't test the AJAX calls
var controller = new SiteToSiteController(emptyDbContext, mockRepo.Object);
// act
var result = controller.Index();
// assert
Assert.NotNull(result);
var viewResult = Assert.IsType<ViewResult>(result);
Assert.True(viewResult.ViewData.Count > 0, "viewResult does not have any records, as it should");
var viewResultSites = Assert.IsAssignableFrom<List<SiteToSite>>(viewResult.ViewData.Model);
if (viewResultSites.Count > 0)
{
// NOTE: I do not like this; it violates unit testing.
Assert.Equal(2, viewResultSites.Count);
Assert.Equal("John Doe", viewResultSites[0]?.OrderedByName);
}
}
When I debug the test, after the result variable is assigned in the "act" step, it does have the data from in-memory. However, the viewResult.ViewData.Model is null. I don't understand how result has data, but viewResult.ViewData.Model doesn't. I've gone to the xUnit repo on GitHub to look at the documentation, but it wasn't clear to me what the problem is. What might be causing the discrepancy?
Addendum 1
Here's the GetAll() method from the SiteToSiteService:
public IEnumerable<SiteToSite?> GetAll()
{
var tmp = _context.SiteToSite.OrderBy(s => s.SiteToSiteID);
return tmp;
}
And here's the Index() method from the controller:
public IActionResult Index()
{
return View();
}
(Posting answer on behalf of the question author, to move it to the answer space).
After Peter's question I realized I made a mistake. The Index action method didn't pass any data into the view. This is what was giving me a null. (For business reasons we don't show a list of site-to-sites.) So, I've put an xUnit [Fact(Skip = "<message>")] into the unit test for the IndexTest.
However, I do have a DetailTest unit test. Here's the code for the DetailTest:
[Fact]
public void DetailsTest()
{
// arrange
var mockRepo = new Mock<ISiteToSiteService>();
mockRepo.Setup(m => m.Get(1)).Returns(SiteToSiteMockData.Get(1));
var controller = new SiteToSiteController(new PharmacyDBContext(), mockRepo.Object);
// act
var result = controller.GetSiteToSiteById(1);
// assert
var viewResult = Assert.IsType<ViewResult>(result);
var viewResultSiteToSite = Assert.IsAssignableFrom<SiteToSite>(viewResult.ViewData.Model);
Assert.Equal("John Doe", viewResultSiteToSite.OrderedByName);
}
Here's the action method from the controller:
public ActionResult GetSiteToSiteById(int id)
{
var site2Site = siteToSiteService.Get(id);
if (site2Site == null)
{
return NotFound();
}
return View("SiteToSiteDetail", site2Site);
}
and here's the Get(int id) method from the service/repository method:
public SiteToSite? Get(int id)
{
var result = _context.SiteToSite.Include(i => i.SiteToSiteItems).Where(s => s.SiteToSiteID == id);
return result.FirstOrDefault();
}

Unit Testing xUnit Asserting 2 Collections with Moq Interface

Good day everyone,
I'm new in xunit and even in unit testing. I have a code here and I'm trying to assert two collection of list. But I have no idea how to assert and pass this test. Here's my code
[Theory]
[InlineData(1)]
public void GetAllStudents_Exempt1(int number)
{
// arrange
var studentRepo = new Mock<IStudentRepository>();
var listOfStudents = new List<Student> { new Student { StudentId = 1, Firstname = "Firstname1", Lastname = "Firstname1" },
new Student{StudentId=2, Firstname="Firstname2",Lastname="Lastname2"} };
var getAllStudentDetailsExempt1 = studentRepo.Setup(s => s.GetStudents()).Returns(listOfStudents.Where(x => x.StudentId != number));
var studentService = new StudentService(studentRepo.Object);
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(1);
// assert
// I don't have any idea how to assert
}
First a few notes:
var getAllStudentDetailsExempt1 = studentRepo
.Setup(s => s.GetStudents())
.Returns(listOfStudents.Where(x => x.StudentId != number));
you don't need var getAllStudentDetailsExempt1, you can just setup your repo-mock...
studentRepo
.Setup(s => s.GetStudents())
.Returns(listOfStudents.Where(x => x.StudentId != number));
You probably want to change:
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(1);
to use the number variable...
// act
var getStudentsDetails = studentService.ListOfStudentsExempt1(number);
So then you can assert by checking some properties:
Assert.Equals(1, getStudentsDetails.Count);
Assert.Equals("FirstName1", getStudentsDetails.First().Firstname);
etc. etc.
Give it a shot!

callback is not called using moq + autofaq

I have a unit test done using moq to mock the objects, and the test is working fine, and now I want to use autofac +moq, but I'm having a few problems.
this is the test:
using (var mock = AutoMock.GetLoose())
{
var issues = new List<Issue>();
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 1 });
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 2 });
var numKeys = 0;
mock.MockRepository.Create<IStorageService>()
.Setup(myMock => myMock.GetBatchIssues(It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<IList<string>>()))
.Callback((string org, string repo, IList<string> keys) => numKeys = keys.Count)
.Returns(issues);
var sut = mock.Create<IssueReceiveService>();
var check = await sut.CheckInStorage("org", "repo", issues);
Assert.AreEqual(issues.Count, numKeys);
}
the call to sut.CheckInStorage return null, and the variable numKeys is not updated to the correct value. This test works fine using just moxk, so I suppose I'm missing something how to configure a mock with autoMock.
Where can I find more informations?
UPDATE:
after a few more tests I found the solution
using (var mock = AutoMock.GetLoose())
{
var issues = new List<Issue>();
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 1 });
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 2 });
var numKeys = 0;
mock.Mock<IStorageService>()
.Setup(myMock => myMock.GetBatchIssues(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IList<string>>()))
.Callback((string org, string repo, IList<string> keys) => numKeys = keys.Count)
.Returns(issues);
var sut = mock.Create<IssueReceiveService>();
var check = await sut.CheckInStorage("org", "repo", issues);
Assert.AreEqual(issues.Count, numKeys);
}
after a few more tests I found the solution
using (var mock = AutoMock.GetLoose())
{
var issues = new List<Issue>();
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 1 });
issues.Add(new Issue { Organization = "org", Repository = "repo", Number = 2 });
var numKeys = 0;
mock.Mock<IStorageService>()
.Setup(myMock => myMock.GetBatchIssues(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IList<string>>()))
.Callback((string org, string repo, IList<string> keys) => numKeys = keys.Count)
.Returns(issues);
var sut = mock.Create<IssueReceiveService>();
var check = await sut.CheckInStorage("org", "repo", issues);
Assert.AreEqual(issues.Count, numKeys);
}

How do I search for lead or account using Dynamics CRM SDK?

Can someone please provide a sample code for retrieving leads by email in CRM SDK? Is there any built in function that works like this?
Guid leadID = someLeadManager.GetByEmail(email);
Assuming that you've obtained the service, you can execute the following query.
private Guid GetGuidByEmail(String email)
{
QueryExpression query = new QueryExpression
{
EntityName = "lead",
ColumnSet = new ColumnSet("emailaddress1"),
Criteria = new FilterExpression
{
Filters =
{
new FilterExpression
{
Conditions =
{
new ConditionExpression(
"emailaddress1", ConditionOperator.Equals, email)
}
}
}
}
};
Entity entity = service.RetrieveMultiple(query).Entities.FirstOrDefault();
if(entity != null)
return entity.Id;
return Guid.Empty;
}
Now, if you need filtration for a match of part of the email, the query gets shorter, and instead, you can do the selection using LINQ like this.
private IEnumerable<Guid> GetGuidsByEmail(String email)
{
QueryExpression query = new QueryExpression
{
EntityName = "lead",
ColumnSet = new ColumnSet("emailaddress1")
};
IEnumerable<Entity> entities = service.RetrieveMultiple(query).Entities;
return entities
.Where(element => element.Contains("emailaddress1"))
.Where(element => Regex.IsMatch(element["emailaddress1"], email))
.Select(element => element.Id);
}
Do you want to retrieve leads that have a specific mail? Something like this can be done in that case.
private EntityCollection GetLeadsWithEmail(
IOrganizationService service, String wantedEmailAddress)
{
QueryExpression query = new QueryExpression();
query.EntityName = "lead";
// the columns you want
query.ColumnSet = new ColumnSet() { AllColumns = true };
query.Criteria = new FilterExpression();
query.Criteria.FilterOperator = LogicalOperator.And;
query.Criteria.Conditions.Add(new ConditionExpression(
"emailaddress1", ConditionOperator.Equal, wantedEmailAddress));
return service.RetrieveMultiple(query);
}
This will retrieve all the leads that have wantedEmailAddress in their emailaddress1 field.
You can then check if there were any matches from where you called it;
EntityCollection leadCollection = GetLeadsWithEmail(
service, "someone#example.com");
Entity entity = leadCollection[0];
You probably should first check the number of entities in the collection with leadCollection.Entities.Count and continue from there.
Here's a sample from MSDN.

How to get result of rollup query for a custom entity

When trying to get the records I get this error
The 'Rollup' method does not support entities of type 'new_X'.
This is my code
RollupRequest req = new RollupRequest();
QueryExpression qe = new QueryExpression();
qe.EntityName = "new_x";
qe.ColumnSet = new ColumnSet(true);
req.Query = qe;
req.Target = new EntityReference("new_newpost", new Guid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
req.RollupType = RollupType.Related;
RollupResponse resp = (RollupResponse)xrm.Execute(req);
How can I get the results of rollup query
Thanks in advance
For custom entities you can do the fallowing
var rollupQuery = xrm.GoalRollupQuerySet.Where(c => c.Id == x.new_RecordstoRun.Id).First();
var result = xrm.RetrieveMultiple(new FetchExpression(rollupQuery.FetchXml));
But - How can I add a 'Skip' or 'Take' Linq to this?
You can only use the RollupRequest on a certain set of entities described on the MSDN.
So this will never work for "new_x" or "new_newpost".
This article has a correct demonstration of the RollupRequest using opportunity and account.
I would suggest just creating your own custom QueryExpression to retrieve all "new_x" and then link to "new_newpost" with LinkEntities.
Here is my code so get the results of RollupQuery
List<Guid> GetAllResultsFromRollupQuery(XrmServiceContext xrm, Guid rollupQueryId)
{
var rollupQuery = xrm.GoalRollupQuerySet.Where(v => v.Id == rollupQueryId).First();
var qa = GetQueryExpression(xrm, rollupQuery.FetchXml);
qa.PageInfo.Count = 1000;
qa.ColumnSet.AddColumn(rollupQuery.QueryEntityType + "id");
var result = new List<Guid>();
EntityCollection ec = null;
do
{
ec = xrm.RetrieveMultiple(qa);
ec.Entities.ToList().ForEach(v => result.Add((Guid)v.Attributes[rollupQuery.QueryEntityType + "id"]));
qa.PageInfo.PageNumber += 1;
} while (ec.MoreRecords == true);
return result;
}
QueryExpression GetQueryExpression(XrmServiceContext xrm, string fetchXml)
{
var req = new FetchXmlToQueryExpressionRequest { FetchXml = fetchXml };
var result = (FetchXmlToQueryExpressionResponse)xrm.Execute(req);
return result.Query;
}

Categories

Resources