locked
Creating Parameterized Moles returns null Mole only RRS feed

  • Question

  • I was working on a demo on using Pex with SharePoint and stumbled upon the following challenge.

    Imagine the following code:

    public static SPListItem GetNewsMessage(string title)
    {
        SPListItem newsMessage = null;
    
        if (String.IsNullOrEmpty(title))
        {
            throw new ArgumentException("title");
        }
    
        SPList newsList = SPContext.Current.Web.Lists["Pages"];
        if (newsList != null)
        {
            SPListItemCollection newsItems = newsList.GetItems(new SPQuery()
            {
                Query = String.Format("<Where><And><Eq><FieldRef Name='ContentType'/><Value Type='Text'>News</Value></Eq><Eq><FieldRef Name='Title'/><Value Type='Text'>{0}</Value></Eq></And></Where><OrderBy><FieldRef Name='Created' Ascending='True'/></OrderBy>", title),
                RowLimit = 1
            });
    
            if (newsItems != null && newsItems.Count > 0)
            {
                newsMessage = newsItems[0];
            }
        }
    
        return newsMessage;
    }
    

    It's a method that returns a list item with the given title.

    The problem is that Pex doesn't generate any test to cover the case when an item is found (ie. newsItems != null && newsItems.Count > 0). The mock of the GetItems(SPQuery) method always results in a null value. While I could create two PexMethods to cover this I'd rather use a single method with parameterized mole.

    I've looked at the documentation provided with Pex and came up with the following PexMethod:

    [PexMethod]
    public SPListItem GetNewsMessage(string title)
    {
        MSPContext.FallbackAsNotImplemented();
    
        MSPContext.CurrentGet = () => new MSPContext()
        {
            WebGet = () => new MSPWeb()
            {
                ListsGet = () => new MSPListCollection()
                {
                    ItemGetString = listName =>
                    {
                        PexAssert.IsTrue(listName == "Pages");
                        return new MSPList()
                        {
                            GetItemsSPQuery = query =>
                            {
                                PexAssert.IsNotNull(query);
                                var call = PexChoose.FromCall(this);
                                return call.ChooseResult<MSPListItemCollection>();
                            }
                        };
                    }
                }
            }
        };
    
        SPListItem result = RecentNewsWebPart.GetNewsMessage(title);
        return result;
        // TODO: add assertions to method RecentNewsWebPartTest.GetNewsMessage(String)
    }
    

    Unfortunately, as I mentioned before, the GetItemsSPQuery method always returns a null value.

    Am I missing something here, or is this something SharePoint-specific that isn't supported by Pex yet?


    w: http://blog.mastykarz.nl | t: @waldekm | c: http://imtech.codeplex.com | c: http://www.imtechvelocity.nl
    Monday, November 23, 2009 3:29 PM

Answers

All replies

  • The problem lies in this line:

                                return call.ChooseResult<MSPListItemCollection>();

    Pex does not know how to create an instance of MSPListItemCollection so it only generates the 'null' case. The proper to write the sample would be to (1) choose a SPListItemCollection instead of the mole type:

                                return call.ChooseResult<SPListItemCollection>();

    (2) create a parameterized factory method that creates a new SPListItemCollection. In that factory, you can use the moles to prepare that instance: For example,

        public static class SPListItemFactory {
            [PexFactoryMethod]
            public static SPListItemCollection Create(SPListItem[] items) {
                var mole = new MSPListItemCollection();
                if(items != null)
                    mole.Bind(items);
                return mole;
            }
        }

    Of course, you will need to add a factory method for SPListItem as well.

    Jonathan "Peli" de Halleux - Give us your input about Pex!
    Monday, November 23, 2009 4:25 PM
  • I've just added both steps. In the GetNewsMessage(string) method I've changed the last check to:

    if (newsItems != null && newsItems.Count > 0 && newsItems[0] != null)
    {
        newsMessage = newsItems[0];
    }
    Pex generated now 7 tests. The problem is that none of them results in a non-null value.
    w: http://blog.mastykarz.nl | t: @waldekm | c: http://imtech.codeplex.com | c: http://www.imtechvelocity.nl
    Monday, November 23, 2009 5:25 PM
  • Same problem. Pex does not know how to create SPListItem instance. You need to create a factory for that type as well.

    Jonathan "Peli" de Halleux - Give us your input about Pex!
    Monday, November 23, 2009 9:07 PM
  • I thought I did. I've included the following code snippet:

    [PexFactoryMethod]
    public static SPListItem CreateListItem(SPListItem item)
    {
        var mole = new MSPListItem();
        if (item != null)
        {
            mole.Bind(item);
        }
    
        return mole;
    }

    w: http://blog.mastykarz.nl | t: @waldekm | c: http://imtech.codeplex.com | c: http://www.imtechvelocity.nl
    Monday, November 23, 2009 9:36 PM
  • Bind will only bind the method from IEnumerable so you also have to hook individual methods as well. Anyway, I don't think you want to take a SPLIstItem as a parameter, rather you should query for string, value, or fields that consitute the state of that item.

    Btw, we are starting to work on the modeling part of the tutorial which should cover this questions.


    Jonathan "Peli" de Halleux - Give us your input about Pex!
    Monday, November 23, 2009 11:16 PM
  • Thanks, Peli. Looking forward to the examples. Would taking the SPListItem as a parameter explain why Pex keeps returning a null value?
    You mention, that Bind works only on IEnumerable. In the above example, the intellisense says different. The Bind method for MSPListItem accepts a parameter of SecurableObject type (base class for SPListItem).


    w: http://blog.mastykarz.nl | t: @waldekm | c: http://imtech.codeplex.com | c: http://www.imtechvelocity.nl
    Tuesday, November 24, 2009 6:17 AM
  • > Would taking the SPListItem as a parameter explain why Pex keeps returning a null value?

    Absolutely. Try removing that argument and passing simple values for the ID, etc... instead. You can construct the mole in the factory.

    Note that Bind will only bind the method of SPListItem that implement ISecurableObject, in that sense a small subset of the SPListItem API.


    Jonathan "Peli" de Halleux - Give us your input about Pex!
    Tuesday, November 24, 2009 9:23 AM
  • If you plan to present stubs and moles,  you can find a slidedeck about stubs and moles at http://research.microsoft.com/en-us/projects/stubs/. There are also many other slide decks for Pex on our documentation page http://research.microsoft.com/en-us/projects/pex/ .

    Jonathan "Peli" de Halleux - Give us your input about Pex!
    Tuesday, November 24, 2009 5:31 PM