The SCORE rule is used to summarize a unit test process that is lazy.

Source: Internet
Author: User

The SCORE rule is used to summarize a unit test process that is lazy.



Recently, I encountered a unit test problem. This week I learned a SCORE rule. Here I am practicing this rule to share the cause and effect of the problem with you.

S: Background
Code requires unit tests. The standard of detection is to calculate the unit test coverage rate of the Code. programmers must meet the specified minimum coverage rate.


C: conflict, or ask a question.

Due to the special relationship between the project structure and the code scanning tool, more unit tests need to be written, because the Code Description of the development management department is configured to scan by JAVA project, multiple projects cannot be scanned as a whole.

One of my projects sets up interfaces and object objects separately as a JAVA project. The entire project is divided into two JAVA projects:

  • Interface and entity. The project name is core.
  • The implementation of business logic and data persistence. The project name is service and relies on the above core

Generally, because core only contains interfaces and entities, I am not aware of writing unit tests. Because unit tests are of little value, they are nothing more than testing whether entities can be serialized, if JSR303 is implemented, the logic of these verifications may also be of some test value. Because our service depends on core, we have actually called interfaces and entities when writing unit tests for the service. Theoretically, we do not need to write unit tests for the core. However, in the era of core issues, the code scanning tool is currently not so intelligent by the development and management departments. It uses a single JAVA project to measure unit test coverage, for our structure, if we only write unit tests in the service, then the effective code overwrite line will only count in the service Project, and the code in the called core project is not included in it. However, these core interfaces and the code lines occupied by entities still have a certain amount of weight. If these statistics are not included, it would be hard to reach a high coverage rate, unless you have a lot of time to write.

O: The selected Solution
Object objects are nothing more than get and set cost methods. to test them, we can use the serialization mechanism to serialize objects into strings to complete get calls, in turn, serialization of strings into objects will complete the set call. How can this problem be achieved?

  • Write unit tests, instantiate objects, and complete serialization and deserialization for each object.

Advantage: You can precisely control the value of each attribute.
Disadvantages: Many unit tests need to be written, which is costly in time. The new entity classes mean that new unit tests need to be written, and deletion or modification will also affect.

  • The reflection mechanism is used to dynamically calculate entities in the project to automatically complete serialization and deserialization.

Advantage: easy to use. You only need a small amount of code to perform unit tests on all entity classes, and do not write unit tests because of the increase in Entity volume.
Disadvantage: the assignment of specific attributes in an object cannot be precisely controlled. However, if there are special cases, you can write unit tests separately to supplement them.

  • Optimize code scanning tools

Theoretically, it is feasible, but difficult and inflexible. The tool is dead and will only be executed according to the rules written in advance, for example, the current situation is that it is only responsible for scanning by a single JAVA project.

R: Result
As you can see from the note title, I must have chosen solution 2, the unit test cases are not changed with the increase or decrease of the entity class for such entity class tests. The time saved is too attractive.

E: RatingThis is my personal use, so it is my personal summary.
When you need to meet the company's code rules, you need to pay attention to your own implementation methods to improve efficiency as much as possible, and lazy work will be more relaxed.



Implementation process: enter a package namespace containing entity classes. The system loads all classes under the package. If it is an enumeration, the system calls the enumeration method, if non-enumeration is used to generate default instance objects and complete serialization and deserialization.

  • Load classes by specified package, pass the package namespace, and return all classes under this package. This code is used for reference on the Internet. It is said that this is part of spring source code and I have not verified it yet.
