I am using Ldapconnection.Sendrequest because I am running on Linux, so I can only use classes from the System.DirectoryServices.Protocols namespace.
The code works perfectly well against a live Active Directory, but here is an example anyway.
// retrieves the distinguishedname for all groups, starting from dc=test,dc=local
var request = new SearchRequest("dc=test,dc=local", "(Objectclass=group)", "distinguishedname");
var searchResponse = ldapConnection.SendRequest(request) as SearchResponse;
My question is, how can this be unit tested? I would like to unit test it because a different searchResponse will cause a different path to be taken through code.
I have tried mocking SearchResponse: (How to create an instance of SearchResponse class (which has no public constructors)?)
var ctors = typeof (SearchResponse).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
var neededCtor = ctors.First(
ctor =>
ctor.GetParameters().Count() == 5);
SearchResponse response = neededCtor.Invoke(new object[]
{
"distinguishedName",
null, // System.DirectoryServices.Protocols.DirectoryControl[]
null, // System.DirectoryServices.Protocols.ResultCode
errorMessage,
null // System.Uri[]
}) as SearchResponse;
return response;
But I cannot find a way to mock SearchResponse.Entries, as SearchResultEntryCollection and SearchResultEntry are (seemingly) unmockable. It's as though the dotnet team has gone out of its way to make this area completely unmockable.
I'm new to using LdapConnection (previously I could use the AccountManagement class, as I was running in Windows), so maybe I shouldn't be using LdapConnection in the first place?
Equally, I'd be open to running the AD tests in memory, as I would for database access, but I cannot find a way to do so.
Many thanks for you help,
Dan
Related
We're trying to use the REST API of Administration Service to manage the Configuration Manager
(What is the administration service in Configuration Manager?)
We have successfully queried entities of different types and executed some custom static methods (i.e. MoveMembers Method on SMS_ObjectContainerItem). It's all mostly blind shots as there is barely any documentation, but those basic functionalities seem to work fine.
Now we have hit the wall, trying to add collection rules to a SMS_Collection (existing or new). This was normally done calling the AddMembershipRule on the instance itself, that was previously fetched by e.g. WqlConnectionManager or some other proxy. However, this is clearly a no-go on a plain object fetched from the REST service.
We have tried to use the wmi OData service (by a generated proxy) as it clearly offers similar functionality, but this ends up with a "Not supported exception":
var savedCollection = Proxy.SMS_Collection.Where(c => c.CollectionID == result.CollectionID).FirstOrDefault();
savedCollection.AddMembershipRule(inclusionRule);
Proxy.UpdateObject(savedCollection);
Proxy.SaveChanges(); //EXCEPTION
I have tried running POST request in numerous ways, using urls like:
SMS_Collection.AddMembershipRule?CollectionID=DV000037 -> 404
SMS_Collection/DV000037/AddMembershipRule -> 404
SMS_Collection.DV000037.AddMembershipRule -> 404
SMS_Collection/DV000037.AddMembershipRule -> treated it as post to SMS_Collection/DV000037, and therefore triggers an update
or just
SMS_Collection.AddMembershipRule with collectionID as param
As for the request body I've used (or just the AddCollectionMembershipRuleRequestRule):
public class AddCollectionMembershipRuleRequest
{
public AddCollectionMembershipRuleRequestRule CollectionRule { get; set; }
}
public class AddCollectionMembershipRuleRequestRule
{
public string RuleName { get; set; }
public string IncludeCollectionID { get; set; }
}
I have also tried to Post an existing or new collection, with CollectionRules prefilled, but this ends up with an exception complaining about IncludeCollectionID not being part of CollectionRule (base class) - looks like validation being too strict and not dealing well with the inheritance.
var collectionRequest = new ECMCollectionCreationRequest()
{
Name = collectionName,
CollectionType = 2,
RefreshType = 4,
LimitToCollectionID = DefaultLimitingCollectionID,
CollectionRules = new List<SMS_CollectionRule>()
{
new SMS_CollectionRuleIncludeCollection()
{
RuleName = "MembershipRule",
IncludeCollectionID = "DV100020"
}
}
};
Stil, no luck with any of those. Do you have any idea if such a scenario (modification of CollectionRules) is even supported with the Rest /OData service? If so, what would be the right way to achieve so?
It looks like this part is simply not supported at the moment. Looking at the code it seems that the service is not interpreting the arguments properly and therefore causing validation issues.
However, the same can be achieved, in a bit less clean and structured way, using ManagementScope and ManagementObject
var scope = new ManagementScope(siteAddress);
scope.Connect();
using (ManagementObject collection = new ManagementObject(scope, new ManagementPath($"SMS_Collection.CollectionID='{collectionID}'"), new ObjectGetOptions()))
{
if (collection == null)
throw new Exception($"Unable to find collection with ID '{collectionID}'");
collection.Get();
using (ManagementBaseObject inParams = collection.GetMethodParameters("AddMembershipRule"))
using (ManagementClass ruleClass = new ManagementClass(scope, new ManagementPath("SMS_CollectionRuleDirect"), new ObjectGetOptions()))
using (ManagementObject rule = ruleClass.CreateInstance())
{
rule["ResourceClassName"] = "SMS_R_System";
rule["ResourceID"] = ecmResourceID;
rule["RuleName"] = machineName;
inParams["collectionRule"] = rule;
collection.InvokeMethod("AddMembershipRule", inParams, null);
}
}
One can add and remove all the other rule types in similar way.
Another alternative is of course to use PowerShell. Sill, I hope that with one of the next iterations of the Administration Service, support of those methods will be added.
Similarly, there seems to be no way to add/remove application or package and import/export them, using the admin services or even in the way mentioned above.
$Rule="{'collectionRule':{`"#odata.type`": `"#AdminService.SMS_CollectionRuleDirect`", `"ResourceClassName`": `"SMS_R_System`", `"ResourceID`": $DeviceID,`"RuleName`": `"$MachineName`"}}"
$RuleCreated = (Invoke-RestMethod -Method Post -Uri "https://$($CMProvider)/AdminService/wmi/SMS_Collection('$CollectionID')/AdminService.AddMembershipRule" -Body $Rule -ContentType 'application/json' -Credential $Cred)
Using NotificationHubClient I can get all registered devices using GetAllRegistrationsAsync(). But if I do not use the registration model but the installation model instead, how can I get all installations? There are methods to retrieve a specific installation but none to get everything.
You're correct, as of July 2016 there's no way to get all installations for a hub. In the future, the product team is planning to add this feature to the installations model, but it will work in a different way. Instead of making it a runtime operation, you'll provide your storage connection string and you'll get a blob with everything associated with the hub.
Sorry for visiting an old thread... but in theory you could use the GetAllRegistrationsAsyc to get all the installations. I guess this will return everything without an installation id as well, but you could just ignore those if you choose.
Could look something like this
var allRegistrations = await _hub.GetAllRegistrationsAsync(0);
var continuationToken = allRegistrations.ContinuationToken;
var registrationDescriptionsList = new List<RegistrationDescription>(allRegistrations);
while (!string.IsNullOrWhiteSpace(continuationToken))
{
var otherRegistrations = await _hub.GetAllRegistrationsAsync(continuationToken, 0);
registrationDescriptionsList.AddRange(otherRegistrations);
continuationToken = otherRegistrations.ContinuationToken;
}
// Put into DeviceInstallation object
var deviceInstallationList = new List<DeviceInstallation>();
foreach (var registration in registrationDescriptionsList)
{
var deviceInstallation = new DeviceInstallation();
var tags = registration.Tags;
foreach(var tag in tags)
{
if (tag.Contains("InstallationId:"))
{
deviceInstallation.InstallationId = new Guid(tag.Substring(tag.IndexOf(":")+1));
}
}
deviceInstallation.PushHandle = registration.PnsHandle;
deviceInstallation.Tags = new List<string>(registration.Tags);
deviceInstallationList.Add(deviceInstallation);
}
I am not suggesting this to be the cleanest chunk of code written, but it does the trick for us. We only use this for debugging type purposes anyways
I am trying to mock up the Win32 Registry objects in order to isolate some of my code for unit testing. I am able to create the shims and they get called to execute my code just fine. However I am not sure how to make the CreateSubKey method work. It needs to return a new RegistryKey object pointing to the newly created key. But I am having trouble figuring out how to do that. Here is what I have:
using (ShimsContext.Create())
{
Microsoft.Win32.Fakes.ShimRegistryKey.AllInstances.CreateSubKeyStringRegistryKeyPermissionCheck = (key, newSubkey, permissionCheck) =>
{
return key; // This returns the base, but I need a new one that adds the subkey...
};
}
What am I missing here?
Ok, I feel silly for not thinking of this before. Right after I posted, it hit me. I just need to make a new ShimRegistryKey.
using (ShimsContext.Create())
{
Microsoft.Win32.Fakes.ShimRegistryKey.AllInstances.CreateSubKeyStringRegistryKeyPermissionCheck = (key, newSubKeyName, permissionCheck) =>
{
var newSubKey = new Microsoft.Win32.Fakes.ShimRegistryKey();
newSubKey.NameGet = () => Path.Combine(key.Name, newSubKeyName); // All I care about for now is the name of the new subkey
return newSubKey;
};
}
Sometimes you just have to ask the question to see the answer. :-)
I have a MVC4 web application I'm unit testing right now. It uses entity framework for the database portion. I'm using NSubstitute to mock the database. This code is basically copied and pasted from another site which works fine, so I hope I'm just missing something super simple.
Thanks in advance!
Applications table in SQL:
AppID | ApplicationName
----------------------------
1 | MyCoolApplication
2 | MyOtherApplication
Entity created the Application class:
public class Application
{
public int AppID { get; set; }
public string ApplicationName { get; set; }
}
The mock section of the unit test looks like this:
var mockDb = Substitute.For<MyCoolApplicationsEntities>();
var applications = new List<Application>
{
new Application {AppID = 1, ApplicationName = "MyCoolApplication"},
new Application {AppID = 2, ApplicationName = "MyOtherApplication"},
};
var mockApplicationSet = Substitute.For<IDbSet<Application>, DbSet<Application>>();
mockApplicationSet.Provider.Returns(applications.AsQueryable().Provider);
mockApplicationSet.Expression.Returns(applications.AsQueryable().Expression);
mockApplicationSet.ElementType.Returns(applications.AsQueryable().ElementType);
mockApplicationSet.GetEnumerator().Returns(applications.AsQueryable().GetEnumerator());
mockApplicationSet.When(q => q.Add(Arg.Any<Application>()))
.Do(q => applications.Add(q.Arg<Application>()));
mockApplicationSet.When(q => q.Remove(Arg.Any<Application>()))
.Do(q => applications.Remove(q.Arg<Application>()));
mockDb.Applications.Returns(mockApplicationSet); //This is the line creating the error
The full error is:
Test method
MyProjectName.Controllers.MyControllerTest.TestOfSectionImTesting
threw exception:
NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException:
Could not find a call to return from.
Make sure you called Returns() after calling your substitute (for
example: mySub.SomeMethod().Returns(value)), and that you are not
configuring other substitutes within Returns() (for example, avoid
this: mySub.SomeMethod().Returns(ConfigOtherSub())).
If you substituted for a class rather than an interface, check that
the call to your substitute was on a virtual/abstract member. Return
values cannot be configured for non-virtual/non-abstract members.
Correct use:
mySub.SomeMethod().Returns(returnValue);
Potentially problematic use:
mySub.SomeMethod().Returns(ConfigOtherSub());
Instead try:
var returnValue = ConfigOtherSub();
mySub.SomeMethod().Returns(returnValue);
But that doesn't work in my environment because Applications isn't a method. Like I said, this works fine in another site of mine, so it's got to be something basic I'm missing. Nothing I've found online has been helpful with my particular case. I updated to the newest version of NSubstitute and I uninstalled/reinstalled, but still have got nothing.
Again, thanks in advance!
NSubstitute can not mock non-virtual members. (There are quite a few caveats to substituting for classes.)
MyCoolApplicationsEntities.Applications will need to be virtual for .Returns() to work.
Here's what ended up working:
var context = Substitute.For<MyCoolApplicationsEntities>();
var applications = new List<Application>
{
new Application {AppID = 1, ApplicationName = "MyCoolApplication"}
};
var mockApplications = Substitute.For<DbSet<Application>, IQueryable<Application>>();
((IQueryable<Application>)mockApplications).Provider.Returns(applications.AsQueryable().Provider);
((IQueryable<Application>)mockApplications).Expression.Returns(applications.AsQueryable().Expression);
((IQueryable<Application>)mockApplications).ElementType.Returns(applications.AsQueryable().ElementType);
((IQueryable<Application>)mockApplications).GetEnumerator().Returns(applications.AsQueryable().GetEnumerator());
mockApplications.When(q => q.Add(Arg.Any<Application>()))
.Do(q => applications.Add(q.Arg<Application>()));
mockApplications.When(q => q.Remove(Arg.Any<Application>()))
.Do(q => applications.Remove(q.Arg<Application>()));
context.Applications = mockApplications;
I can't see you classes but you need to create interfaces with virtual members and have your code call the class by the interface, then you will be able to mock out the class.
Below I have some code that that I cannot Unit test because it tries to read settings from IIS7 and unfortunately our nightly build machine does not have IIS7. The only thing I can think of is to pass the ServerManager into the method, but then again in the caller I will have a ServerManager that will make that method unable to be unit tested. We use MOQ for our Mock library.
public ISection GetCurrentSettings(string location, Action<string> status)
{
#region Sanity Checks
if (string.IsNullOrEmpty(location))
{
throw new ArgumentNullException("location");
}
if (status == null)
{
throw new ArgumentNullException("status");
}
#endregion
ISection section = null;
_logger.Debug(string.Format("Retrieving current IIS settings for app at {0}.", location));
status("Getting current IIS settings.");
using (ServerManager manager = new ServerManager())
{
var data = (from site in manager.Sites
from app in site.Applications
from vdir in app.VirtualDirectories
where vdir.PhysicalPath.Equals(location, StringComparison.CurrentCultureIgnoreCase)
select new {Website = site, App = app}).SingleOrDefault();
if (data == null)
{
_logger.Debug(string.Format("Could not find an application at {0} in IIS. Going to load the defaults instead.", location));
//ToDo possibly load defaults
}
else
{
_logger.Debug(string.Format("Application found in IIS with website: {0} and a path of {1}", data.Website.Name, data.App.Path));
int port =
data.Website.Bindings.Where(b => b.EndPoint != null).Select(b => b.EndPoint.Port).Single();
section = new IISSection
{
ApplicationPoolName = data.App.ApplicationPoolName,
VirtualDirectoryAlias = data.App.Path,
WebsiteName = data.Website.Name,
WebsiteRoot = data.App.VirtualDirectories[0].PhysicalPath,
Port = port.ToString(CultureInfo.InvariantCulture),
WillApply = true,
AnonymousUser = _userService.GetUserByType(UserType.Anonymous)
};
}
return section;
}
Without rewriting your code fully, the general idea would be to pass in an ISettingReader* (implemented as IisSettingReader), which would expose methods that would get the data you need from IIS. Then, you can stub in the ISettingReader to return what you need, by passing ISettingReader into the method/class
*Or, IServerManager as it seems to be the current name, but I am not sure if that is IIS specific
UPDATE
To be more specific, as Darin Dimitrov elaborated, you need to pull all of the dependencies outside of the method and pass them in via parameter/constructor/property injection. This will require a rewrite of the code as it stands in its current state.
If not (and I do suggest a rewrite), then you can use something like TypeMock, which supposedly can fake the dependencies INSIDE a class, but I have not used this myself and only know what I have read on it.
Use Moq.
This will allow you to create a mocked version of ISettings rather than having to create a real one. It has the added advantage of allowing you to specify your own functionality as well.