Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] AspectJ around singleton for testing

> So here's my question.  I'd like to avoid the whole line being called.  Is there a simple way to do an around
> advise that goes around both calls at once?  Ie. Around an entire expression:  OurHttpClient.getInstance().postTo(..)?

Not around both calls at once, no.  I took your code and knocked this up:

public class C {
	public static void main(String[] args) {
		String s = OurHttpClient.getInstance().postTo("abc","def");
	}
}
	
class OurHttpClient {
	        private static OurHttpClient instance;
	        private OurHttpClient() {
	      	  System.out.println("original private ctor running");
	        }
	        public static OurHttpClient getInstance() {
	      	  System.out.println("original getInstance running");
	      	  if (instance == null) { instance = new OurHttpClient(); }
return instance;
	        }
	        public String postTo(String url, String msg) {
	      	  System.out.println("original postTo() running");
	      	  return "foo";/* does post and returns response */
	        }
	}

when run it produces:

original getInstance running
original private ctor running
original postTo() running

Then two options, the one you seem to want to write:

aspect A {
	
	public OurHttpClient.new(String s) {super(); }
	
	OurHttpClient around(): execution(* OurHttpClient.getInstance()) {
		return new OurHttpClient("");
	}
}

and when run, results in:

original postTo() running


Or the alternative with a subtype:

aspect A {
	
        // Could override execution of getInstance() to return MyHttpClient
	OurHttpClient around(): call(OurHttpClient.new(..)) {
		return new MyHttpClient();
	}

	public OurHttpClient.new(String s) { super();}
}

The around advice on the ctor returns the subtype that we want to
create.  Because our subtype needs a reachable ctor in OurHttpClient,
the aspect also adds one - it has to have a parameter to differentiate
from the one that is already there (bit ugly...)

class MyHttpClient extends OurHttpClient {
	MyHttpClient() {
		super(null);
	}
	
	public String postTo(String url, String msg) {
		return "foo";
	}
	
}

Then I can run it again:

original getInstance running

cheers,
Andy



On 15 March 2010 15:40,  <William.J.McDonald@xxxxxxxxxxxxxx> wrote:
> I'm looking at doing some system testing and mocking out (simulating
> responses from) other systems.  The problem is that many of the points of
> control to the system that I want to mock out are at library object calls
> that are implemented as singletons.  For example, I'd like to mock out the
> postTo call, and also avoid the setup code done by the private constructor:
>
>         response = OurHttpClient.getInstance().postTo(url, msg);
>
> Where OurHttpClient code is in a library jar file like this:
>
> public class OurHttpClient {
>         private static OurHttpClient instance;
>         private OurHttpClient() { /* do some setup things that I don't want
> to run during my system testing */ }
>         public static OurHttpClient getInstance() { if (instance == null) {
> instance = new OurHttpClient(); } return instance; }
>         public String postTo(String url, String msg) { /* does post and
> returns response */ }
> }
>
> Again, I want to put in a mock so that when the postTo is called, I can
> return my canned response, simulating the system's indirect input. I also
> don't want some of the things that the private constructor does in the class
> to happen.  (This is a simple example, but I have other singletons that
> access JNDI for datasources and JMS queues and I want to run this outside
> the EJB container too).
>
> I first thought about putting an around advice for the getInstance()
> method.  The problem is that the next thing to execute after that would be
> the postTo(..) method on an OurHttpClient object instance, and because of
> how the singleton is written, I can't return an instance of OurHttpClient
> without going through the setup code inside the constructor.
>
> Initially I opted for a non AOP solution, powerMockito.  I planned on
> replacing the getInstance() call to OurHttpClient with one that returned
> MyHttpClientMock.  But, as I described, the problem was that the next call
> needed an object of type OurHttpClient when it calls the postTo method.  So
> I made MyHttpClientMock a subclass of OurHttpClient thinking that I'd have
> an instance with which to call the postTo method that I could override:
>
> public class MyHttpClientMock extends OurHttpClient {
>         public static MyHttpClientMock getInstance() { return new
> MyHttpClientMock(); }
>         public String postTo(String url, String msg) { /* returns simulated
> response */ }
> }
>
> The problem with this is that eclipse complains about not being able to call
> the OurHttpClient() constructor.  Now what?
>
> Does aspectJ allow me to add another constructor to OurHttpClient, change
> the getInstance to point to it, so I can then mock out postTo() like I
> wanted to in the first place?
>
> public class OurHttpClient {
>         private static OurHttpClient instance;
> /* woven in via aspectj: */
> private OurHttpClient(String dummy) {  }
>
>         private OurHttpClient() { /* do some setup things that I don't want
> to run during my system testing */ }
>         public static OurHttpClient getInstance() {
>                 if (instance == null) {
>                         instance = new OurHttpClient(); // AOP CHANGES THIS
> TO new OurHttpClient("dummy");
>                 }
>                 return instance;
>         }
>         public String postTo(String url, String msg) { /* does post and
> returns response */ }
> }
>
> As it turned out, in some of the cases I'm coding, the class I wanted to
> mock out actually called 'new' on another class, and I was able to join my
> code at that point, but I just got lucky that the 'new' call was done in the
> constructor before any of the setup code I didn't want to run happened.
>
> When I consider using aspectj instead of powermockito to mock out the call,
> I still need to make sure the class's private constructor doesn't really get
> called.  I want to do an around advise of the getInstance, but I think I'm
> going to run into the same problem of requiring the around advise to return
> an object of type OurHttpClient so that the call to .postTo() is correct.  I
> don’t think I can give it just any object that implements postTo instead -
> it has to be one of type OurHttpClient, no?
>
> So here's my question.  I'd like to avoid the whole line being called.  Is
> there a simple way to do an around advise that goes around both calls at
> once?  Ie. Around an entire expression:
> OurHttpClient.getInstance().postTo(..)?
>
> I'm trying to setup regression testing before refactoring, and wanted to be
> able to test without changing the SUT code.
>
> TIA,
>
> Bill McDonald
> William.J.McDonald@xxxxxxxxxxxxxx
> 480-456-8417 office
> CSTS (Consumer Credit Services and Technology Solutions)
>
> This message may contain confidential and/or privileged information. If you
> are not authorized to receive this message, you must not take any action on
> this message. If you have received this message in error, please advise the
> sender immediately by reply e-mail and delete this message. Thank you for
> your cooperation.
>
>
> _______________________________________________
> aspectj-users mailing list
> aspectj-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/aspectj-users
>
>


Back to the top