简体   繁体   中英

Testing a method used from an abstract class

I have to Unit Test a method (runMethod()) that uses a method from an inhereted abstract class to create a boolean. The method in the abstract class uses XmlDocuments and nodes to retrieve information. The code looks somewhat like this (and this is extremely simplified, but it states my problem)

namespace AbstractTestExample
{
public abstract class AbstractExample
{
    public string propertyValues;
    protected XmlNode propertyValuesXML;
    protected string getProperty(string propertyName)
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(new System.IO.StringReader(propertyValues));
        propertyValuesXML= doc.FirstChild;

        XmlNode node = propertyValuesXML.SelectSingleNode(String.Format("property[name='{0}']/value", propertyName));
        return node.InnerText;
    }
}

public class AbstractInheret : AbstractExample
{
    public void runMethod()
    {
        bool addIfContains = (getProperty("AddIfContains") == null || getProperty("AddIfContains") == "True");
        //Do something with boolean
    }
}
}

So, the code wants to get a property from a created XmlDocument and uses it to form the result to a boolean. Now my question is, what is the best solution to make sure I have control over the booleans result behaviour. I'm using Moq for possible mocking.

I know this code example is probably a bit fuzzy, but it's the best I could show. Hope you guys can help.

EDIT: What I basically need is:
I need to be able to control getProperty() while im testing the AbstractInheret class

Since the value of the boolean is controlled by the xml, you could refactor your code, so you can easily set the xml.

Like this:

namespace AbstractTestExample
{
    public abstract class AbstractExample
    {
        protected XmlNode propertyValuesXML;

        protected string getProperty(string propertyName)
        {
            XmlNode node = propertyValuesXML.FirstChild.SelectSingleNode(String.Format("property[name='{0}']/value", propertyName));
            return node.InnerText;
        }
    }

    public class AbstractInheret : AbstractExample
    {
        public AbstractInheret(string propertyValues){

            propertyValuesXML = new XmlDocument();
            propertyValuesXML.Load(new System.IO.StringReader(propertyValues));
        }

        public void runMethod()
        {
            bool addIfContains = (getProperty("AddIfContains") == null || getProperty("AddIfContains") == "True");
            //Do something with boolean
        }
    }
}

This way you don't have to mock anything and you could easily pass a string of xml to your class. You should also check for error condition involving not valid xml passed to the constructor.

If I understand your question correctly, you want to know how to control the result of getProperty so that you can test addIfContains with different boolean values.

If you are having difficulty writing a test for something, it often means you are trying to test too much, or that there is a problem with the design of the code.

You might be able to make this code more testable by avoiding the use of inheritance. As you have discovered, inheritance is often hard to handle in unit tests (there is a lot of tight-coupling going on), and delegation usually works better anyway. I'd recommend making the following change to the design:

namespace TestExample
{
    public interface PropertyManager {
      public string getProperty(string propertyName);
    }

    public class XmlPropertyManager: PropertyManager {
      public string getProperty(string propertyName) {
      //...xml parsing code here
      }
    }

    public class Example {
      private PropertyManager propertyManager;

      public void runMethod() {
        bool addIfContains = (propertyManager.getProperty("AddIfContains") == null || propertyManager.getProperty("AddIfContains") == "True");
        //Do something with boolean
      }
    }
}

Then you can easily mock out PropertyManager.

If your design requires that you use an abstract base class (for other functions other than just getProperty), you can still use the delegate approach:

namespace TestExample
{
    public interface PropertyManager {
      public string getProperty(string propertyName);
    }

    public class XmlPropertyManager: PropertyManager {
      public string getProperty(string propertyName) {
      //...xml parsing code here
      }
    }

    public abstract class AbstractExample
    {
       protected PropertyManager propertyManager;

       //... other important methods
    }

    public class AbstractInheret: AbstractExample {

      public void runMethod() {
        bool addIfContains = (propertyManager.getProperty("AddIfContains") == null || propertyManager.getProperty("AddIfContains") == "True");
        //Do something with boolean
      }
    }
}

I am not very sure what exactly you mean by this question -

"what is the best solution to make sure I have control over the booleans result behaviour."

I am guessing that you need the boolean value of a property. So, this might just do!

    public static bool GetBoolean(string boolStr)
    {
        bool bVal = default(bool);
        try
        {
            bVal = Convert.ToBoolean(boolStr);
        }
        catch (Exception) {
            bVal = default(bool);
        }
        return bVal;
    }

Just plug it in code and it should be good. :)

If for some reason you cannot refactor your code in the way described in my first answer (such as a third party is providing the abstract base class), then you can create a subclass of AbstractInherit that overrides just the getProperty method. Note that I really don't like that type of approach, as it couples your tests to the internal implementation of the class. But sometimes you have no choice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM