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.
Related
I am very new using Moq and unit tests.
I have been watching videos and reading different articles but I can't find the right example.
I am trying to mock a method that returns list of a object, but I'm getting this error
Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: 'Assert.AreEqual failed.
Expected:<System.Collections.Generic.List'1[BusinessLayer.Model.XSIPhoneTrunkDetails]>. Actual:<System.Collections.Generic.List'1[BusinessLayer.Model.XSIPhoneTrunkDetails]>.
This is my code:
Controller:
public List<XSIPhoneTrunkDetails> GetTrunksRange(LocationVM location)
{
//var response = false;
List<XSIPhoneTrunkDetails> newDetails = new List<XSIPhoneTrunkDetails>();
if (!string.IsNullOrWhiteSpace(location.TrunksRange) && !location.TrunksRange.Equals("[]"))
{
var xSIPhoneTrunkDetails = JsonConvert.DeserializeObject<List<XSIPhoneTrunkDetails>>(location.TrunksRange);
foreach (var item in xSIPhoneTrunkDetails)
{
if (item.RangeStart.Equals(item.RangeEnd))
item.RangeEnd = String.Empty;
if (item.XSIPhoneTrunkDetailsID <= 0)
newDetails.Add(item);
}
}
return newDetails;
}
Interface:
public interface ILocation
{
List<XSIPhoneTrunkDetails> GetTrunksRange(LocationVM location);
}
My unit test
[Fact]
public void ShouldGetTrunkRange()
{
using (var mock = AutoMock.GetLoose())
{
var phone = GetLocationVMsNotRange().First();
var item = new List<BusinessLayer.Model.XSIPhoneTrunkDetails>();
mock.Mock<ILocation>()
.Setup(x => x.GetTrunksRange(phone))
.Returns(item);
var cls = mock.Create<Location>();
var result = cls.GetTrunksRange(phone);
Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(item.ToList(), result.ToList());
}
}
What am I doing wrong?
Use loop to iterate through the list and assert individual objects like this,
Assert.True(item.Equals(result));
If you use loop:
Assert.True(item[0].Equals(result[0]));
the code is not exact just to give you an idea.
Also another thing you can assert the list count or property values of the objects using loop. Do what works, good luck!
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);
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.
I have unit test for a ServiceStack based service that passes on my windows workstation, however the TeamCity server which is on ubuntu/mono doesn't pass - other tests do run however, just one in particular.
This fails
[Test]
public void Post_Valid_Property_Should_Return_HttpStatus_OK()
{
var manager = new Mock<IRedisClientsManager>();
var redisClient = new Mock<IRedisClient>();
var redisTypedClient = new Mock<IRedisTypedClient<Share.Property.Model.Property>>();
manager.Setup(t => t.GetClient()).Returns(redisClient.Object);
redisClient.Setup(t => t.As<Share.Property.Model.Property>()).Returns(redisTypedClient.Object);
var sut = new SomeService(manager.Object);
var result = sut.Post(new PropertySaveRequest {Property = new Share.Property.Model.Property { Id = 1, OwnerId = 2 } });
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
Other tests not using mocks pass fine eg.
[Test]
public void Post_Invalid_Property_Should_Throw_Exception()
{
_container = new WindsorContainer()
.Install(new PropertyInstaller());
var service = _container.Resolve<IPropertyService>();
Assert.That(() => service.Post(new PropertySaveRequest { Property = new Share.Property.Model.Property{Id=-11, OwnerId = -14} }),
Throws.Exception.TypeOf<ArgumentOutOfRangeException>());
}
TeamCity log
My guess is the error is Moq related, as the other test can use Castle IOC without throwing.
Test(s) failed. System.Collections.Generic.KeyNotFoundException : The given key was not present in the dictionary.
Any ideas appreciated
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.