1

I have an "Test" class that has an IEnumerable of concrete objects

public class Test
{
    public IEnumerable<MyObject> MyObjects { get; }
}

public class MyObject
{
    public string Id { get; }


    public MyObject(
        string id,
    {
        this.Id = id;
    }
}

When debugging my "Steps.cs" class used by my Specflow feature file, I noticed that the values for "Id" were changing. They were as expected in the "Given" step and I added these to the ScenarioContext

[Given("I have use case (.*)")]
{
    var _test = _retrieveTest.GetMyObjects(useCase);
    ScenarioContext.Current.Add("test", _test); 
}

They were changed when I read them out of the ScenarioContext in the "When" Step

[When("I do something")]
public void WhenIDoSomething()
{
    var _test = (MyProject.Entity.Test)ScenarioContext.Current["test"];         
} 

The solution to stop the values changing was to use the LINQ "ToList()" call when retrieving the object as shown below:

private IEnumerable<MyObject> GetMyObjects(
        string usecase,
        MyProject.Entity.Test test)
    {
        ...
        return testData.Objects
            .Select(Object => {
                var _id = var _id = Guid.NewGuid();

                return new MyProject.Entity.MyObject(
                    _id);
            }).ToList();
    }

Can anyone explain why it is necessary to call ".ToList()" here and without it why the value of "Id" changes in the "ScenarioContext " between the "Given" and "When" steps

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
Mike
  • 827
  • 11
  • 27
  • ScenarioContext.Current is a Directory. Are you sure you don't override a key with another value? – Andreas Willich Jan 30 '18 at 13:29
  • Yes, I'm sure that I don't do anything with it especially between the Given and When steps. I do understand what ScenarioContext.Current is and what it is used for. – Mike Jan 30 '18 at 13:39

1 Answers1

3

Without the .ToList() you have return an Enumerator that is executed everytime you iterate over it. With the .ToList() you materialize the enumerator and have a concrete list.

See IEnumerable vs List - What to Use? How do they work? for a more detailed answer.

Andreas Willich
  • 5,665
  • 3
  • 15
  • 22
  • Thanks. That explains what is going on. The Specflow ScenarioContext was a Red herring! On further investigation, the "Id" was changing in the "Given" step every time I opened the Quick Watch window, a different Guid was being generated. Due to it being an IEnumerable the Guid.NewGuid(); code is getting called every time as it is evaluated late as opposed to when I used ".ToList()" which makes it a concrete type in the background and keeps the original Guid value. I am using IEnumerable as I like working with the read-only collection to get the benefits of immutability. – Mike Feb 02 '18 at 08:19
  • 1
    If you want a read only collection type, use the IReadOnlyCollection Interface. https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlycollection-1?view=netframework-4.7.1 I think it is better suited as IEnumerable when you want immutability. – Andreas Willich Feb 02 '18 at 09:08
  • Thanks for the tip. I've edited the original title for the question to remove the reference to Specflow ScenarioContext as this was a Red herring as previously stated. – Mike Feb 02 '18 at 09:17