2

Some people decided to close my previous question, but the question they linked (What is a NullReferenceException, and how do I fix it?) did not have an answer. This question is fundamentally different since the enumerable is populated. It is not null. Just as the first answer stated, I placed "strategic breakpoints" and checked the variables.

I'm debugging a XUnit test and it turns out that in my business logic the iteration variable in the foreach loop is throws an exception "Object Reference not set to instance of object". However, the list over which the iteration is happening is NOT null. I can see that when I'm debugging. Here is the code:

Business logic:

List<string> regionArray = new List<string>();
if (someCondition)
{
    regionArray = _utils.GetRegions(someParam); // this is not returning null
}
foreach (var region in regionArray)
{
      var query = from dataSet in myDataSets
                  where dataSet.Location == region
                  select dataSet;
      var queryResult = query.FirstOrDefault();
      if (queryResult == null)
      {
           // do stuff
      } else if (queryResult.State != States.Provisioned)
      {
           // do stuff
      }
}

Here is how I am mocking the _utils.GetRegions call, but I dont think thats the problem.

private Mock<IUtils> _mockRegionUtils;

[Fact]
public void ItWorks()
{
    // do stuff
    _mockRegionUtils = new Mock<IUtils>();
    _mockRegionUtils.Setup(utils => utils.GetRegions(It.IsAny<ISomeParam>())).Returns(new List<string>() {"america", "china"});
    // call business logic
}