public static Set<Class<?>> getClasses(String pack) {        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();        boolean recursive = true;        String packageName = pack;        String packageDirName = packageName.replace('.', '/');        Enumeration<URL> dirs;        try {            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);            while (dirs.hasMoreElements()) {                URL url = dirs.nextElement();                String protocol = url.getProtocol();                if ("file".equals(protocol)) {                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");                    findAndAddClassesInPackageByFile(packageName, filePath,                            recursive, classes);                }            }        } catch (IOException e) {            e.printStackTrace();        }        return classes;    }    public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {        File dir = new File(packagePath);        if (!dir.exists() || !dir.isDirectory()) {            return;        }        File[] dirfiles = dir.listFiles(new FileFilter() {            public boolean accept(File file) {                return (recursive && file.isDirectory())                        || (file.getName().endsWith(".class"));            }        });        for (File file : dirfiles) {            if (file.isDirectory()) {                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);            } else {                String className = file.getName().substring(0,file.getName().length() - 6);                try {                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));                } catch (ClassNotFoundException e) {                    e.printStackTrace();                }            }        }    }
  • The class loaded cyclically for processing. Because entity objects contain enumeration, enumeration needs to be treated differently because we have fixed rules. Let's take a look at the enumeration definition:

Contains two example methods without parameters and two static methods with parameters, and inherits an interface IEnumCodeName

public enum AppOwnerType implements IEnumCodeName {    Enterprise(1, "Enterprise"),    User(2, "User");    private String name;    private int code;    private AppOwnerType(int code, String name) {        this.name = name;        this.code = code;    }    public static AppOwnerType getByCode(int code) {        return EnumHelper.getByCode(AppOwnerType.class, code);    }    public static AppOwnerType getByName(String name) {        return EnumHelper.getByName(AppOwnerType.class, name);    }    public String getName() {        return name;    }    @Override    public int getCode() {        return code;    }    public static void main(String a[]){        System.out.println(AppOwnerType.Enterprise.getName());    }        }


Determine whether the current class is the enumeration we defined above, and determine whether to implement the IEnumCodeName interface. It can be seen how important it is to define an interface for enumeration in the project.

private boolean isEnumCodeNameByObj(Class<?> classObj){        Class<?>[] interfaces=classObj.getInterfaces();        if(null==interfaces||interfaces.length==0){            return false;        }        List<Class<?>> interfaceList=Lists.newArrayList(interfaces);        Object enumCodeNameObj=Iterables.find(interfaceList, new Predicate<Class<?>>() {            @Override            public boolean apply(Class<?> input) {                return input.getName().indexOf("IEnumCodeName")!=-1;            }        },null);        return null!=enumCodeNameObj;    }
    • If the class is enumeration, test the enumeration method.
private void testEnum(Class<?> classObj) throws Exception {        EnumHelper.IEnumCodeName enumCodeName=ClassloadHelper.getFirstEnumByClass(classObj);        Method[] methods= classObj.getMethods();        if(null!=enumCodeName) {            Method methodCode = classObj.getMethod("getByCode",new Class[]{int.class});            methodCode.invoke(null,enumCodeName.getCode());            Method methodName = classObj.getMethod("getByName",new Class[]{String.class});            methodName.invoke(null,enumCodeName.getName());        }    }
    • If the class is not enumeration, a default instance is generated and then serialization and deserialization are called. (JsonHelper is an encapsulated jackson, so we will not post it here)
private void testObj(Class<?> classObj) throws Exception {        Object obj = classObj.newInstance();        String jsonString = JsonHelper.toJsonString(obj);        Object objNew = JsonHelper.json2Object(jsonString,classObj);        Assert.isTrue(null!=objNew);        Assert.isTrue(!StringUtils.isBlank(jsonString));    }
  • Unit test code:
@Test    public void testPojo() throws Exception {        Set<Class<?>> classes=ClassloadHelper.getClasses("xxx.core.model");        if(null!=classes){            for(Class classObj:classes){                try {                    boolean isEnumCodeName=this.isEnumCodeNameByObj(classObj);                    if(isEnumCodeName) {                        this.testEnum(classObj);                    }                    else {                        this.testObj(classObj);                    }                }                catch (Exception e){                    e.printStackTrace();                }            }        }    }

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.