Method not entered in test case, works when ran normally - c#

I have a very weird issue and I am clueless as to what is causing this behaviour.
I will provide the relevant code of two classes, to keep this short.
Class A has a method that gets an xml document and puts that in an XDocument object, it uses a method from class B for this. Then it uses a different but similar method from class B to add some additional xml to the XDocument.
For some reason, when I run the test cases, the second method from Class B is never entered. Instead, it gives back null.
Here is the code:
First call to a method in class B, from class A
// Get view (xml) for current supplier
XDocument navigationView = ProductBUS.GetProductNavigationXDocument(suppID, viewName, selectedProductNavigationView);
Second call to a method in class B, from class A (this is the faulty one)
if (true)
{
navigationView = ProductBUS.AddOptionsToNavigationMenu(navigationView);
}
(The if(true) is placeholder code, will be changed in the future to a boolean value. This value needs to come from the DB, which is empty at this point.)
The methods in class B, 1 and 2 respectively.
public XDocument GetProductNavigationXDocument(Guid supplierID, String viewName, string selectedProductNavigationView)
{
// TODO : implementing full scale DI
INavigationViewFactory factory = new NavigationViewFactory();
INavigationView navigationXml = factory.Create(NavigationViewTypes.Product);
return navigationXml.GetNavigationXDocument(supplierID, viewName, selectedProductNavigationView);
}
And the second one (which is never entered)
public XDocument AddOptionsToNavigationMenu(XDocument menu)
{
menu.Element(XmlNames.NodeNames.MenuItems).Add(
new XElement(XmlNames.NodeNames.MenuItem,
new XAttribute(XmlNames.AttributeNames.ID, "27301D05-EBBB-4F39-AC74-B0E944F26C52"),
new XAttribute(XmlNames.AttributeNames.DefaultName, "Options"),
new XAttribute(XmlNames.AttributeNames.NameTranslationID, "9999"),
new XAttribute(XmlNames.AttributeNames.DisplayMode, "Options"),
new XElement(XmlNames.NodeNames.MenuItem,
new XAttribute(XmlNames.AttributeNames.ID, "27301D05-EBBB-4F39-AC74-B0E944F26C57"),
new XAttribute(XmlNames.AttributeNames.NameTranslationID, "9999"),
new XAttribute(XmlNames.AttributeNames.DefaultName, "Notifications"),
new XElement(XmlNames.NodeNames.Subscriptions,
new XElement(XmlNames.NodeNames.Subscription,
new XAttribute(XmlNames.AttributeNames.ID, "7"),
new XAttribute(XmlNames.AttributeNames.DefaultName, "Subscriptions"))))));
return menu;
}
I put breakpoints before, on and after the method call as well as inside of the method. If I run the application, I can debug right through the method. But when I run some test cases, that cover this bit of code, they method call is never entered. Instead, the code equals navigationView to null. I fixed this by putting the method's code in the first class, but I'd like to know why this is an issue.
EDIT
Here is the code of one of the test cases that causes the faulty behavior when tested.
[TestMethod]
public void GetProductDetailNavigationModel_ProductWith3FieldValuesAnd2Documents_MappedObjectIsNotNull()
{
// Arrange
DDSInterfaceBlock.Current.IsImpersonated = false;
Domain.Supplier supplier = new Domain.Supplier().Init();
Domain.User user = new Domain.User().Init().Create();
Domain.Language language = new Domain.Language().Init();
Domain.Product product = new Domain.Product().Init().LinkSupplier(supplier);
Domain.ProductOverview.ProductDetail productDetail = ArrangeProductDetailData(supplier, user, language, product);
XDocument document = ArrangeXDocument(productDetail);
ProductDetail svc = new ProductDetail();
IProduct bus = MockRepository.GenerateStub<IProduct>();
bus.Stub(t => t.GetProductNavigationXDocument(supplier.Id, null, string.Empty))
.IgnoreArguments()
.Return(document);
bus.Stub(t => t.GetProductDetail(supplier.Id, user.Id, product.Id, language.Id, language.Id))
.IgnoreArguments()
.Return(productDetail);
svc.ProductBUS = bus;
svc.UserBUS = MockRepository.GenerateMock<BUS.Interfaces.IUser>();
svc.UserBUS
.Stub(t => t.CheckIfUserInPRAGroup(Guid.Empty))
.IgnoreArguments()
.Return(false);
// Act
Domain.ProductOverview.ProductDetailNavigationModel result
= svc.GetProductDetailNavigationModel(supplier.Id, user.Id, product.Id, language.Id, language.Id);
// Assert
Assert.IsNotNull(result);
}

Could it be because you have a stub for GetProductNavigationXDocument, but there is not sub for AddOptionsToNavigationMenu

Related

adding records to inmemorydatabase just once

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.

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);

In C#, what will happen for an removed object's Action?

I want to make a deep Copy for my Class TreeNode. Here is my code:
public TreeNode(TreeNode node, GUIStyle inPointStyle, GUIStyle outPointStyle, Action<ConnectionPoint> OnClickInPoint, Action<ConnectionPoint> OnClickOutPoint)
{
this.rect = new Rect(node.rect);
this.style = new GUIStyle(node.style);
this.inPoint = new ConnectionPoint(this, ConnectionPointType.In, inPointStyle, OnClickInPoint);
this.outPoint = new ConnectionPoint(this, ConnectionPointType.Out, outPointStyle, OnClickOutPoint);
this.defaultNodeStyle = new GUIStyle(node.defaultNodeStyle);
this.selectedNodeStyle = new GUIStyle(node.selectedNodeStyle);
this.allDecorations = new List<GameObject>(node.allDecorations);
this.objs = new Dictionary<GameObject, IndividualSettings>(node.objs);
this.name = String.Copy(node.name);
this.RemoveClonedObj = new Action(node.RemoveClonedObj);
this.OnChangeView = new Action<TreeNode>(node.OnChangeView);
this.OnRemoveNode = new Action<TreeNode>(node.OnRemoveNode);
this.OnCopyNode = new Action<TreeNode>(node.OnCopyNode);
this.PreviewTree = new Action<TreeNode, bool> (node.PreviewTree);
}
However, the Rider gave me the warning:
It seems the Rider was saying that my "new" is meaningless.
If I follow Rider's instruction, usethis.RemoveClonedObj = node.RemoveClonedObj; what will happen for my copyed TreeNode's Actions aftering removing the orginal TreeNode? Will they be removed as well? If so, why does Rider give me such warning?
In C# 2.0 or above, the following codes are equivalent (DelegateType is a delegate type, as its name suggests):
newDelegate = new DelegateType(oldDelegate);
newDelegate = oldDelegate;
(See MSDN - How to: Declare, Instantiate, and Use a Delegate (C# Programming Guide))
Also, Microsoft specifies (see here) that such operation will always create a new instance of DelegateType, which has the same invocation list as the oldDelegate. They do not refer to the same object (don't be confused by the = assignment):
The binding-time processing of a delegate_creation_expression of the form new D(E), where D is a delegate_type and E is an expression, consists of the following steps:
If E is a method group, the delegate creation expression is processed in the same way as a method group conversion (Method group conversions) from E to D.
If E is an anonymous function, the delegate creation expression is processed in the same way as an anonymous function conversion (Anonymous function conversions) from E to D.
If E is a value, E must be compatible (Delegate declarations) with D, and the result is a reference to a newly created delegate of type D that refers to the same invocation list as E. If E is not compatible with D, a compile-time error occurs.
So regarding your question
What will happen for my copyed TreeNode's Actions aftering removing the orginal TreeNode? Will they be removed as well?
Nothing will happen to them. They will not be removed.
By the way, since you are trying to make a deep copy of your tree-node, I suspect whether it is the correct way. Though you have created a new instance of your delegate, the class instance associated with it (the instance on which member methods will be invoked) stays the same.
Do not link instance methods to each other. This will lead to memory leaks.
Even after the original node is removed and no longer needed by your code, due to the reference from the copy the original instance will live in the memory and not be garbage collected.
I suspect this is not what you want, Test code for this
class Program
{
static void Main(string[] args)
{
First t = new First();
Second s = new Second();
t.Print = s.TestMethod;
s.test = "change";
s = null;
t.Print("Hell"); // can debug and see that the function call goes through and string test is = "change"
}
}
public class First
{
public string s;
public Action<string> Print;
}
public class Second
{
public string test = "created";
public void TestMethod (string test)
{
var res = "hello" + test + test;
}
}
Either your methods on the node should be part of the Node object, this way you do not have to assign them to new nodes, or they should be in a separate class, preferably static, so that creation of new nodes does not lead to a memory issue.

Mocking DBContext in EF 6 not working as expected

I'm writing a unit test that mocks out the IDbSet properties of the DbContext; however, I seem to be getting some strange results.
Here's the code where I mock out the data:
var myData1 = new List<MyData1>()
{
new MyData1() { Id = 2, Test = "test" },
new MyData1() { Id = 3, Test = "test" },
new MyData1() { Id = 4, Test = "test" }
}.AsQueryable();
IDbSet<MyData1> myDbSet = Substitute.For<IDbSet<MyData1>>();
myDbSet.Provider.Returns(myData1.Provider);
myDbSet.Expression.Returns(myData1.Expression);
myDbSet.ElementType.Returns(myData1.ElementType);
myDbSet.GetEnumerator().Returns(myData1.GetEnumerator());
myDbContext.MyData1.Returns(myDbSet);
. . .
myDbContext.MyData2.Returns(myDbSet2);
. . .
myDbContext.MyData3.Returns(myDbSet3);
When I come to interrogate the data; for example:
using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
var myData = myDbContext.MyData1.ToList();
}
_dbContextGenerator is simply substituted to return my test DbContext, instead of the real one:
IDbContextGenerator dbContextGenerator = Substitute.For<IDbContextGenerator>();
dbContextGenerator.GenerateDbContext().Returns(myDbContext);
This seems to work; however, if I call the method twice; it doesn't. So:
using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
myData = myDbContext.MyData1.ToList();
}
Assert.Equal(3, myData.Count());
Works; however:
using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
myData = myDbContext.MyData1.ToList();
}
using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
myData = myDbContext.MyData1.ToList();
}
Assert.Equal(3, myData.Count());
Does not. I get no data returned; however, if I debug the line, I can see that:
myDbContextMyData1.Provider
Contains the correct test data.
Please could someone point me in the right direction on this?
The problem is
myDbSet.GetEnumerator().Returns(myData1.GetEnumerator());
Which will return the same enumerator instance every time it is called.
And since the enumerator is forward only, it will need to be reset. Calling it more than once without resetting will exhibit the described behavior of only being able to enumerate once because the pointer is at the end.
Use a delegate call back so that it is invoked every time the mock is called to return a new enumerator every time GetEnumerator() is called.
myDbSet.GetEnumerator().Returns(_ => myData1.GetEnumerator());
Now enumerating the mock multiple times should then behave as expected.

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