/** * Copyright (C) 2014 CUSTIS (http://www.custis.ru/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ru.custis.beanpath; import org.junit.Test; import ru.custis.beanpath.beans.Document; import ru.custis.beanpath.beans.Gender; import ru.custis.beanpath.beans.Identified; import ru.custis.beanpath.beans.NamesBean; import ru.custis.beanpath.beans.Person; import ru.custis.beanpath.beans.PrimitiveBean; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import static ru.custis.beanpath.BeanPathMagic.$; import static ru.custis.beanpath.BeanPathMagic.$$; import static ru.custis.beanpath.BeanPathMagic.root; public class BeanPathMagicTest { /* * Basic usage scenario */ @Test public void basicUsage() { final Person person = root(Person.class); // String property assertEquals(BeanPath.root(Person.class).append("name", String.class), $(person.getName())); // nested property assertEquals(BeanPath.root(Person.class).append("document", Document.class).append("number", String.class), $(person.getDocument().getNumber())); // primitive property assertEquals(BeanPath.root(Person.class).append("age", Integer.class), $(person.getAge())); // enum property assertEquals(BeanPath.root(Person.class).append("gender", Gender.class), $(person.getGender())); // recursive property assertEquals(BeanPath.root(Person.class).append("bestFriend", Person.class).append("name", String.class), $(person.getBestFriend().getName())); // double-dollar shortcut assertEquals("document.issuedBy", $$(person.getDocument().getIssuedBy())); } @Test public void mockCaching() { // The Framework caches mock instances on its type basis for best performance assertSame(root(Person.class), root(Person.class)); assertSame(root(Document.class), root(Document.class)); assertSame(root(new TypeLiteral<Identified<?>>() {}), root(new TypeLiteral<Identified<?>>() {})); } public abstract static class Uninstantaible { private Uninstantaible() { throw new AssertionError(); } } @Test public void allocatingWithOutConstructorInvocation() { // The Framework allocates mock using sun.misc.Unsafe.allocateObject() // without constructor invocation root(Uninstantaible.class); } /* * Naming and type conversion concepts */ @Test public void propertyNamingConcepts() { // Java Bean style properties converts appropriately final NamesBean names = root(NamesBean.class); assertEquals("property", $(names.getProperty()).getName()); // 'get' prefix assertEquals("property", $(names.isProperty()).getName()); // 'is' prefix assertEquals("a", $(names.getA()).getName()); // notice lowercase 'a' assertEquals("UTC", $(names.getUTC()).getName()); // notice 'UTC' in uppercase // otherwise lives unchanged assertEquals("property", $(names.property()).getName()); // no prefix assertEquals("is", $(names.is()).getName()); // 'prefix' only assertEquals("get", $(names.get()).getName()); // 'prefix' only assertEquals("getting", $(names.getting()).getName()); // looks like prefix but it's not assertEquals("isabel", $(names.isabel()).getName()); // looks like prefix but it's not } @Test public void primitiveTypesWrapping() { // Primitive types promotes to their corresponding wrapper types final PrimitiveBean primitives = root(PrimitiveBean.class); assertEquals(Boolean.class, $(primitives.getBoolean()).getType()); assertEquals(Character.class, $(primitives.getChar()).getType()); assertEquals(Byte.class, $(primitives.getByte()).getType()); assertEquals(Short.class, $(primitives.getShort()).getType()); assertEquals(Integer.class, $(primitives.getInt()).getType()); assertEquals(Long.class, $(primitives.getLong()).getType()); assertEquals(Float.class, $(primitives.getFloat()).getType()); assertEquals(Double.class, $(primitives.getDouble()).getType()); primitives.getVoid(); assertEquals(Void.class, $((Void) null).getType()); } /* * Examples with generics */ @Test public void generics_actualTypeParameterResolution() { final Person person = root(Person.class); // The Framework is smart enough to resolve actual type parameter assertEquals(Long.class, $(person.getId()).getType()); } @Test public void generics_wildCardedTypes() { final Person person = root(Person.class); // Wildcard type parameter erases to its upper bound... assertEquals(Number.class, $(person.getNumbers().get(0)).getType()); // ...that may be Object implicitly assertEquals(Object.class, $(person.getStuff().next()).getType()); } @Test public void generics_TypeLiteralUsage() { // One can use TypeToken final Identified<String> identified = root(new TypeLiteral<Identified<String>>() {}); assertEquals(String.class, $(identified.getId()).getType()); } /* * Examples of awkward and very likely meaningless use, but still legal */ @Test public void awkward_propertyWithParameter() { final Person person = root(Person.class); assertEquals(BeanPath.root(Person.class).append("withParam", String.class), $(person.withParam(0))); } @Test public void awkward_voidReturnType() { root(Person.class).sleep(); final BeanPath<Void> path = $((Void) null); assertEquals(BeanPath.root(Person.class).append("sleep", Void.class), path); } /* * Examples of illegal use */ @Test(expected = BeanPathMagicException.class) public void illegal_noCurrentPath() { $(null); } @Test public void illegal_finalClass() { final Person person = root(Person.class); // Attempt to track property of final class fails with NullPointerException try { $(person.getName().getBytes()); // getName() returns String, which is a final class fail(); } catch (NullPointerException ignored) { } // because The Framework cannot mock final classes (including String, all primitive wrappers, enums and arrays) // and intentionally returns null (to point out illegal use with NullPointerException) assertNull(person.getName()); $(null); // clean up } public static class ParanoidPerson { private String getSecret() { return "The Secret"; } public final String getFinalSecret() { return "The Final Secret"; } } @Test public void illegal_privateMethod() { final ParanoidPerson paranoidPerson = root(ParanoidPerson.class); // The Framework cannot intercept private method invocation // (although visible here, but not visible to the Framework) try { $(paranoidPerson.getSecret()); fail(); } catch (BeanPathMagicException ignored) { } // Such invocation just fall through to the real method assertEquals("The Secret", paranoidPerson.getSecret()); } @Test(expected = BeanPathMagicException.class) public void illegal_finalMethod() { ParanoidPerson paranoidPerson = root(ParanoidPerson.class); assertEquals("The Final Secret", paranoidPerson.getFinalSecret()); // The Framework cannot intercept private method invocation // (although visible here, but not visible to the Framework) $(paranoidPerson.getFinalSecret()); } }