0

Hi I have a possible design flaw and i need to solve it with an extension method. Lets say I have a class and it has a property of StringCollection. Example code

public class MyProblematicClass
{
    public IDbAccess Db{get;set;}
    public StringCollection Errors{get;set;}

    public MyProblematicClass(IDbAcces db){ Db=db;}

    public int SetItem(Item i)
    {

        var id = Db.Save(i);
        this.Errors = Db.Erros;
        return id;
    }

}

What I am doing is, in my unit test class I mock IDbAccess. This class validates object according to attributes. If any error occures it doesnt hit to db, it just fills its own Errors collection. For unit test I use another dbclass which just runs validation routines and here is problem i cannot get Error. Let me give you example for further understanding ( I know design is problematic, but for now I want to deal with it without changing anything)

public static class MyDbExtension
{
   public static Save(Item i)
   {
     Validation v = new Validation();
     var erros =    v.ValidateObject(i);
     //Here is problem i cannot pass it to MyProblematicClass
      if ( errors.Count > 0 )
         return -1;
     else 
        return 1;


    /* what I want to is :
     var stackTrace = new StackTrace(); get stack trace
     var object = stackTrace.GetFrame(1).GetMethod().GetObject() or sth like that. get object
     object.GetProperties()[0].SetValue(object,errors,null);  find property and set it.
   */
   }
}

in my unit test :

public class UnitTest
{
   Mock<IDbAccess> _db ;
   MyProblematicClass _mpc;
   pubic Setup()
   {

       _db.Setup(x=>x.Save(It.IsAny<Item>).Returns(u =>MyDbExtension.Save(u));
      _mpc = new MyProblematicClass(_db.Object);

   }

 public void SetItem_EmptyObject_Contains3Erros()
 {
    Item i = new Item();
    _mpc.SetItem(i);
    //At this point i cannot set _mpc.Errors
 }

What I want to achieve is in my DbExtension class can I access caller class and set its Errors property? I tried but it wasn unlikely yet. If anyone has any decent solution I will be appreciative and of course you can comment on design problems.

Edit

I appreciate Alex's answer he just said ignore Save method just mock Erros property and it will be ok. That make sense but what I wonder is in question, is it possible to access Stack Trace and manipulate caller methods object's property?

Thanks in advance.

Sam Holder
  • 32,535
  • 13
  • 101
  • 181
adt
  • 4,320
  • 5
  • 35
  • 54

4 Answers4

1

You need to setup the return value of _db.Errors, something like this:

public class UnitTest
{   
     Mock<IDbAccess> _db ;   
     MyProblematicClass _mpc;   
     StringCollection errors;

     pubic Setup()   
     {       
          _db.Setup(x=>x.Save(It.IsAny<Item>).Returns(u =>MyDbExtension.Save(u));
          _db.Setup(x=>x.Errors).Returns(errors);      
          _mpc = new MyProblematicClass(_db.Object);   
     } 

     public void SetItem_EmptyObject_ContainsError() 
     {    
          errors.Add("Expected Error!");

          Item i = new Item();    
          _mpc.SetItem(i);    

          Assert.AreEqual("Expected Error!", _mpc.Errors[0]);
     }
 }

I must admit I don't really follow your design, why are you using a static method for save? You could just as easily have the line:

_db.Setup(x=>x.Save(It.IsAny<Item>).Returns(-1);

Then test IDbAccess.Save() independently.

In your 'extension' class the save method has no return value, and MyProblematicClass does not inspect the return value before assigning errors.

Alex Peck
  • 4,603
  • 1
  • 33
  • 37
  • the reason it is static this Save is used anywhere in project, like IRepository's save method. so I dont want everybody implement save method everytime. its pretty straight forward it validates and saves to db. I know i can set Erros with mock but i want it to be validated and should be filled with collection. And i wonder if i can use stack trace out of this question's context. – adt Jul 04 '11 at 18:06
  • Alex thank you i edited my question, Thanks for warning why i didnt think to mock Erross :) Anyway i will up answer but its not exactly what i am asking. – adt Jul 04 '11 at 18:18
1

Not sure to fully understand the question, but you cannot access the parameters on the stack from a normal program. Runtime metadata is only about static information (method, properties, constants, etc...).

I believe only a debugger (which is considered as a special beast of its own) can do this without changing the program/source, and this has serious performance cost. As a side note, here is a link that explain how to build your own managed debugger (.NET 4): CLR Managed Debugger (mdbg) Sample 4.0

Another solution is to instrument your code (automatically or using a tool) to add some tracing call that can capture the list of parameters on each traced methods. Tools like PostSharp can do this. Here is another link: Non-Invasive Tracing & Logging

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
0

You could use unmanaged debugging API to access the call stack and get the object previous function on the stack was called on.

The problem is, the stack may not contain the method you are expecting. In cases such as inlining and tail call optimization, the call stack doesn't contain the previous method called, which means you can't reliably do what you want.

For more information see this answer by Eric Lippert.

Community
  • 1
  • 1
svick
  • 236,525
  • 50
  • 385
  • 514
0

This doesn't use the call stack, but might get you some mileage:

class CalledClass
{
    public static void PokeCaller()
    {
        Program._this.Error = "Error!!!";
    }
}

class Program
{
    public string Error = null;
    [ThreadStatic] public static Program _this;
    public void Run()
    {
        _this = this;
        CalledClass.PokeCaller();
        Console.WriteLine(Error);
        Console.ReadKey();
    }

    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
    }
}

Making Errors be [ThreadStatic] might be a more direct way to do it... or some other variation on that theme. You might also combine it with stack trace checking to see if you were actually called by something that has "Errors" attribute before setting it...

  • I didnt get the answer. What i am trying to accomplish is i dont have my callers instance i just know its an interface and i want to access its object through StackTrace. – adt Jul 13 '11 at 19:03
  • If you use a thread local static variable, you just access it statically and if you're on the same thread, you'll be accessing the variable for that thread. (you don't need a reference since it's static) Note: You might have to keep a static thread local in an abstract base class instead of an interface to do this easily for different sub-classes. –  Jul 13 '11 at 19:09