I have checked all the types in the debugger. regionArray.GetType() returns {System.Collections.Generic.List`1[System.String]}. when I type region into the console however, i get:

region
'region' threw an exception of type 'System.NullReferenceException'

how is this possible?

EDIT: fixed a typo above, sorry about that. Something weird though, so if I reassign the value of regionArray to be an inline list, it still fails. But if I define a new inline list and iterate over that, the looping works fine.

List<string> regionArray = new List<string>();
if (someCondition)
{
    regionArray = _utils.GetRegions(someParam); // this is not returning null
}
regionArray = new List<string>() {"china", "america"};
List<string> temp = new List<string>() {"foo", "bar"}
foreach (var region in regionArray)
{
      // region still throws null reference exception
      foreach (var tempVar in temp)
      {
          var c = tempVar; // this works. tempvar is never null.
      }
      var query = from dataSet in myDataSets
                  where dataSet.Location == region
                  select dataSet;
      var queryResult = query.FirstOrDefault();
      if (queryResult == null)
      {
           // do stuff
      } else if (queryResult.State != States.Provisioned)
      {
           // do stuff
      }
}

EDIT 2: So I tried iterating over the regionArray in the same way just before the logic above, and it worked fine.

List<string> regionArray = new List<string>();
if (someCondition)
{
    regionArray = _utils.GetRegions(someParam); // this is not returning null
}

foreach (var region in regionArray)
{
      var c = region; // this works
}

foreach (var region in regionArray)
{
      // region throws null reference exception
      var query = from dataSet in myDataSets
                  where dataSet.Location == region
                  select dataSet;
      var queryResult = query.FirstOrDefault();
      if (queryResult == null)
      {
           // do stuff
      } else if (queryResult.State != States.Provisioned)
      {
           // do stuff
      }
}

so most likely, it is not a problem with the moq object. based on @Iliar's suggestion, I will see if regionArray gets modified, but at first glance since regionArray is not used within the loop, my answer would be "no".

Jeremy Fisher
  • 2,510
  • 7
  • 30
  • 59
  • What is `myDataSets`? Does each item of this collection has non-null `Location` property value? How many times is method with `foreach` over `regionArray` called? – Iliar Turdushev May 22 '20 at 04:38
  • myDataSets is an array of a custom object. But the error is being thrown on the looping variable `region`. I verified that if I manually create a list in the business logic and loop over it that way, the looping variable is populated correctly. There is something wrong with the `regionArray` in my opinion. The foreach would be called for how many elements are in regionArray, could be 1 or 2 but shouldn't matter. – Jeremy Fisher May 22 '20 at 04:47
  • To be clear, what I did was instead of using `regionArray`, what I tested was I made a List with some random elements right in the business logic and then looped over that list, and it looped as expected. So in my opinion this seems to be a problem with iterating over a value returned by moq or something like that. – Jeremy Fisher May 22 '20 at 04:48
  • 4
    Are you really interested in treating this like puzzle to come up with a type that would cause NRE for you there (on option is to use `DebuggerDisplayAttribute` to make VS show totally unrelated values) or you going to provide [MCVE] at some point? – Alexei Levenkov May 22 '20 at 04:53
  • I tried your sample. Here is code: https://dotnetfiddle.net/JUZi8v. I failed to reproduce the problem. `Moq version 4.0.10827.0`. What version of the `Moq` do you use? Do you have additional setups on `_mockRegionUtils` object? – Iliar Turdushev May 22 '20 at 05:12
  • Mine is 4.10.1 it seems. But let me check if there is some other mockRegionUtils or some other setup happening on the object. – Jeremy Fisher May 22 '20 at 05:17
  • Do you change `regionArray` variable inside foreach loop? If there is a code that accesses this variable inside foreach, please, share it. – Iliar Turdushev May 22 '20 at 05:19
  • I don't think I do, but let me check more thoroughly. I'll update the question with more details of what the //do stuff is but it's basically just throwing an exception. Something interesting though, right before the loop above, I wrote another foreach loop on `regionArray` that was the exact same looping, but did nothing to the `region` variable, and it worked. So you might be right, maybe i am modifying the regionArray variable in some way but I highly doubt it. – Jeremy Fisher May 22 '20 at 05:26
  • How does the call stack look like when the NullReferenceException is raised? – Klaus Gütter May 22 '20 at 05:37
  • No exception is actually thrown. Error shows up in a tooltip when I hover on region variable: ```'region' threw an exception of type 'System.NullReferenceException' Data [IDictionary]:{System.Collections.ListDictionaryInternal} HResult [int]:-2147467261 HelpLink [string]:null InnerException [Exception]:null Message [string]:"Object reference not set to an instance of an object." Source [string]:"9dd66c33104045bba27ad3fc9fb95185" StackTrace [string]:" at <>x.<>m0(d__13 <>4__this)" TargetSite [MethodBase]:{System.String <>m0(d__13)} Static members ...``` – Jeremy Fisher May 22 '20 at 05:48
  • And then if I enter `region` into the debug console, it will print ```'region' threw an exception of type 'System.NullReferenceException'``` – Jeremy Fisher May 22 '20 at 05:50
  • Can you show what's actually in 'regionArray' after calling _utils.GetRegions(someParam)? – sr28 May 22 '20 at 07:34

3 Answers3

3

Update: I got around this issue by renaming the region looping variable to a different name. As it turns out, I was doing another foreach (var region ...) loop earlier in my code. I spoke to some senior colleagues as to why these 2 names would conflict with each other, and they said maybe it was some issue with symbols in VSCode and not really with my actual code. Thank you all for your help!

There was a lot of info in this thread, so just to summarize here are a few bulletpoints in case it is helpful to someone else in the future:

  • When debugging an XUnit test, I was seeing my looping variable in my foreach displaying the following info in the tooltip 'region' threw an exception of type 'System.NullReferenceException' Data [IDictionary]:{System.Collections.ListDictionaryInternal} HResult [int]:-2147467261 HelpLink [string]:null InnerException [Exception]:null Message [string]:"Object reference not set to an instance of an object." Source [string]:"9dd66c33104045bba27ad3fc9fb95185" StackTrace [string]:" at <>x.<>m0(<IngestEvents>d__13 <>4__this)" TargetSite [MethodBase]:{System.String <>m0(<IngestEvents>d__13)} Static members ....

  • even as I stepped INTO the loop, the tooltip for region was still showing the above, and when I typed region into the console, I got 'region' threw an exception of type 'System.NullReferenceException'.

  • The above 2 points led me to believe region was null. However, through @IVSoftware 's help, I verified that region was not actually null, because the assertion was passing.

  • I then looked at the rest of my code, and as a random guess, I tried renaming the looping variable region to something else. When I did, region was correctly set to the elements of the list.
Jeremy Fisher
  • 2,510
  • 7
  • 30
  • 59
0

Hi I really hope to be helpful. First I will answer your question "how is this possible?" and I think I can explain why your edited question with the inline list works. True, the GetRegions method returns a list that is not null. Sure, if you call GetType() on this it correctly identifies it as a "list of strings". I believe however, that the GetRegions method is returning a list that contains at least one null value. And you prove it out yourself when you added the edit, because you say this works:

regionArray = new List<string>() {"china", "america"};

But try making a list with three values like this where one of them is null and probably the loop will fail again.

regionArray = new List<string>() {"china", null, "america"};

This suggests a bug inside the GetRegions method is putting a null value into the list that it is returning. Adding these two lines at the beginning of your loop might go a long way to identifying the issue:

        foreach (var region in regionArray)
        {
            // Look for this in the Visual Studio 'Output' window 
            System.Diagnostics.Debug.WriteLine(region == null ? "Null" : region);
            // You believe that region is not null. So 'assert' that 
            // this evaluates to True and if it doesn't the debugger will break here.
            System.Diagnostics.Debug.Assert(region != null, "Break on this line if region is null");
    ...

From what I can tell, I would look inside your GetRegions(someParam) method and see if it's inserting a null into the list somewhere. Good luck!

IVSoftware
  • 5,732
  • 2
  • 12
  • 23
  • thanks for the answer! I made an update in my question where I iterated over the regionArray before my actual business logic and that worked fine. So perhaps there is some modification of the array happening but I highly doubt it. I don't think any of the values are actually null but i'll add a check in there and put a breakpoint to see if it hits. – Jeremy Fisher May 22 '20 at 05:52
  • 1
    var c = region; // this works || Yes this will worl because it doesn't care if region is null, right? The variable 'c' will just be null in this case. – IVSoftware May 22 '20 at 05:54
  • no, i mean that region itself is not null. but when iterating over regionArray in the actual business logic, it is null. Maybe I'm not explaining it correctly but when stepping through the debugger once I enter the loop, even before doing ANYTHING, `region` is already getting an "Object reference not set to instance of object" issue. – Jeremy Fisher May 22 '20 at 16:39
  • Hi @Jeremy. Did you try inserting those two Diagnostics.Debug statements and running it? That would help us help you. Take out all your breakpoints and run it with the System.Diagnostics.Debug statements in place. Tell me what you see please. – IVSoftware May 22 '20 at 16:50
  • The output did NOT print "Null", and the assertion passed. but when I hover over `region` it clearly shows an error `'region' threw an exception of type 'System.NullReferenceException' Data [IDictionary]:{System.Collections.ListDictionaryInternal} HResult [int]:-2147467261 HelpLink [string]:null InnerException [Exception]:null Message [string]:"Object reference not set to an instance of an object." Source [string]:"0056d25a9a2d4408a94d85027b2acb84" StackTrace [string]:" at <>x.<>m0(d__13 <>4__this)" TargetSite [MethodBase]:{System.String <>m0(d__13)}` – Jeremy Fisher May 22 '20 at 17:26
0
List<string> regionArray = new List<string>();
if (someCondition)
{
    regionArray = _utils.GetRegions(someParam); // this is not returning null
}

this will override the regionArray instance, to the GetRegions instance, so creating new List<string> instance is useless.

What you want is :

List<string> regionArray = new List<string>();
if (someCondition)
{
    regionArray.AddRange(_utils.GetRegions(someParam)); 
}

which is using this new instance, and add the resulted elements inside it. If _utils.GetRegions(someParam) returns empty set, then regionArray will not be null, but it'll be empty regionArray.Count == 0.

this is also can be done using ToList as well:

var regionArray = _utils.GetRegions(someParam).ToList();

now , you need to check regionArray after that :

if(regionArray.Count == 0) 
{
    // do something 
}

Or using Linq

if(!regionArray.Any())
{
    // do something 
}

if the collection is not empty then you can iterate through the list and validate each string inside this list before you process it:

foreach (var region in regionArray)
{
    // check if the element is null or empty 
    // if true, will skip this element and go to the next one
    if(string.IsNullOrEmpty(region)) { continue; } // go to the next iteration

    // get the results    
    var queryResult = myDataSets.FirstOrDefault(x=> x.Location == region);

    if (queryResult == null)
    {
       // do stuff
    } 
    else if (queryResult.State != States.Provisioned)
    {
       // do stuff
    }
}
iSR5
  • 3,274
  • 2
  • 14
  • 13
  • Hi, sorry the code I put in my question is oversimplified so that I unnecessary details didn't get in the way of the actual problem. Yes, I am using `ToList` already and checking if the list is empty before i enter the loop. As I said in one of my edits, the list is not empty and iterations over the list that don't do anything work perfectly well. It's just the above iteration that runs a query that is actually throwing this exception. – Jeremy Fisher May 22 '20 at 17:38
  • @iSR5 I dunno if Jeremy will like it, but I do! Good catch on the redundant initialization of regionArray. The way I see it one goal is to simply get the OP's code running, but also familiarize a little with strategic try-catch and diagnostics.debug stuff. Anyway, just wanted to give you kudos on your code-nice and clean. I know we're all just trying to help. I think I'm throwing in the towel on this one, though. – IVSoftware May 22 '20 at 17:40
  • @IVSoftware I don't blame you, at this point I'm convinced it's a red herring, some strange behavior with the test environment im using or the IDE. I wrote another loop iterating over the same list and although it initially shows that error stack trace, it corrects itself and reassigns region to be the values of the list. – Jeremy Fisher May 22 '20 at 17:58
  • @JeremyFisher if you see you did everything as expected, then it's UnitX bug, and your second test on the same collection proves that (as it shows the error stack trace). You need to retest it in another Test Unit to prove it though. – iSR5 May 22 '20 at 18:45
  • @IVSoftware we are in the same boat mate ;). – iSR5 May 22 '20 at 18:47