Is it possible to create a mock object with constructor arguments. For e.g
Say I have an object and uses two kinds of constructors. How ?
Class test{
List<String> list
public test()
{
list = new ArrayList<String>()
}
public test(List<String> list)
{
this.list = list
}
}
Question 2:
Can I use expect on a real object if one of its methods returns a mock object
For e.g PreferenceService prefServ = easyMock.create(...) Now prefServ is a mock object which is returned by one of the methods in class 'Test' E.g. PreferenceService getPreferenceService(). If I create a real object of type Test can i use expect(test.getPreferenceService()).andReturn(mockPreferenceService) ??? I get an error that says incompatible return type.
I think what you want is partial mocking. You could do:
PreferenceService prefServ = createMock(PreferenceService.class);
Test defaultTest = createMockBuilder(Test.class).addMockMethod("getPreferenceService").
createMock();
expect(defaultTest.getPreferenceService()).andReturn(prefServ);
Now you have defaultTest, instantiated with the default constructor, which is a real instance of Test except that the method getPreferenceService() is mocked.
List<String> testList = new ArrayList<String>();
Test otherConstructorTest = createMockBuilder(Test.class).
addMockMethod("getPreferenceService").withConstructor(testList);
expect(defaultTest.getPreferenceService()).andReturn(prefServ);
Now you have the same as above, but this time the Test object was constructed with the List constructor.
Related
I am not able to understand why below two tests are not giving the same result.
#Service
public class SomeManager{
private final SomeDependency someDependency;
#Autowired
public SomeManager(SomeDependency someDependency){
this.someDependency = someDependency;
}
public List<returnType> methodToTest(Arg arg){
List<JsonObject> jo = someDependency.search(arg);
return jo.stream().map(returnType::parse).collect(Collectors.toList());
}
}
Test with any(). This Test pass.
#RunWith(SpringJUnit4ClassRunner.class)
public class TestMethodToTest(){
#Test
public void TestMethod(){
SomeDependency someDependency = mock(SomeDependency.class);
List<JsonObject> expected := \some valid list of JsonObject\
// Here I used any() method.
when(someDependency.search(any())).thenReturn(expected);
SomeManager someManager = new SomeManager(someDependency);
List<returnType> actual = someManager.methodToTest(any(Arg.class));
assertArrayEquals(acutal.toArray(), expected.stream().map(returnType::parse).toArray());
}
}
But since search(Arg arg) method of SomeDependency takes parameter of class Arg so I changed above test like this:
#RunWith(SpringJUnit4ClassRunner.class)
public class TestMethodToTest(){
#Test
public void TestMethod(){
SomeDependency someDependency = mock(SomeDependency.class);
List<JsonObject> expected := \some valid list of JsonObject\
// Here I used any(Arg.class) method.
when(someDependency.search(any(Arg.class))).thenReturn(expected);
SomeManager someManager = new SomeManager(someDependency);
List<returnType> actual = someManager.methodToTest(any(Arg.class));
assertArrayEquals(acutal.toArray(), expected.stream().map(returnType::parse).toArray());
}
}
This second test fails with output java.lang.AssertionError: array lengths differed, expected.length=1 actual.length=0.What's the possible reason behind this?
Note: The value expected.length=1 in output depends on what value is provided by the user as valid list of json objects in the test.
The difference stems from the fact that any matches null, while anyClass does not match null. See ArgumentMatchers javadoc:
any() Matches anything, including nulls and varargs.
any(Class<T> type) Matches any object of given type, excluding nulls.
You are passing null to your method under test here:
List<returnType> actual = someManager.methodToTest(any(Arg.class));
any() returns null which you pass to method under test.
Note that using argument matchers this way is illegal - you should only call them inside calls to when and verify. You should pass a real instance of Arg to method under test.
See Mockito javadoc
Matcher methods like any(), eq() do not return matchers. Internally, they record a matcher on a stack and return a dummy value (usually null). This implementation is due to static type safety imposed by the java compiler. The consequence is that you cannot use any(), eq() methods outside of verified/stubbed method.
Tl;dr: I want to get test MyCmdTest."data bind works" in this code green.
Thanks to Jeff Scott Brown for getting me that far.
I have a POGO with some custom conversions from JSON which I expect to receive in a Grails controller:
def myAction(MyCmd myData) {
...
}
With:
#Validateable
class MyCmd {
SomeType some
void setSome(Object value) {
this.some = customMap(value)
}
}
Note how customMap creates an instance of SomeType from a JSON value (say, a String). Let's assume the default setter won't work; for instance, an pattern we have around more than once is an enum like this:
enum SomeType {
Foo(17, "foos"),
Bar(19, "barista")
int id
String jsonName
SomeType(id, jsonName) {
this.id = id
this.jsonName = jsonName
}
}
Here, customMap would take an integer or string, and return the matching case (or null, if none fits).
Now, I have a unit test of the following form:
class RegistrationCmdTest extends Specification {
String validData // hard-coded, conforms to JSON schema
void test() {
MyCmd cmd = new MyCmd(JSON.parse(validData))
// check members: success
MyCmd cmd2 = JSON.parse(validData) as MyCmd
// check members: success
}
}
Apparently, setSome is called in both variants.
I also have a controller unit test that sets the request JSON to the same string:
void "register successfully"() {
given:
ResonseCmd = someMock()
when:
controller.request.method = 'POST'
controller.request.contentType = "application/json"
controller.request.json = validData
controller.myAction()
then:
noExceptionThrown()
// successful validations: service called, etc.
}
Basically the same thing also runs as integration test.
However, the mapping fails when running the full application; some == null.
Which methods do I have to implement or override so Grails calls my conversions (here, customMap) instead of inserting null where it doesn't know what to do?
It's possible to customize data binding using the #BindUsing annotation:
#BindUsing({ newCmd, jsonMap ->
customMap(jsonMap['someType'])
})
SomeType someType
See also the MWE repo.
Sources: Hubert Klein Ikkink # DZone, Official Docs (there are other ways to customize)
By browsing around I did successfully manage to create a class that can "opt in" dat:convert by exposing a Map toJson() method and can be json-ified with JSON.encode(myClass), more or less like the following:
//My dummy class
class MyClass{
String p1;
String p2;
Map toJson{
return {
'p1':this.p1,
'p2':this.p2
}
}
}
//so I can do
String jsonString = JSON.encode(myClass)
However I'd like to do this even the other way around, like:
String jsonString = '{"p1":"value","p2":"value"}'
MyClass instance = JSON.decode(jsonString)
But so far I've failed to find a way.
I know I can build a constructor for my class that initialises it from a map, something like:
String jsonString = '{"p1":"value","p2":"value"}'
MyClass instance = MyClass.fromMap(JSON.decode(jsonString))
However I was looking for a more "symmetric" way using just JSON.encode() and JSON.decode(), is it somehow doable? Am I missing something?
There is no standard way to encode the class in JSON. {"p1":"value","p2":"value"} doesn't contain any information about what class to instantiate. There is also no standard way to create a new class from as string (what library should be used when several contain a class with the same name, ...
As far as I know a reviver can be used for that purpose
reviver(var key, var value) {
// decode manually
}
final jsonDecoder = new JsonDecoder(reviver);
but the reviver would need to have some hardcoded logic how to recognize what JSON should result in what Dart class and how it should instantiate it and initialize the properties from the JSON.
I'm trying to persist Maps of properties as single JSON-encoded columns, as shown in this question.
The problem I'm having is that apparently transient properties cannot be set in the default map constructor. Given any transient field:
class Test {
//...
String foo
static transients = ['foo']
}
It seems that the map constructor (which Grails overrides in various ways) simply discards transient fields:
groovy:000> t = new Test(foo:'bar')
===> Test : (unsaved)
groovy:000> t.foo
===> null
While direct assignment (through the setter method) works as expected:
groovy:000> c.foo = 'bar'
===> bar
groovy:000> c.foo
===> bar
Is there a way to make the map constructor accept transient fields?
Or rather: is there a better way to persist a Map as a single JSON-encoded DB field, rather than the method shown in the linked question?
Here's the complete example:
import grails.converters.JSON
class JsonMap {
Map data
String dataAsJSON
static transients = ['data']
def afterLoad() { data = JSON.parse(dataAsJSON) }
def beforeValidate() { dataAsJSON = data as JSON }
}
I can set data using the setter (which will then be converted into dataAsJSON) but not using the map constructor.
The map constructor in GORM uses the data binding mechanism, and transient properties are not data-bindable by default. But you can override this using the bindable constraint
class Test {
//...
String foo
static transients = ['foo']
static constraints = {
foo bindable:true
}
}
I've also replied to your original question, that you don't need json conversion to achieve what you need. However, If you need json conversion badly, why don't you implement it in your getters/setters?
class Test {
String propsAsJson
static transients = ['props']
public Map getProps() {
return JSON.parse(propsAsJson)
}
public void setProps(Map props) {
propsAsJson = props as JSON
}
}
//So you can do
Test t = new Test(props : ["foo" : "bar"])
t.save()
In this way you encapsulate the conversion stuff, and in DB you have your properties as Json.
You can simplify your case by adding the JSON-conversion methods to your domain class, they should have nothing to do with GORMing:
class Test {
String title
void titleFromJSON( json ){
title = json.toStringOfSomeKind()
}
def titleAsJSON(){
new JSON( title )
}
}
In the Play framework i have a few models that have fields which are object references to other models. When i use renderJSON, i don't want those object references to be included. Currently for my needs i create a separate view model class which contains the fields i want, and in the controller i create instances of this view class as needed. Ideally i would like to be able to use the model class itself without having to write the view class.
Is there a way to annotate a field so that it will not be serialized when using renderJSON?
because play uses Gson for its Json serialization you can try the following:
public static void test()
{
Object foo = new SomeObject("testData");
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
.create();
renderJSON(gson.toJson(foo));
}
now each field marked as transient will not be serialized. There is also another (better) way. You can use the com.google.gson.annotations.Expose annotation to mark each field you want to serialize.
public static void test()
{
Object foo = new SomeObject("testData");
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
renderJSON(gson.toJson(foo));
}
Using FlexJSON with play is another option, explained in this article: http://www.lunatech-research.com/archives/2011/04/20/play-framework-better-json-serialization-flexjson
Not sure why no one has written the most direct solution to this answer so I will do it here:
Simply mark the fields you do not want serialized via Gson as transient.
Here's an example:
public class Animal
{
private String name = "dog";
transient private int port = 80;
private String species = "canine";
transient private String password = "NoOneShouldSeeThis";
}
None of the items which are marked transient will be serialized.
When deserialized they will be set to their default (class default) values.
Resulting JSON will look like the following:
{"name":"dog","species":"canine"}
For more information on transient you can see the SO
Why does Java have transient fields?
I would override renderJSON to check a the field name against a member array of serialization exclusions.