-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mocking java method calls #55
Comments
hi jevgeni, does the gen-interface have to be compiled before the midje code tries to run? |
Hi Alex, I believe that gen-interface has to be run before the proxy is generated. If metaconstant is using/representing that proxy, then this could be done at the same time, as fact macro evaluated (somewhere at the same time, when "provided" form is evaluated). |
In the spirit of brainstorming, I wonder if we could somehow leverage an already existing Java mocking framework, such as Mockito, for the internals? |
I was thinking about Mockito as well. However, it looks like there is no easy way to generate interface/class, it is expected to be written first. It might have it, but I never encountered that in my daily job. As to the second part of this problem - mocking the existing method - in my opinion proxy is easier solution (just copy-pasting the forms to the method body) rather than using Mockito's mocks. Another way would be telling which interface/class metaconstant does implements, like this (or something better):
In that case Mockito will be quite useful. |
Any more ideas on this jevgeni? I think it is a really useful and sensible feature to work to include in Midje. It sounds like you have some ideas on how to approach it, and I would really appreciate all the ideas you have to offer! P.S. I wonder about mocking Java static methods? I wonder if Clojure gives us ANY tools to get around the usual impossibility of this... Test I'd liked to have written tonight: (fact "access environment vars only when namespace is loaded"
(do
(red "a")
(yellow "b")
(red "c")
(yellow "d")) => anything
(provided
(System/getenv "MIDJE_COLORIZE") => anything :times 0)) |
Looks like PowerMock enables mocking Java static method calls: |
A couple of proposed syntaxes for creating mocks/stubs (not spies) (via IRC - Alan Malloy) (provided
(mocking bar IBar (.size [this] 10))) (fact
(x) => 1
(provided
(.foo IFoo f) => 2
(.bar IBar b) => 3
(.baz IBaz bz ) => 4)) ;;the case i'm concerned about is:
(provided
(.foo IBar b) => 10
(.bar IBar b) => 9) |
I haven't used PowerMock, but I guess it should be a good choice as well. As to syntax - I've just checked my Java code and I see, that I use mocking on the same objects quite intensibly. Won't it be too much duplication if you stick with " (.method SomeLongClassName metaName) => returnValue " form? I think syntax is the major decision point. Implementation should be quite straighforward as I might imagine (I might be wrong, as I don't know the internals of "fact" macro) - creating an mock instance of the provided class, saving it under ..meta.. symbol, run few powermock invocations such as Mockito.when(mockObj.methodToMock()).thenReturn(123); |
After doing some more digging, I'm wondering if the approach to this could be to expand on the idea of metaconstant fakes (...m...)? I think this is what you were getting at jevgeni. It might look like: (fact
(x ..foo..) => 1
(provided
(.foo IFoo ..foo..) => 2 )) |
vote +1 |
I've thought some more about this. In order to get a sytnax like the below we need to do some tricky stuff.
(fact
(x ..foo..) => 1
(provided
(.foo IFoo ..foo..) => 2 )) I'm just not seeing a clear way to do this, but I really want to, because Clojure's built on the JVM and imo Midje should make working with the JVM easier for our users... Any ideas? |
Perhaps using the Mockito.spy would work here. It enables you to mock on an object (not class or interface). So, for a mocked metaconstant the steps would be to make a version of Metaconstant that extends the appropriate interface/protocol, then make a new one of those SubMetaconstants, then Mockito.spy that object. Meh, we'll see. |
I'm thinking I'd like to close this one. I'm averse to tying Midje too closely to Java libraries. I'm also mindful of the slogan "if it's too hard to test, change your design." That is, problems mocking out java methods is a sign that the Java classes should be hidden behind a thin facade. (This is like the old idea that's been around in many guises, such as "the humble dialog box" in GUI development.) So I'll close it. If anyone wants to reopen, feel free. |
I heard that phrase as well quite often: "if it's too hard to test, change your design." So I adapted my designs accordingly. Until I came to use jMockit (https://code.google.com/p/jmockit). And this makes mocking everything (static, final, private) dead easy. It uses a java agent to "re-implement" whatever class on the fly, while the class is loaded. It allows doing amazing stuff. I have now a Clojure project, in which I want to mock "Thread.sleep". , to make the test run faster. Using current midje, I need to wrap the Thread.sleep in a public function, for being able to mock it. Not a big deal, but I need to adapt my design to make it testable....which is not ideal. |
I don't see this comment on the github site, but I really doubt it will ever happen. Sorry. Antonis Kalou wrote:
|
Yea, sorry for deleting, I didn't notice the date of the posts until after I commented. Thanks for your help. |
I am trying to mock java calls using meta constants like this:
I expect that (.getOutputStream ..sock..) will be replaced with System/
out in the write function call. However, I get "java.lang.Exception:
Unable to resolve var: .getOutputStream in this context (core.clj:
10)"
Considering you know which functions are to be mocked:
it should be possible to perform something like this during macro evaluation:
(assuming that interface name is generated and there is some mechanism
to clean compile-path)
Then associate ..meta.. with this stub and use it like this:
The text was updated successfully, but these errors were encountered: