How to MOQ Sitecore User object? - c#

I need to do Moq test for the below original method
public virtual User GetBOUser(string domainName, string userName, string password, Login model)
{
try
{
if (Sitecore.Security.Accounts.User.Exists(domainName + #"\" + userName))
{
User user = Sitecore.Security.Accounts.User.FromName(domainName + #"\" + userName, true);
return user;
}
}
catch (Exception ex)
{
Log.Error("GetBOUser Error - " + ex.ToString(), this);
}
return null;
}
The sample unit test method I have created is below
[TestMethod]
public void LoginApiTest()
{
User use = null;
var mockapi = new Mock<ApiController>();
mockapi.Setup(x => x.GetBOUser("", "", "", new Login())).Returns(use);
var dataObject = mockapi.Object;
Assert.AreEqual<User>(use, dataObject.GetBOUser("", "", "", new Login()));
}
Here I am able to check only for null in the return type of test method, But how to return a actual user object ?

Before I address your question, I need to point out that your sample test is not actually testing anything. You are mocking your system under test, telling it to return null and then asserting that it returned null. This will not execute your production code.
You should be creating a normal instance of your ApiController. However, that highlights another issue in that your method has a dependency on Sitecore's static security API. If you want your code to be testable, you will need to create an interface and wrapper class for that API and make your controller accept an instance of your interface in its constructor so that you can provide a mock instance for your test but a real instance in production.
Your wrapper class should only call the static API and should contain no logic. That way you don't really need to test it. When you set up the mock instance of your interface you can create a mock IPrincipal and pass it to User.FromPrincipal to prevent it from actually trying to lookup the user in the database. If you were to name your interface IUserService, your test would end up looking something like this:
[TestMethod]
public void GetBOUser_WithExistingUser_ReturnsUser()
{
// Arrange
var name = "Joe";
var domain = "extranet";
var fullName = domain + #"\" + name;
var principal = new Mock<IPrincipal>();
principal.Setup(p => p.Identity.Name).Returns(fullName);
var joeUser = User.FromPrincipal(principal);
var userService = new Mock<IUserService>();
userService.Setup(u => u.Exists(fullName)).Returns(true);
userService.Setup(u => u.FromName(fullName)).Returns(joeUser.Object);
var controller = new ApiController(userService.Object);
// Act
var result = controller.GetBOUser("extranet", "Joe", "password", new Login());
// Assert
Assert.AreEqual(fullName, result.Name);
}
One other thing... Your GetBOUser method accepts a password, but does not validate it before returning a user. That is very misleading to consumers of your API. If you accept a password you should make sure it is correct before returning the requested user object.

Related

Using Moq the Start() method keeps retuning a null object

I'm having trouble figuring out why I can't test my driver variable, it keeps coming back null with I call Start(). I basically would like to access that variable and test it.
My current test that isn't working:
[TestMethod]
public void Start_Default_IsChrome2()
{
var dummyManager = new Mock<IRemoteDriver>();
var mockDriver = new Mock<IWebDriver>();
dummyManager.Setup(x => x.CreateRemoteWebDriver(new ChromeOptions()))
.Returns(It.IsAny<RemoteWebDriver>());
var session = new SauceSession(dummyManager.Object);
//The Start() keeps returning a null object
var driver = session.Start();
var capabilities = driver.Capabilities;
capabilities.GetCapability("browserName").Should().Be("chrome");
}
Dependency to be mocked
public interface IRemoteDriver
{
IWebDriver CreateRemoteWebDriver(ChromeOptions chromeOptions);
}
Subject Under Test
public SauceSession(IRemoteDriver driverManager)
{
remoteDriverManager = driverManager;
}
public RemoteWebDriver Start()
{
sauceUserName = Environment.GetEnvironmentVariable("SAUCE_USERNAME", EnvironmentVariableTarget.User);
sauceAccessKey = Environment.GetEnvironmentVariable("SAUCE_ACCESS_KEY", EnvironmentVariableTarget.User);
sauceOptions = new Dictionary<string, object>
{
["username"] = sauceUserName,
["accessKey"] = sauceAccessKey
};
var chromeOptions = new ChromeOptions
{
BrowserVersion = "latest",
PlatformName = "Windows 10",
UseSpecCompliantProtocol = true
};
chromeOptions.AddAdditionalCapability("sauce:options", sauceOptions, true);
//This keeps returning a null
return (RemoteWebDriver)remoteDriverManager.CreateRemoteWebDriver(chromeOptions);
}
If it helps, the Concrete implementation works just fine and that test looks like this:
[TestMethod]
public void Start_Default_IsChrome()
{
var session = new SauceSession();
var driver = session.Start();
var capabilities = ((RemoteWebDriver)driver).Capabilities;
capabilities.GetCapability("browserName").Should().Be("chrome");
}
Everything else is the same except the object that I set here:
public SauceSession()
{
remoteDriverManager = new ConcreteRemoteWebDriver();
}
class ConcreteRemoteWebDriver : IRemoteDriver
{
public IWebDriver CreateRemoteWebDriver(ChromeOptions chromeOptions)
{
return new RemoteWebDriver(new Uri("https://ondemand.saucelabs.com/wd/hub"),
chromeOptions.ToCapabilities(), TimeSpan.FromSeconds(600));
}
}
Here's the RemoteWebDriver:
public class RemoteWebDriver : IWebDriver, ISearchContext, IDisposable, IJavaScriptExecutor, IFindsById, IFindsByClassName, IFindsByLinkText, IFindsByName, IFindsByTagName, IFindsByXPath, IFindsByPartialLinkText, IFindsByCssSelector, ITakesScreenshot, IHasInputDevices, IHasCapabilities, IHasWebStorage, IHasLocationContext, IHasApplicationCache, IAllowsFileDetection, IHasSessionId, IActionExecutor
You've done your setup improperly.
dummyManager.Setup(x => x.CreateRemoteWebDriver(new ChromeOptions()))
.Returns(It.IsAny<RemoteWebDriver>());
Two things here:
You're matching on precisely an instance of new ChromeOptions(). When determining which object to return, Moq will check if the arguments passed to CreateRemoteWebDriver are the same as the ones provided in the setup. It's unlikely that
new ChromeOptions
{
BrowserVersion = "latest",
PlatformName = "Windows 10",
UseSpecCompliantProtocol = true
};
and
new ChromeOptions()
will evaluate as equal, meaning that this setup won't be matched.
You probably just meant to use It.IsAny<ChromeOptions>(), like this
dummyManager.Setup(x => x.CreateRemoteWebDriver(It.IsAny<ChromeOptions>()))
The second issue is that your return value is explicitly null.
It.IsAny<T>() always returns the default value for T. The It methods are all only used for argument matching within the Setup expression. If you use them outside of a setup expression, you're just going to get the default value of the generic argument, which in this case is a null value. It's a shame that the Moq library doesn't make this improper usage a loud error message. Consequently, you'll need to provide an actual instance of RemoteWebDriver as a return value. (Or if you can decouple the implementation from a particular concrete type, you could just return something that implements IWebDriver.)
That value could be another Mock object, potentially, but it needs to be something you've either created ahead of time, or something that can be created via the Returns callback.
A correct setup might look something like:
var mockDriver = new Mock<RemoteWebDriver>();
dummyManager.Setup(x => x.CreateRemoteWebDriver(It.IsAny<ChromeOptions>()))
.Returns(mockDriver.Object); //This could throw an exception if RemoteWebDriver needs arguments.
A small caveat is that you will actually create a RemoteWebDriver instance as a result. If that has undesirable side effects (such as creating a chrome window), you will want to consider changing your strategy from using a particular concrete type to some interface or abstract class. If you did that, the setup might look something like the below:
var mockDriver = new Mock<IWebDriver>();
dummyManager.Setup(x => x.CreateRemoteWebDriver(It.IsAny<ChromeOptions>()))
.Returns(mockDriver.Object);

How to run WebServices in a unit test

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.

Unit testing functions which access Entity Database

Trying to unit test functions which access a entity framework. So i tried to put all the entity code into the test function below? However it stops at the Linq statement; obviously trying to access the database is too much drama for it. Maybe a work around would be too to create a replica database within the unit test function based on sql lite or compact;(Its not a big database anyways) then execution would not have to leave the test function? Is this possible and how would i implement it?
public void RetreiveKeyFnTest()
{
StegApp target = new StegApp(); // TODO: Initialize to an appropriate value
string username = "david"; // TODO: Initialize to an appropriate value
string password = "david1"; // TODO: Initialize to an appropriate value
string ConnectionString = ConfigurationManager.ConnectionStrings["DatabaseEntities"].ToString();
var dataContext = new DatabaseEntities(ConnectionString);
var user = dataContext.Users.FirstOrDefault(u => u.Username.Equals(username) && u.Password.Equals(password));
Assert.IsNotNull(user);
//target.RetreiveKeyFn(username, password);
//Assert.IsInstanceOfType(target.RetreiveLogs,typeof(DataAccess));
//Assert.IsInstanceOfType(target.p);
//Assert.IsNotNull(target.RetreiveLogs.AuthenitcateCredentials(username,password));
//Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
Below is the code i am trying to test:
public void RetreiveKeyFn(string username, string password)
{
BusinessObjects.User p = RetreiveLogs.AuthenitcateCredentials(username,password);
if (p != null)
{
if (RetreiveLogs.RetreiveMessages(p.UserId) == null)
{
DisplayLogs.Text = "Sorry No messages for you recorded in Database, your correspondant might have chose not to record the entry";
}
else
{
MessageBox.Show("LogId = " + RetreiveLogs.RetreiveMessages(p.UserId).LogId + "\n" +
"UserId = " + RetreiveLogs.RetreiveMessages(p.UserId).UserId + "\n" +
"Message Key = " + RetreiveLogs.RetreiveMessages(p.UserId).MessageKey + "\n" + "PictureId = " + RetreiveLogs.RetreiveMessages(p.UserId).PictureId +
" Date & time = " + RetreiveLogs.RetreiveMessages(p.UserId).SentDateTime);
DisplayLogs.Visible = true;
}
}
else
{
MessageBox.Show("Please enter your correct username and password in order to retreive either key, image or both from Databse");
}
}
First, you should be able to access the same database in your test application as the one you're using in your main/actual application. You just need to make sure that your Test project contains your connection string in its own App.config.
The initialization of the context should be done either inside your StegApp(), or you should be able to pass a context to your StegApp() from a different scope. From what I read of your code, your StegApp() will not be able to access the dataContext variable you created.
Your test for null user already happens inside the RetrieveKeyFn() under the AuthenticateCredentials() method so there's no need for the first "Assert.IsNotNull(user)". I would recommend separating your business logic for RetrieveKeyFn from your UI behaviors so that you can easily do unit tests. You can bind the "Messagebox" operations to say a button click event handler which calls just RetrieveKeyFn(). I would suggest maybe something like this:
public class StegApp
{
public DatabaseEntities context;
//other properties
public StegApp()
{
//assuming your DatabaseEntities class inherits from DbContext.
//You should create other constructors that allow you to set options
//like lazy loading and mappings
this.context = new DatabaseEntities();
}
//ASSUMING YOUR RetrieveLogs.RetrieveMessages() function returns
//a Message object. replace this type with whatever type the
//RetrieveLogs.RetrieveMessages() method returns.
public Message RetrieveKeyFn (string username, string password)
{
BusinessObjects.User p = RetreiveLogs.AuthenitcateCredentials(username,password);
if (p != null)
{
var message = RetrieveLogs.RetrieveMessages(p.UserId);
if (message == null)
// handle behavior for no messages. In this case
// I will just create a new Message object with a -1 LogId
return new Message {LogId =-1};
else
return message;
}
else
//handle behavior when the user is not authenticated.
//In this case I throw an exception
throw new Exception();
}
//on your button click handler, do something like:
// try
// {
// var message = RetrieveKeyFn(txtUsername.Text.Trim(), txtPassword.Text.Trim());
// if (message.LogId == -1)
// DisplayLogs.Text = "Sorry No messages for you recorded in Database, your correspondant might have chose not to record the entry";
// else
// {
// MessageBox.Show("Log Id = " + message.LogId)
// etc. etc. etc.
// }
// }
// catch
// {
// MessageBox.Show ("user is not authenticated");
// }
}
When you do your unit test, remember to have the appropriate configuration strings in your test project's App.Config If the app.config does not yet exist, go ahead and create one. You should create tests for all possibilities (i.e. 1) user is valid, you get the message, 2) user is valid, there are no messages, 3) user is invalid).
Here's an example for case 2
[TestMethod]
public void RetrieveKeyFnTest1()
{
StegApp target = new StegApp(); // this creates your context. I'm assuming it also creates your RetrieveLogs object, etc
var username = "UserWithNotMessages"; //this user should exist in your database but should not have any messages. You could insert this user as part of your TestInitialize method
var password = "UserWithNotMessagesPassword"; //this should be the proper password
var message = target.RetrieveKeyFn(username, password);
Assert.AreEqual (-1, message.LogId);
}
I got my unit tests to work fine. The mistake i had was not to copy the app.config file into the test project! Although to be honest i expected Visual studio would have done that anyways.

Return dynamic object

I have a simple data layer routine that performs a password update, the user passes in the following:
Current Password, New Password, Confirm New Password.
In my data layer (proc) checks a couple things such as:
Is the current password correct?
Is the new password and confirm password correct?
Has the new password been assigned in the past?
And so on...
Now I know I can simply create a class and returned a couple booleans:
public class UpdatePasswordResponse{
public bool CurrentPasswordCorrect {get;set;}
....(and so on)
}
But is there a way I can dynamically return that information to the biz layer in properties instead of creating a new class everytime (for every data layer routine)? I seem to remember thinking this was possible. I am pretty sure I read it somewhere but don't remember the syntax, can someone help me?
You can do this in .NET 4 with the use of the dynamic keyword.
The class you will want to return would be an ExpandoObject.
Basically, follow this pattern:
public object GetDynamicObject()
{
dynamic obj = new ExpandoObject();
obj.DynamicProperty1 = "hello world";
obj.DynamicProperty2 = 123;
return obj;
}
// elsewhere in your code:
dynamic myObj = GetDynamicObject();
string hello = myObj.DynamicProperty1;
If you just want to dynamically create a class you write:
public object MyMethod()
{
var result = new { Username = "my name", Password = "the password" };
return result;
}

Mocking SPServer.Local

I want to be able to mock the object that is returned by SPServer.Local but I can't seem to do it in typemock. At the moment when I debug, I see that SPServer.Local returns a null object of type SPServer. Shouldn't typemock be swapping out this instance with my fake instance? Is there something I'm doing wrong? The code runs fine on the sharepoint server.
[TestInitialize]
public void Setup()
{
fakeSite = Isolate.Fake.Instance<SPSite>(Members.ReturnRecursiveFakes);
Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
fakeServer = Isolate.Fake.Instance<SPServer>(Members.ReturnRecursiveFakes);
Isolate.Swap.NextInstance<SPServer>().With(fakeServer);
sharePointStorageRepository = new SharePointStorageRepository();
}
[TestMethod]
[Isolated]
public void CreateHRFolderMethodCreatesHRFolder()
{
// arrange
// some arrange logic here
// act
var actual = sharePointStorageRepository.Create();
// assert
Assert.AreEqual(expected, actual);
}
This is the bit of code that is being run:
internal static Guid GetSiteGuid(string serverRelativeUrl, string webApplicationName)
{
Guid? guid = null;
SPServer myServer = SPServer.Local;
foreach (var serviceInstance in myServer.ServiceInstances.Where(si => si.Service is SPWebService)){
var service = (SPWebService) serviceInstance.Service;
var webapp = service.WebApplications.SingleOrDefault(wa => wa.DisplayName == webApplicationName);
if (webapp != null){
var site = webapp.Sites.SingleOrDefault(wa => wa.ServerRelativeUrl == serverRelativeUrl);
if (site != null) guid = site.ID;
}
}
if (!guid.HasValue){
throw new FileNotFoundException(
String.Format(
"Cannot find Site Collection with WebApplication \"{1}\" and ServerRelativeUrl \"{2}\" running on \"{0}\"",
myServer.Address, webApplicationName, serverRelativeUrl));
}
return guid.Value;
}
Thanks all!
I don't work in SharePoint, but something I noticed: You're not actually mocking the return of SPServer.Local anywhere. I think that's the missing step. I'm also not entirely sure you need to SwapNextInstance since I don't see anywhere that is actually creating an SPServer object.
That would change your test code to:
[TestInitialize]
public void Setup()
{
// I don't see where you're using SPSite, so I assume it's in code
// not being shown; otherwise you can remove this.
fakeSite = Isolate.Fake.Instance<SPSite>(Members.ReturnRecursiveFakes);
Isolate.Swap.NextInstance<SPSite>().With(fakeSite);
fakeServer = Isolate.Fake.Instance<SPServer>(Members.ReturnRecursiveFakes);
// INSTEAD OF THIS: Isolate.Swap.NextInstance<SPServer>().With(fakeServer);
// DO THIS:
Isolate.WhenCalled(() => SPServer.Local).WillReturn(fakeServer);
sharePointStorageRepository = new SharePointStorageRepository();
}
That WhenCalled method will mean that any time anyone asks for SPServer.Local, it'll return your fake instance.
Note that I see in the code being tested that you get the ServerInstances property. I don't see any specific return values getting set up, so I assume you're controlling the rest of the stuff in the omitted "arrange" logic.

Categories

Resources