I am willing to start writing Unit Tests for my CRM 2011 code. I have very little experience of Unit Testing, so I would like some help with it.
The method I would like to test is:
public Proxy.custom_serviceobject PopulateServiceObject(Proxy.custom_serviceobject crmServiceObject, serviceobject irisServiceObject, Guid locationId)
{
//set the reference to the location the order is connected to
crmServiceObject.custom_location = new EntityReference("custom_location", locationId);
//add the reference to the product in the serviceobject
crmServiceObject.custom_product = new EntityReference("product", new Guid(irisServiceObject.ItemId));
//convert the errorid to an int
Int32 errorId;
var success = Int32.TryParse(irisServiceObject.ErrorId, out errorId);
crmServiceObject.custom_errorclassOptionSetValue = success ? new OptionSetValue(errorId) : new OptionSetValue(Int32.MinValue);
crmServiceObject.custom_serviceobjecttype =
new EntityReference("custom_serviceobjecttype", new Guid(irisServiceObject.ObjectType.Id));
crmServiceObject.custom_placement = irisServiceObject.SortId;
crmServiceObject.custom_yearofinstallationOptionSetValue = new OptionSetValue(irisServiceObject.AuditYear);
crmServiceObject.custom_yearofmanufactureOptionSetValue = new OptionSetValue(irisServiceObject.ProductionYear);
crmServiceObject.custom_location = new EntityReference("custom_location", new Guid(irisServiceObject.Location));
crmServiceObject.custom_quantity = irisServiceObject.Qty;
crmServiceObject.custom_remark = irisServiceObject.ExternalNote;
crmServiceObject.custom_internalremark = irisServiceObject.InternalNote;
if (irisServiceObject.Cancelled)
{
var setStateRequest = new SetStateRequest
{
EntityMoniker = new EntityReference
{
Id = crmServiceObject.Id,
LogicalName = "custom_serviceobject"
},
State = new OptionSetValue(StatusInactive),
Status = new OptionSetValue(StatusReasonInactive)
};
Service.Execute(setStateRequest);
}
return crmServiceObject;
}
Do you have some ideas about what kind of test I could write? Should I mock some components?
Your method only does two things, update some values in the crmServiceObject, and execute an update statement. Since there is nothing CRM specific (besides besides the crmServiceObject being a type defined by CRM) to unit testing updating an object I'll skip that part of it.
As far as the CRM call to disable the entity, you've got two options for a unit test. Verify that CRM performed a disable or not, or mock an IOrganizationService and assert that the Execute call was made or not. It's up to you really.
This question may be helpful as well, although it relates to plugins specifically.
Related
I am trying to write my very first xunit test with Database, instead of mocking the DbContext I used the inMemoryDatabase as I read in articles, so I did like following
public class GetCustomersTest
{
DbContextOptions _context;
public GetCustomersTest()
{
if (_context==null)
_context = CreateContextForCustomer();
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void GetCustomerById_ShouldReturnCorrectObject(int id)
{
using (var context = new DataBaseContext(_context))
{
var customerByIdService = new GetCustomerByIdService(context);
var customer = customerByIdService.Execute(id);
var customerActual = context.Customers.Where(x => x.Id == id).SingleOrDefault();
var customerTmp = new Customer()
{
Id = id,
FirstName = customer.Data.FirstName,
LastName = customer.Data.LastName,
Phone = customer.Data.Phone,
ClientNote = customer.Data.ClientNote
};
Assert.Equal(customerTmp.FirstName, customerActual.FirstName);
Assert.Equal(customerTmp.LastName, customerActual.LastName);
Assert.Equal(customerTmp.Phone, customerActual.Phone);
Assert.Equal(customerTmp.ClientNote, customerActual.ClientNote);
}
}
private DbContextOptions<DataBaseContext> CreateContextForCustomer() {
var options = new DbContextOptionsBuilder<DataBaseContext>()
.UseInMemoryDatabase(databaseName: "SalonDatabase")
.Options;
using (var context = new DataBaseContext(options))
{
context.Customers.Add(new Customer
{
Id = 1,
FirstName = "User1",
LastName = "Surname1",
Phone = "123",
ClientNote = ""
});
context.Customers.Add(new Customer
{
Id = 2,
FirstName = "User2",
LastName = "Surname2",
Phone = "4567",
ClientNote = "The best"
});
context.SaveChanges();
}
return options;
}
}
it works find on [InlineData(1)] but when it comes to [InlineData(2)], it seems that it starts to run the constructor again , so as it wants to add the customerdata to table, it says that the record with that Id key exists.what is the best way for doing that?
When building your DB context options, add a GUID to the database name to make it unique:
var options = new DbContextOptionsBuilder<DataBaseContext>()
.UseInMemoryDatabase(databaseName: "SalonDatabase" + Guid.NewGuid().ToString())
.Options;
Or if you're using a new enough language version you can use string interpolation instead of concatenation:
var options = new DbContextOptionsBuilder<DataBaseContext>()
.UseInMemoryDatabase(databaseName: $"SalonDatabase{Guid.NewGuid()}")
.Options;
If you do this then every test uses a brand new database, unaffected by any previous tests.
As #MicheleMassari says, it's good practice to follow the Arrange Act Assert pattern, so that it's clear which lines are setting things up ready for the test and which are performing the action that you want to test the outcome of.
Arrange inputs and targets. Arrange steps should set up the test case. Does the test require any objects or special settings? Does it need to prep a database? Does it need to log into a web app? Handle all of these operations at the start of the test.
Act on the target behavior. Act steps should cover the main thing to be tested. This could be calling a function or method, calling a REST API, or interacting with a web page. Keep actions focused on the target behavior.
Assert expected outcomes. Act steps should elicit some sort of response. Assert steps verify the goodness or badness of that response. Sometimes, assertions are as simple as checking numeric or string values. Other times, they may require checking multiple facets of a system. Assertions will ultimately determine if the test passes or fails.
The code samples in that page are written in Python rather than C#, but the pattern is valid for unit tests in any language. In the case of your test, structuring the test in this way makes it clear whether you're testing the behaviour of GetCustomerByIdService.Execute or of Entity Framework's Where method.
Microsoft Dynamics CRM 2015.
I test Asp.Net Core controller's action. When I create new Lead record some plugin generates new Guid for lead.new_master_id field (it's type is string). Therefore after creating I retrive the record to get it's generated new_master_id value. How can I emulate this plugin behaviour through Fake Xrm Easy?
var fakedContext = new XrmFakedContext();
fakedContext.ProxyTypesAssembly = typeof(Lead).Assembly;
var entities = new Entity[]
{
// is empty array
};
fakedContext.Initialize(entities);
var orgService = fakedContext.GetOrganizationService();
var lead = new Lead { FirstName = "James", LastName = "Bond" };
var leadId = orgService.Create(lead);
var masterId = orgService.Retrieve(Lead.EntityLogicalName, leadId,
new Microsoft.Xrm.Sdk.Query.ColumnSet(Lead.Fields.new_master_id))
.ToEntity<Lead>().new_master_id;
In v1.x of FakeXrmEasy you'll need to enable PipelineSimulation and register the plugin steps you would like to be fired on Create manually by registering their steps.
fakedContext.UsePipelineSimulation = true;
Once enabled, you'll need to enable the necessary steps via calling RegisterPluginStep. In your example you'll need to at least register something along the lines of:
fakedContext.RegisterPluginStep<LeadPlugin>("Create", ProcessingStepStage.Preoperation);
Where LeadPlugin would be the name of your plugin that generates the new_master_id property.
Keep in mind v1.x is limited in that it supports pipeline simulation for basic CRUD requests only.
Later versions (2.x and/or 3.x) come with a brand new middleware implementation allowing registering plugin steps for any message. Soon we'll be implementing automatic registration of plugin steps based on an actual environment and/or custom attributes.
Here's an example using the new middleware
public class FakeXrmEasyTestsBase
{
protected readonly IXrmFakedContext _context;
protected readonly IOrganizationServiceAsync2 _service;
public FakeXrmEasyTestsBase()
{
_context = MiddlewareBuilder
.New()
.AddCrud()
.AddFakeMessageExecutors()
.AddPipelineSimulation()
.UsePipelineSimulation()
.UseCrud()
.UseMessages()
.Build();
_service = _context.GetAsyncOrganizationService2();
}
}
You can find more info on the QuickStart guide here
Disclaimer: I'm the author of FakeXrmEasy :)
I created a function that verifies some rules. The function is supposed to return a bool whenever an element of a list matches an element of another list.
Here is the relevant code of the Rule Class
public override TestResult Execute()
{
Instrument ins = (Items.Length > 0) ? Items[0] as Instrument : null;
string errorInfo;
if (ins == null)
{
Result.Message = "Unable to perform test";
Result.Status = ResultStatus.Error;
return Result;
}
if (MPICSupportDB(ins))
{
Result.Message = "DB not supported by MPIC";
Result.Status = ResultStatus.Yellow;
}
else
{
Result.Status = ResultStatus.Green;
}
return Result;
}
private bool MPICSupportDB(Instrument ins)
{
IServiceProviderFactory serviceFactory = new WebServiceProviderFactory();
IInterfaceAssignmentService wService = serviceFactory.CreateInterfaceAssignmentService();
InterfaceAssignment wAssignments = wService.LoadAssignmentGroup("R4");
return ins.Connections.OfType<InterfaceConnection>()
.Where(conn => conn.Card.IsDB)
.Any(conn => wAssignments.PartMasters
.Any(partNumber => (conn.CardPartNumber == partNumber.PartNumber)));
}
I am trying to test the function MPICSupportDB in a unit test. So far I have started creating my unit test (below), but now I'm lost and I have no idea what to do.
[TestMethod]
public void TestForcompatibleDB()
{
var ins = new Instrument();
var serviceFactoryMock = new Mock<IServiceProviderFactory>();
var wserviceTest = new Mock<IInterfaceAssignmentService>();
var wassagnementTest = new Mock<InterfaceAssignment>();
// adding an MPIC card
ins.Connections.Add(AddCard(CardType.MPIC, "MA505400612268", "CARD1", 0, ins));
// adding an MPIC daughterboard
ins.Connections.Add(AddCard(CardType.GPIM_DB, "MA335022012268", "DB1", 1, ins));
var rule = new Rule026(RuleApplicability.Test, new object[] { ins });
var result = rule.Execute();
Assert.IsNotNull(result);
Assert.AreEqual(ResultStatus.Green, result.Status);
}
The problem is that classes like Webservice and factoryService cannot be run directly in a unit test.
Can someone explain to me how to properly mock these object and make my test run?
You need to provide the IServiceProviderFactory to your Rule026 class, rather than constructing it within the class. This will allow you to use your Mocks that you're creating. The most common approach would be through constructor injection. You haven't provided the Constructor for your Rule class, but if you modify it to something like this:
public Rule026(/*otherArgs*/, IServiceProviderFactory scpFactory = null) {
if(null == scpFactory)
scpFactory = new ServiceProviderFactory();
}
_serviceProviderFactory = scpFactory;
}
Then you will be able to inject the factory from your tests, whilst not having to update all of the code currently constructing Rules. Moving to an IOC container to provide the dependencies, or removing the default and forcing the clients to create the factory in order to able to instantiate the Rule may be preferable, depending on your situation.
Once you can pass in your mocks, you just need to setup a return chain to allow the mocks to return each other. Something like:
var serviceFactoryMock = new Mock<IServiceProviderFactory>();
var wserviceTest = new Mock<IInterfaceAssignmentService>();
var wassagnementTest = new Mock<InterfaceAssignment>();
serviceFactoryMock.Setup(x=>x.CreateInterfaceAssignmentService())
.Returns(wserviceTest.Object);
wserviceTest.Setup(x=>x.CreateInterfaceAssignmentService())
.Returns(wassagnementTest.Object);
wassagnementTest.Setup(x=>x.LoadAssignmentGroup(It.IsAny<string>()))
.Returns(cannedInterfaceAssignmentResponse);
And then supply the mock when constructing your object:
var rule = new Rule026(RuleApplicability.Test,
new object[] { ins },
serviceFactoryMock.Object);
You may also want to add verification etc to your mocks, depending on what you're trying to test and your particular style of testing.
We are having an issue with searching a custom record through SuiteTalk. Below is a sample of what we are calling. The issue we are having is in trying to set up the search using the internalId of the record. The issue here lies in in our initial development account the internal id of this custom record is 482 but when we deployed it through the our bundle the record was assigned with the internal Id of 314. It would stand to reason that this internal id is not static in a site per site install so we wondered what property to set up to reference the custom record. When we made the record we assigned its “scriptId’ to be 'customrecord_myCustomRecord' but through suitetalk we do not have a “scriptId”. What is the best way for us to allow for this code to work in all environments and not a specific one? And if so, could you give an example of how it might be used.
Code (C#) that we are attempting to make the call from. We are using the 2013.2 endpoints at this time.
private SearchResult NetSuite_getPackageContentsCustomRecord(string sParentRef)
{
List<object> PackageSearchResults = new List<object>();
CustomRecord custRec = new CustomRecord();
CustomRecordSearch customRecordSearch = new CustomRecordSearch();
SearchMultiSelectCustomField searchFilter1 = new SearchMultiSelectCustomField();
searchFilter1.internalId = "customrecord_myCustomRecord_sublist";
searchFilter1.#operator = SearchMultiSelectFieldOperator.anyOf;
searchFilter1.operatorSpecified = true;
ListOrRecordRef lRecordRef = new ListOrRecordRef();
lRecordRef.internalId = sParentRef;
searchFilter1.searchValue = new ListOrRecordRef[] { lRecordRef };
CustomRecordSearchBasic customRecordBasic = new CustomRecordSearchBasic();
customRecordBasic.recType = new RecordRef();
customRecordBasic.recType.internalId = "314"; // "482"; //THIS LINE IS GIVING US THE TROUBLE
//customRecordBasic.recType.name = "customrecord_myCustomRecord";
customRecordBasic.customFieldList = new SearchCustomField[] { searchFilter1 };
customRecordSearch.basic = customRecordBasic;
// Search for the customer entity
SearchResult results = _service.search(customRecordSearch);
return results;
}
I searched all over for a solution to avoid hardcoding internalId's. Even NetSuite support failed to give me a solution. Finally I stumbled upon a solution in NetSuite's knowledgebase, getCustomizationId.
This returns the internalId, scriptId and name for all customRecord's (or customRecordType's in NetSuite terms! Which is what made it hard to find.)
public string GetCustomizationId(string scriptId)
{
// Perform getCustomizationId on custom record type
CustomizationType ct = new CustomizationType();
ct.getCustomizationTypeSpecified = true;
ct.getCustomizationType = GetCustomizationType.customRecordType;
// Retrieve active custom record type IDs. The includeInactives param is set to false.
GetCustomizationIdResult getCustIdResult = _service.getCustomizationId(ct, false);
foreach (var customizationRef in getCustIdResult.customizationRefList)
{
if (customizationRef.scriptId == scriptId) return customizationRef.internalId;
}
return null;
}
you can make the internalid as an external property so that you can change it according to environment.
The internalId will be changed only when you install first time into an environment. when you deploy it into that environment, the internalid will not change with the future deployments unless you choose Add/Rename option during deployment.
I'm trying to trigger an automation on ExactTarget using SOAP in .net in C#
so far i have found the examples page help.exacttarget.com/en-GB/technical_library/web_service_guide/technical_articles/interacting_with_automation_studio_via_the_web_service_soap_api/
which says var performResponse = soapClient.Perform(performRequest);
however soapClient.Perform dose not take a singe agument.
what i have so far is this
Automation automation = new Automation();
PerformOptions options = new PerformOptions();
automation.CustomerKey = "53ba121d-2934-90d6-d86d-e0662c656165";
automation.ScheduledTime = DateTime.Now;
automation.ScheduledTimeSpecified = true;
automation.IsActive = true;
automation.AutomationSource = new AutomationSource()
{
AutomationSourceID = Guid.NewGuid().ToString(),
AutomationSourceType = "RestAPI"
};
automation.Notifications = new AutomationNotification[0];
// automation.ObjectID = "7d88eb5b-80ea-43bb-97b2-4067aaa19c35";
automation.PartnerProperties = new APIProperty[0] { };
// automation.PartnerKey = "53ba121d-2934-90d6-d86d-e0662c656165";
string sA;
string sB;
string sC;
PerformResult[] steve = soapClient.Perform(new PerformOptions(),
"start", new APIObject[] { automation }, out sA, out sB, out sC);
can anyone give me a hand or show me a simple example
thanks.
I'm using mainly Java to interact with ET but I can provide you some hints:
try to create automation first
if automation already exists try to find it/retrieve it
you can pass this retrieved object to Perform method
I think that if object already exists then CustomerKey should be sufficient for your needs, you don't need to specify nothing more.