`
kidneyball
  • 浏览: 330014 次
  • 性别: Icon_minigender_1
  • 来自: 南太平洋
社区版块
存档分类
最新评论

无聊写了个单元测试的基类,准备拿回公司用

 
阅读更多
package net.daniel.test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

/**
 * 本测试用例适用于JUnit4,提供以下功能:
 * 1. 通过标注(Annotation)进行测试用例对象域的创建和注入。提供以下标注:
 *  a)@Mock private MyType myField; 自动创建MyType类型的Mock对象,赋值给myField;
 *  b) @Create private MyType myField; 自动创建MyType类型的对象实例,赋值给myField;
 *  c)@Create @Inject private MyType myField; 自动创建MyType类型的对象,赋值给myField,并且注入到被测试用例的同名对象域中。
 * 2. 可通过反射机制直接访问被测试用例(或其它对象)的不可见对象域,支持继承的对象域;
 * 3. 可通过反射机制直接调用被测试用例(或其它对象)的不可见方法,支持继承的方法,支持自动匹配与参数表最接近的重载方法签名,无须另行指定参数类型列表。
 * 4. 支持通过Objnesis机制创建类实例
 * 5. 支持按测试用例对象域名称获取合适的mockery对象类型
 * 6. 提供若干集合处理的便利方法
 *
 * @author Daniel Deng
 * 
 * @param <CUT> 被测试的类型
 *            
 */
public abstract class AbstractTestCase<CUT> {

    /**
     * 被测试的对象
     */
    protected CUT toTest;
    
    /**
     * 用于Mock接口的Mockery
     */
    protected Mockery interfaceMockery = new Mockery();
    
    /**
     * 用于Mock具体类的Mockery
     */
    protected Mockery classMockery = new Mockery(){{
        setImposteriser(ClassImposteriser.INSTANCE);
    }};

    /**
     * 当前TestCase对象域的缓存,包括祖先类中的对象域。
     */
    private Map<String, Field> mockedFields,
                               createFields,
                               injectFields,
                               allFields;
    
    /**
     * 被测试对象的对象域缓存,包括祖先类中的对象域。
     */
    private Map<String, Field> sutFields;
    
    /**
     * 用于强行创建类实例的Objenesis对象,详情参考 http://code.google.com/p/objenesis/
     */
    private Objenesis objenesis;

    /**
     * 每次测试前执行的初始化方法,JUnit将保证此方法会在子类的@Before方法之前执行。
     * 在本基类中,此方法负责处理标注与注入
     * @throws Exception
     */
    @Before
    public void init() throws Exception {
        applyAnntation();
        toTest = createSUT();
        applyInjection();
    }
    
    /**
     * 子类应覆盖此方法,创建并返回被测试类的实例。
     * 此方法将在@Mock与@Create标注处理完成后才被调用。如果需要的话,在创建被测试类实例时可使用这些被标注的对象域。
     */
    protected abstract CUT createSUT();

    /**
     * 处理注入({@link Inject})标注。此方法应在createSUT()方法之后调用。
     * 
     * 
     * @throws Exception
     */
    protected void applyInjection() throws Exception {
        prepareSUTFields();
        for (Field f : injectFields.values()) {
            Inject injectAnno = f.getAnnotation(Inject.class);
            String injectFieldName = injectAnno.value();
            if (injectFieldName == null) injectFieldName = f.getName();
            Field fieldToInject = sutFields.get(injectFieldName);
            if (fieldToInject == null) {
                throw new NoSuchFieldException("Field [" + injectFieldName + "] is not found in Class [" + toTest.getClass().getName() + "].");
            }
             fieldToInject.set(toTest, f.get(this));
        }
    }

    private void prepareSUTFields() {
        if (sutFields == null) {
            Class<?> clazz = toTest.getClass();
            sutFields = newHashMap();
            while (notAtTopLevel(clazz)) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field f : fields) {
                    f.setAccessible(true);
                    String fname = f.getName();
                    if (!sutFields.containsKey(fname)) {
                        sutFields.put(fname, f);
                    }
                }
                clazz = clazz.getSuperclass();
            }
        }
    }
    
    /**
     * 通过 Objenesis机制来强制创建类实例,不会执行构造方法.
     * @param clazz 需要创建实例的类
     * @return
     */
    @SuppressWarnings("unchecked")
    protected <T> T forceCreate(Class<T> clazz) {
        if (objenesis == null) objenesis = new ObjenesisStd();
        return ((T) objenesis.getInstantiatorOf(clazz).newInstance());
    }

    private void performCreateFields() throws Exception {

        for (Field f : createFields.values()) {
            Create create = f.getAnnotation(Create.class);
            assert (create != null);
            Class<?> clazz = create.value();
            if (clazz == Object.class) {
                clazz = f.getType();
            }
            Constructor<?> constructor = null;
            Object newInstance = normalInstanciate(clazz, constructor);

            if (newInstance == null) {
                newInstance = forceCreate(clazz);
            }

            if (newInstance != null) {
                f.set(this, newInstance);
            } else {
                throw new RuntimeException("Type [" + clazz.getName()
                        + "] of field [" + f.getName()
                        + "] cannot instanciate.");
            }
        }
    }

    private Object normalInstanciate(Class<?> clazz, Constructor<?> constructor) {
        Object newInstance = null;
        try {
            constructor = clazz.getConstructor();
        } catch (Exception e) {
            // Will be handled by objenesis later
        }
        if (constructor != null) {
            try {
                newInstance = clazz.newInstance();
            } catch (Exception e) {
                // Will be handled by objenesis later
            }
        }
        return newInstance;
    }

    /**
     * 处理{@link Create}与{@link Mock}标注,实例化测试用例的对象域
     * @throws Exception
     */
    protected void applyAnntation() throws Exception {
        prepareTestCaseFields();
        performCreateFields();
        performMockFields();
    }

    private void performMockFields() throws Exception{
        for (Field f : mockedFields.values()) {
            Class<?> clazz = f.getType();
            if (clazz.isInterface()) {
                f.set(this, interfaceMockery.mock(clazz));
            } else {
                f.set(this, classMockery.mock(clazz));
            }
        }
    }
    
    /**
     * JMock需要根据Mock对象的类型不同而选用不同的mockery实例,容易引起混淆和错误。此方法将根据传入的测试用例对象域名称自动选用合适的mockery实例。
     * @param fieldName 采用了Mock对象的测试用例对象域名称
     * @return 与指定对象域类型对应的Mockery实例
     * @throws Exception
     */
    protected Mockery mockery(String fieldName) throws Exception {
        prepareTestCaseFields();
        Field f = allFields.get(fieldName);
        if (f == null) throw new NoSuchFieldException("Field [" + fieldName + "] is not found in Class [" + this.getClass().getName() + "]");
        if (f.getType().isInterface()) {
            return interfaceMockery;
        } else {
            return classMockery;
        }
    }

    private void prepareTestCaseFields() {
        if (mockedFields == null) {
            Class<?> clazz = this.getClass();
            mockedFields = newHashMap();
            createFields = newHashMap();
            injectFields = newHashMap();
            allFields = newHashMap();
            while (notAtTopLevel(clazz)) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field f : fields) {
                    f.setAccessible(true);
                    String fname = f.getName();
                    if (!allFields.containsKey(fname)) {
                        allFields.put(fname, f);
                    }
                    if (f.getAnnotation(Mock.class) != null
                            && !mockedFields.containsKey(fname)) {
                        mockedFields.put(fname, f);
                    }
                    if (f.getAnnotation(Create.class) != null
                            && !createFields.containsKey(fname)) {
                        createFields.put(fname, f);
                    }
                    if (f.getAnnotation(Inject.class) != null
                            && !injectFields.containsKey(fname)) {
                        injectFields.put(fname, f);
                    }
                }
                clazz = clazz.getSuperclass();
            }
        }
    }
    
    /**
     * 绕过可见性限制,为被测试对象的对象域赋值
     * @param fieldName 要赋值的对象域名称
     * @param value 将要赋予对象域的值
     * @throws Exception
     */
    protected void setSUTField(String fieldName, Object value) throws Exception {
        Field f = findSUTField(fieldName);
        f.set(toTest, value);
    }
    
    /**
     * 绕过可见性限制,获取被测试对象的对象域取值
     * @param fieldName 要获取的对象域名称
     * @return 被测试对象中指定对象域的取值
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected <T> T getSUTField(String fieldName) throws Exception {
        Field f = findSUTField(fieldName);
        return (T) f.get(toTest);
    }

    private Field findSUTField(String fieldName) throws NoSuchFieldException {
        prepareSUTFields();
        Field f = sutFields.get(fieldName);
        if (f == null) throw new NoSuchFieldException("Field [" + fieldName + "] is not found in Class [" + toTest.getClass().getName() + "]");
        f.setAccessible(true);
        return f;
    }
    
    /**
     * 绕过可见性限制,为一个指定对象的对象域赋值
     * @param obj 需要赋值的对象实例
     * @param fieldName 要赋值的对象域名称
     * @param value 将要赋予对象域的值
     * @throws Exception
     */
    protected void setField(Object obj, String fieldName, Object value) throws Exception {
        Field f = findField(obj, fieldName);
        f.set(obj, value);
    }
    
    /**
     * 绕过可见性限制,获取一个指定对象的对象域取值
     * @param obj 需要获取对象域取值的对象实例
     * @param fieldName 要获取的对象域名称
     * @return 指定对象中指定对象域的取值
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected <T> T getField(Object obj, String fieldName) throws Exception {
        Field f = findField(obj, fieldName);
        return (T) f.get(obj);
    }
    
    /**
     * 获取指定对象中指定名称的对象域反射引用
     * @param obj 指定的对象实例
     * @param fieldName 对象域名称
     * @return
     * @throws Exception
     */
    protected Field findField(Object obj, String fieldName) throws Exception {
        Class<?> clazz = obj.getClass();
        Field field = null;
        while (notAtTopLevel(clazz)) {
            try {
                field = clazz.getDeclaredField(fieldName);
            }catch (NoSuchFieldException e) {
                //leave the field as null
            }
            if (field != null) {
                field.setAccessible(true);
                return field;
            }
            clazz = clazz.getSuperclass();
        }
        throw new NoSuchFieldException("Field [" + fieldName + "] is not found in Class [" + toTest.getClass().getName() + "]");
    }

    private boolean notAtTopLevel(Class<?> clazz) {
        return clazz != null && clazz != Object.class;
    }
    
    /**
     * 绕过可见性限制,调用被测试对象实例中的指定方法(包括父类中的方法)。本方法将根据传入的参数表自动选择匹配的重载方法。
     * @param methodName 方法名称
     * @param parameters 调用阐述表
     * @return 调用方法的返回值
     * @throws Exception
     */
    protected <T> T invokeSUTMethod(String methodName, Object... parameters) throws Exception {
        return invokeMethod(toTest, methodName, parameters);
    }
    
    /**
     * 绕过可见性限制,调用指定对象实例中的指定方法(包括父类中的方法)。本方法将根据传入的参数表自动选择匹配的重载方法。
     * @param obj 指定的对象实例
     * @param methodName 方法名称
     * @param parameters 调用阐述表
     * @return 调用方法的返回值
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected <T> T invokeMethod(Object obj, String methodName, Object... parameters) throws Exception {
        Method m = findMethod(obj.getClass(), methodName, map(Arrays.asList(parameters), new Fun1<Object, Class<?>>(){
            public Class<?> f(Object arg0) throws Exception {
                return arg0 != null ? arg0.getClass() : null;
            }
        }));
        return (T) m.invoke(obj, parameters);
    }
    
    /**
     * 获取指定基础类型的对应装箱类型。
     * @param clazz 需要装箱的类型
     * @return 如果clazz为java基础类型,返回其对应的装箱类型。否则,返回clazz本身。
     */
    protected Class<?> Boxing(Class<?> clazz) {
        if (clazz == null) return null;
        if (clazz == Boolean.TYPE) return Boolean.class;
        if (clazz == Byte.TYPE) return Byte.class;
        if (clazz == Short.TYPE) return Short.class;
        if (clazz == Integer.TYPE) return Integer.class;
        if (clazz == Long.TYPE) return Long.class;
        if (clazz == Float.TYPE) return Float.class;
        if (clazz == Double.TYPE) return Double.class;
        return clazz;
    }
    
    private Method findMethod(final Class<? extends Object> clazz, final String methodName, final List<Class<?>> argumentClasses) throws Exception {
        Class<?> c = clazz;
        while (notAtTopLevel(c)) {
            List<Method> methods = filter(Arrays.asList(c.getDeclaredMethods()), new Fun1<Method, Boolean>() {
                public Boolean f(Method arg0) throws Exception {
                    return methodName.equals(arg0.getName()) && (arg0.getParameterTypes().length == argumentClasses.size());
                }
            });
            for (Method m : methods) {
               Class<?>[] paramTypes = m.getParameterTypes();
               boolean matched = true;
               for (Pair<Class<?>, Class<?>> paramPair : zip(argumentClasses, Arrays.asList(paramTypes))) {
                   if (paramPair.first != null && !Boxing(paramPair.second).isAssignableFrom(paramPair.first)) {
                       matched = false;
                       break;
                   }
               }
               if (matched) {
                   m.setAccessible(true);
                   return m;
               }
            }
            c = c.getSuperclass();
        }
        throw new NoSuchMethodException("Method [" + methodName + "] is not found in class [" + clazz.getName() + "].");
    }

    //一些便利的集合初始方法。可免去在创建时重复指定泛型参数的麻烦。
    /**
     * 创建一个{@link HashMap}实例
     * @return
     */
    protected static <K, V> Map<K, V> newHashMap() {
        return new HashMap<K, V>();
    }
    
    /**
     * 创建一个{@link ArrayList}实例
     */
    protected static <E> List<E> newArrayList() {
        return new ArrayList<E>();
    }
    
    /**
     * 创建一个{@link HashSet}实例
     * @return
     */
    protected static <E> Set<E> newHashSet() {
        return new HashSet<E>();
    }

    //标注定义
    
    /**
     * 标记了此标注的测试用例对象域会由{@link AbstractTestCase#applyAnntation()}方法自动赋予对应的Mock对象实例。
     * @author Daniel Deng
     *
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    protected @interface Mock {
    }

    /**
     * 标记了此标注的测试用例对象域会由{@link AbstractTestCase#applyAnntation()}方法自动创建一个对象实例。如果指定了value属性,
     * 则创建由该属性所指定类型的对象实例,否则将创建被标注的对象域所定义类型的对象实例。如果该类型无法通过{@link Class#newInstance()}直
     * 接实例化,则将尝试采用Objnesis机制强制实例化。
     * @author Daniel
     *
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    protected @interface Create {
        public Class<?> value() default Object.class;
    }

    /**
     * 标记了此标注的测试用例对象域会由{@link AbstractTestCase#applyInjection()}方法自动注入到被测试对象实例中。可以用value属性
     * 来指定被测试对象的对象域名称。如果未指定该属性,则默认注入到与被标注的测试用例对象域同名的对象域中。
     * @author Daniel
     *
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    protected @interface Inject {
        public String value() default "";
    }
    
    //一些进行简单的函数式集合处理的通用接口
    protected static interface Fun<R> {
        R f() throws Exception;
    }
    
    protected static interface Fun1<P0, R> {
        R f(P0 arg0) throws Exception;
    }
    
    protected static interface Fun2<P0, P1, R> {
        R f(P0 arg0, P1 arg1) throws Exception;
    }
    
    protected static interface VoidFun {
        void f() throws Exception;
    }
    
    protected static interface VoidFun1<P0> {
        void f(P0 arg0) throws Exception;
    }
    
    protected static interface VoidFun2<P0, P1> {
        void f(P0 arg0, P1 arg1) throws Exception;
    }
    
    protected static class Pair<P1, P2> {
        public P1 first;
        public P2 second;
    }
    
    // 一些简单的模拟函数式集合处理的方法
    /**
     * 对原始集合中的所有元素进行转换,映射成新的集合
     * @param collection 原始集合实例
     * @param mapper 一个接受原始集合中的元素,将其转换为结果结合中的元素的函数对象
     * @return 经过转换后的新集合
     * @throws Exception
     */
    protected static <S, T> List<T> map(Collection<S> collection, Fun1<S, T> mapper) throws Exception {
        List<T> converted = newArrayList();
        if (collection != null && collection.size() > 0) {
            Iterator<S> it = collection.iterator();
            while (it.hasNext()) {
                S element = it.next();
                converted.add(mapper.f(element));
            }
        }
        return converted;
    }
    
    /**
     * 返回由原始集合中满足特定条件的元素所组成的新集合
     * @param collection 原始集合实例
     * @param condition 一个接受原始集合中的元素,返回该元素是否满足条件的函数对象。如果该函数对象返回true,则对应的元素会被保留到结果集合中
     * @return 由原始集合中满足特定条件的元素所组成的新集合
     * @throws Exception
     */
    protected static <S> List<S> filter(Collection<S> collection, Fun1<S, Boolean> condition) throws Exception {
        List<S> filtered = newArrayList();
        if (collection != null && collection.size() > 0) {
            Iterator<S> it = collection.iterator();
            while (it.hasNext()) {
                S el = it.next();
                if (Boolean.TRUE.equals(condition.f(el))) {
                    filtered.add(el);
                }
            }
        }
        return filtered;
    }
    
    /**
     * 将两个原始集合的元素按顺序两两配对,将配对结果形成新的集合
     * @param collection1 原始集合1
     * @param collection2 原始集合2
     * @return 一个由集合1与集合2中的元素两两配对所形成的新集合。该集合的元素为{@link Pair}类型,其中first域为集合1中的对应元素,second域为集合2中的对应元素。
     *         如果两个原始集合长度不同,则结果集合的长度等于最长的原始集合长度。在结果集合中,超出较短的原始集合长度部分对应的值{@link Pair}域值为null。
     * @throws Exception
     */
    protected static <P1, P2> List<Pair<P1, P2>> zip(Collection<P1> collection1, Collection<P2> collection2) throws Exception {
        Iterator<P1> it1 = collection1.iterator();
        Iterator<P2> it2 = collection2.iterator();
        List<Pair<P1, P2>> result = newArrayList();
        boolean is1HasNext = it1.hasNext();
        boolean is2HasNext = it2.hasNext();
        while (is1HasNext || is2HasNext) {
            Pair<P1, P2> pair = new Pair<P1, P2>();
            if (is1HasNext) pair.first = it1.next();
            if (is2HasNext) pair.second = it2.next();
            result.add(pair);
            is1HasNext = it1.hasNext();
            is2HasNext = it2.hasNext();
        }
        return result;
    }

}


pom.xml
	<dependencies>
		<dependency>
		    <groupId>junit</groupId>
		    <artifactId>junit</artifactId>
		    <version>4.10</version>
		</dependency>
		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest-all</artifactId>
			<version>1.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.jmock</groupId>
			<artifactId>jmock</artifactId>
			<version>2.5.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
		  <groupId>org.jmock</groupId>
		  <artifactId>jmock-legacy</artifactId>
		  <version>2.5.1</version>
		  <scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.objenesis</groupId>
			<artifactId>objenesis</artifactId>
			<version>1.2</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
0
1
分享到:
评论
1 楼 mfkvfn 2011-11-07  
已阅  

相关推荐

    c#学习笔记.txt

    若要在一个用 @ 引起来的字符串中包括一个双引号,请使用两对双引号:@ 符号的另一种用法是使用碰巧成为 C# 关键字的被引用的 (/reference) 标识符。 8, 修饰符 修饰符作用 访问修饰符 public private internal ...

    ChromeOS镜像文件.zip

    目录: ChromeOS-PC-20130222-oscome.com ChromeOS-Vanilla-4028.0.2013_04_20_1810-r706c4144 ChromeOS-Vanilla-4028.0.2013_04_20_1810-r706c4144-VirtualBox ChromeOS-Vanilla-4028.0.2013_04_20_1810-r706c4144-VMWare ChromeOS-virtualbox-20130222-OSCOME.COM ChromeOS-vmware-20130222-OSCOME.COM 网盘文件永久链接

    ieee33节点matlab模型

    IEEE33节点模型搭建,matlab

    3GPP R15 38.331 5G NR无线资源控制(RRC)协议规范解析

    3GPP R15 38.331 5G NR无线资源控制(RRC)协议规范解析

    基于ssm+mysql实现的零食商城系统(电商购物).zip(毕设&课设&实训&大作业&竞赛&项目)

    项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用,资源为网络商品(电子资料类)基于网络商品和电子资料商品的性质和特征不支持退款

    19考试真题最近的t44.txt

    19考试真题最近的t44.txt

    JSP基于SSH2新闻发布系统.zip(毕设&课设&实训&大作业&竞赛&项目)

    项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用,资源为网络商品(电子资料类)基于网络商品和电子资料商品的性质和特征不支持退款,质量优质,放心下载使用

    19考试真题最近的t49.txt

    19考试真题最近的t49.txt

    19考试真题最近的t61.txt

    19考试真题最近的t61.txt

    电动汽车充电站选址定容优化:基于MATLAB建模求解与成本最小化策略,电动汽车充电站选址定容优化:基于MATLAB的最优规划模型及初学者指南,电动汽车充电站的最优选址定容MATLAB程序 以规划期内充

    电动汽车充电站选址定容优化:基于MATLAB建模求解与成本最小化策略,电动汽车充电站选址定容优化:基于MATLAB的最优规划模型及初学者指南,电动汽车充电站的最优选址定容MATLAB程序 以规划期内充电站的总成本 (包括投资、运行和维护成本)和网损费用之和最小为目标,考虑了相关的约束条件,构造了电动汽车充电站最优规划的数学模型。 从34个位置中,选取7个充电站地址,进行选址优化 关键词:电动汽车;充电站;选址和定容 程序注释清晰,适合初学者学习 ,电动汽车; 充电站选址定容; MATLAB程序; 规划模型; 成本优化; 网损费用; 初学者学习; 程序注释清晰,基于MATLAB的电动汽车充电站选址定容优化程序:成本最小化与约束条件下的选址策略

    威纶通触摸屏图库模板程序:多尺寸适用,PS原文件可自由修改,便捷电气助手应用,威纶通触摸屏图库模板程序:多尺寸适用,PS原文件可自由修改,便捷电气助手应用,威纶通触摸屏图库模板程序(电气助手) 可直接

    威纶通触摸屏图库模板程序:多尺寸适用,PS原文件可自由修改,便捷电气助手应用,威纶通触摸屏图库模板程序:多尺寸适用,PS原文件可自由修改,便捷电气助手应用,威纶通触摸屏图库模板程序(电气助手) 可直接使用。 内附原图、PS原文件可自行修改 不同触摸屏,不同寸尺都可以使用 ,威纶通触摸屏; 图库模板程序; 电气助手; 直接使用; 原图; 修改; 兼容不同寸尺,威纶通触摸屏图库模板程序:电气助手,便捷编辑通用模板

    群辉引导7.2.2 最新 vmware workstation 已经帮忙转换好为vmdk文件 直接使用就可以

    修复 "保存'/opt/rr'的修改" 后 主菜单锁死问题. 修复 trivial 插件的语法错误. 修复 open-vm-tools 套件 缺失的 SOCKETS 驱动. 添加 vmtools 插件, 包含 qemu-ga & open-vm-tools. 4.1. 该插件会自动判断环境并启用对应的功能, 物理机也不用刻意删除该插件. 4.2. 新安装用户会默认选中, 升级用户如需要请手动添加该插件. 4.3. 如启用该插件, 请不要再在系统中安装套件. 修复 wireless 插件. 5.1. 修复 RR 下无线网络 IP 显示和刷新问题. 5.2. 修复 RR 下设置 SSID&PSK 后 DSM 下不驱动的问题. 5.3. 同步 RR 下的 SSID&PSK 到 DSM 下. 5.4. 修复 junior 模式下无线网络的支持, 已支持 无线网卡的 DSM 系统安装. (暂时不支持 intel 无线网卡) 5.5. wpa_supplicant.conf 文件位于引导盘第一个分区根目录, 纯无线环境可手动放置该文件后其启动引导.

    19考试真题最近的t66.txt

    19考试真题最近的t66.txt

    19考试真题最近的t37.txt

    19考试真题最近的t37.txt

    Arduino-Mega2560开发板-毕业设计

    Arduino_Mega2560开发板工程文件 包含 原理图 PCB图

    智能养猪系统的高精度称重算法及其Python实现(含详细可运行代码及解释)

    内容概要:本文详述了一种用于智能养猪的高精度称重系统设计及其实现方法,主要涵盖了卡尔曼滤波、数据采集与预处理、重量估算与存储等功能。文中提供了完整的Python代码示例和详细的代码解释,旨在减少噪声干扰并提高数据准确性。具体而言,通过对采集的数据进行卡尔曼滤波,去除异常值,并使用一定时间段内数据的平均值作为最终的体重估计。此外,还实现了一个简单的图形用户界面,能够实时显示称重数据和估计的重量。 适合人群:农业自动化领域的开发者和技术爱好者,尤其关注智能畜牧业的技术应用。 使用场景及目标:适用于智能养猪场的精准称重,提高养猪效率和管理水平,确保获取高精度、可靠的牲畜体重数据,帮助养殖场更好地管理饲养过程。同时,提供完整的源代码有助于相关人员理解和优化现有系统。 阅读建议:对于想要深入了解智能畜牧业相关技术的读者来说,可以通过本教程掌握从硬件接入、软件设计再到数据处理全流程的具体细节。重点关注各个关键算法的实现原理及其应用场景,从而为自己的项目带来启示与借鉴。

    基于SSM框架构建积分系统和基本商品检索系统(Spring+SpringMVC+MyBatis+Lucene+Redis+MAVEN).zip(毕设&课设&实训&大作业&竞赛&项目)

    项目工程资源经过严格测试运行并且功能上ok,可实现复现复刻,拿到资料包后可实现复现出一样的项目,本人系统开发经验充足(全栈全领域),有任何使用问题欢迎随时与我联系,我会抽时间努力为您解惑,提供帮助 【资源内容】:包含源码+工程文件+说明等。答辩评审平均分达到96分,放心下载使用!可实现复现;设计报告也可借鉴此项目;该资源内项目代码都经过测试运行,功能ok 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 【提供帮助】:有任何使用上的问题欢迎随时与我联系,抽时间努力解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 下载后请首先打开说明文件(如有);整理时不同项目所包含资源内容不同;项目工程可实现复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用,资源为网络商品(电子资料类)基于网络商品和电子资料商品的性质和特征不支持退款

    最新更新!!!地级市-产业链韧性数据(2006-2021年)

    ## 01、数据简介 产业链韧性是指在产业链部分环节出现问题或遭受内外部冲击时,产业链仍能保持其稳定性和动态平衡,迅速做出反应并恢复正常运转的能力。这种能力体现了产业链的复杂适应性,是其能够应对各种不确定性因素和破坏性事件的重要保障。 产业链韧性是保障产业链安全稳定运行的重要基础,对于提升产业竞争力、推动经济高质量发展具有重要意义。 数据名称:地级市-产业链韧性数据 数据年份:2006-2021年 ## 02、相关数据 代码 年度 城市 产业结构HHI 获得专利数 第一产业增加值占GDP比 第二产业增加值占GDP比 第三产业增加值占GDP比 产业链韧性

    PNP发射极接地开关仿真原理图

    PNP发射极接地开关仿真原理图

    上门预约服务小程序v4.10.9+前端.zip

    上门预约服务小程序v4.10.9+前端 文章列表单图时,图标统一左侧对齐 文章内增加视频位置,显示在文章顶部 文章内底部导航增加首页、分享、自定义按钮,可跳转内部页面、其他小程序、业务域名内的H5页面,方便宣传使用

Global site tag (gtag.js) - Google Analytics