0

I am trying to use the yield command to update some methods but I am running into an issue that I don't understand. There is some logic in this method (checking for type of null), if that is the case then I write to a log and yield break. Which does exactly what I want, however in my unit test it is saying that the log function was never called. I am ok with not logging in this situation but I want to know why I can't or if I am doing something wrong.

Here is the code:

public IEnumerable<Ixxx> GetTypes(Type type)
    {
        if (type == null)
        {
            log.WriteRecord("log error", "LogName", true);
            yield break;
        }

        lock (blockingObject)
        {
            foreach (Ixxx item in aDictionary.Values)
            {
                if (item.Type.Name == type.Name)
                {
                    yield return item;
                }
            }
        }
    }

The unit test that is failing is claiming log.WriteRecord was never called. Here is that unit test:

[TestMethod]
    public void TestMethod()
    {
        // Arrange
        mockLog.Setup(a => a.WriteRecord(It.IsAny<string>(), It.IsAny<string>(), true)).Returns(true);

        // Act
        sut.GetTypes(null);

        // Assert
        mockLog.Verify(a => a.WriteRecord(It.IsAny<string>(), It.IsAny<string>(), true), Times.Once());
    }

When I was making a local copy (List) this test passed however now that I am using yield it appears I can not make any function calls within this method? Thanks for any help!

svick
  • 236,525
  • 50
  • 385
  • 514
ShaffDaddy
  • 63
  • 1
  • 9
  • 2
    Your setup is for and overload of `WriteRecord(string, string, true)`, but in your method you call an overload of `WriteRecord(string)`. Did you change which overload you called when you added the `yield`? – juharr Mar 12 '15 at 16:16
  • Also, you need to make the setup verifiable, with `mockLog.Setup(a => ...).Returns(true).Verifiable();` – Erik Mar 12 '15 at 16:22
  • 3
    Using a lock that contains a yield return is extremely dangerous and can easily lead to a dead lock http://stackoverflow.com/questions/4608215/does-the-c-sharp-yield-free-a-lock – John Taylor Mar 12 '15 at 16:22
  • The mock is fine, that is a mess up by me. I was trying to make it have less 'sensitive' information in it, so I removed some the parameters forgetting that it needs them. Sorry for the confusion – ShaffDaddy Mar 12 '15 at 17:20
  • Also thanks for the heads up on the lock, I wasn't sure what side effect yield would have on that – ShaffDaddy Mar 12 '15 at 17:20
  • Then am I stuck creating a local list and adding to it, since my dictionary has to be thread safe? – ShaffDaddy Mar 12 '15 at 17:30
  • You might be able to use a ConcurrentDictionary and avoid the locking. You can safely iterate over the dictionary and it will not cause any exceptions if it changes during the enumeration. Wrapping your head around effectively using a ConcurrentDictionary can be a little difficult. – OldFart Mar 13 '15 at 16:27

1 Answers1

3

The line "sut.GetTypes(null)" just returns an IEnumerable that you are throwing away. Since you never iterate over the enumerable, none of the code in GetTypes ever executes.

Try this instead:

foreach (var x in sut.GetTypes(null)) {}
OldFart
  • 2,411
  • 15
  • 20
  • This. People using keywords without knowing the concepts behind it... It is called "deferred execution" – MrDosu Mar 12 '15 at 16:53