Re: Addin Error Reporting Issue

"Kelly Anderson" <[email protected]>
Newsgroups gmane.comp.windows.dotnet.nunit.devel
Message-ID <[email protected]>
On Thu, Feb 14, 2008 at 12:49 PM, Charlie Poole
<[email protected]> wrote:
> Hi Kelly,
>
> > > FWIW, you could do the same thing like this, without the
> > extension...
> > >
> > >     [Test]
> > >     public void AllObjectsMeetCondition()
> > >     {
> > >        ArrayList rv = new ArrayList();
> > >        DirectoryInfo di = new DirectoryInfo(@"..\..\DataFiles");
> > >        FileInfo[] fi = di.GetFiles("*.*");
> > >        foreach (FileInfo info in fi)
> > >        {
> > >          MyObject s = new MyObject(info.FullName);
> > >          rv.Add(s);
> > >        }
> > >
> > >        Assert.That( rv, Has.All.Property( "Condition", true ) );
> > >     }
> >
> > Yes, you can do this. The question is how does it report a failure.
> > When Has.All.Property fails, does it output separate messages
> > for each Property in the collection that doesn't meet the
> > condition? I really need it to tell me which pieces of data
> > fail in one pass, not a simple all passed or something
> > failed. That's why I did the addin. Granted, I didn't look
> > into Has.All.Property at the time so it could do reporting
> > the way I want, but I didn't know about it.
>
> I shouldn't tell you - so that you're forced to try it out. :-)
>
> But I will. It reports what data didn't fail, but it doesn't
> go past the first failure.

Thanks for spilling the beans. :-)

For programming errors (and NUnit is mostly about those) stopping
after the first failure seems right. For data tests, most often it is
not the right thing.

> However, I think you have missed
> my point, which was stated below...

No, I got it. :-)

> > > My point is not that the addin is useless - this is a simple,
> > > contrived example after all - but that you may need to do a
> > bit more
> > > than what NUnit already does here. The substitute code above has
> > > exactly the same problem as your code... the test will fail without
> > > any indication of which data element caused the failure and none of
> > > the other tests will be run.
> >
> > This is only a problem in my code if it fails in building the
> > list. It does do proper reporting if one of the files doesn't
> > meet the condition inside the [IterativeTest]...
>
> Exactly my point. There are several things that need to be
> added to NUnit to make this work write:
>
> 1) Data arguments to tests
> 2) Running all the data points, even if some fail
> 3) Properly handling initialization errors.
>
> You implemented two out of three and were expecting the NUnit
> core to take care of the third. ;-)

I wasn't so much expecting it to do so as I was curious about what the
right way to do it was. If I can handle it within the Addin, I will do
so. I'm just not sure how in the context of building the lists of
tests that I can build a test failed result without running the test.
I suppose that I could just create a fake test that would fail with a
somewhat meaningful message and put it into the test list... would
that be a proper approach without modifying the core?

> > Sure, that's fine. The question though is how can I have it
> > show up as merely a failed test rather than a complete
> > failure to load the entire set of tests? I'm willing to do
> > some work to make this behave better, but the question is
> > whether it can be done without messing with the core of
> > NUnit, or would I have to go into the Core?
>
> I thought I said, but maybe I should go to your code to
> give it in more detail.
>
> In summary... When you are building test cases, you have to
> handle exceptions. When the attempt to create a test case
> fails because of bad data, you have to do something so that
> it will show up as a failed test case.

Right. How would you do that?

> Exactly what you do depends on what you want to happen, but
> one way would be to mark the test case as non-runnable, and
> fill in an appropriate reason. Another approach would be to
> override Run so that the test case fails when the data
> isn't correct.
>
> If you like, we can go to the code to be more specific.

Marking as non-runnable seems like it might be right. What I envision
is something like this... When the test works, it creates a node in
the tree with children for each data specific sub-test-instance...
When the building of the IEnumerable fails, I would like to create
just that root node, marked red, with some sort of message about the
list building routine throwing an exception or something along those
lines. No children in that case.

The most important thing being that the other tests are run normally,
and display the right results without interference from the fact that
my list building activity faild.

So, going to the code, as you suggested... the key function seems to
be this one:

    public Test BuildFrom(MethodInfo method)
    {
      Attribute attrib = Reflect.GetAttribute(method,
IterativeTestAttribute, false);
      string functionName =
IterativeTestFramework.GetSourceEnumeratorName(attrib);
      TestSuite suite = new TestSuite(method.Name);

      object tester = Reflect.Construct(method.DeclaringType);
      MethodInfo m = tester.GetType().GetMethod(functionName);
      IEnumerable returnValue = (IEnumerable) m.Invoke(tester, null);
      if (returnValue != null)
      {
        foreach (object current in returnValue)
        {
          suite.Add(new IterativeTestCase(method, current));
        }
      }

      return suite;
    }

So, if I try to handle the situation myself... My best guess is
something along these lines:

    public Test BuildFrom(MethodInfo method)
    {
      Attribute attrib = Reflect.GetAttribute(method,
IterativeTestAttribute, false);
      string functionName =
IterativeTestFramework.GetSourceEnumeratorName(attrib);
      TestSuite suite = new TestSuite(method.Name);

      object tester = Reflect.Construct(method.DeclaringType);
      MethodInfo m = tester.GetType().GetMethod(functionName);
      IEnumerable returnValue = null;
      try
      {
        returnValue = (IEnumerable) m.Invoke(tester, null);
        if (returnValue != null)
        {
          foreach (object current in returnValue)
          {
            suite.Add(new IterativeTestCase(method, current));
          }
        }
      }
      catch (Exception e)
      {
        suite.RunState = RunState.NotRunnable;
        suite.IgnoreReason =
          string.Format("{0} failed to return a valid
IEnumerable.\n{1}", functionName, e.Message);
      }

      return suite;
    }

But since RunState and IgnoreReason have no intellisense... I'm not
sure this is exactly the right approach, but I suspect it's something
close to this that I want to do.

-Kelly

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.