用Mockito测试SpringMVC+Hibernate
用Mockito测试SpringMVC+Hibernate
译自:Spring 4 MVC+Hibernate 4+MySQL+Maven integration + Testing example using annotations
2017-01-19
目录:
1 目录结构
2 pom.xml
3 Testing Controller Layer
3.1 com.websystique.springmvc.controller.AppControllerTest
4 Testing Service Layer
4.1 com.websystique.springmvc.service.EmployeeServiceImplTest
5 Testing Data Layer
5.1 com.websystique.springmvc.configuration.HibernateTestConfiguration
5.2 com.websystique.springmvc.dao.EntityDaoImplTest
5.3 com.websystique.springmvc.dao.EmployeeDaoImplTest
5.4 src/test/resources/Employee.xml
源代码 : SpringHibernateExample.zip
1 目录结构

2 pom.xml
与 被测项目 Spring 4 MVC+Hibernate 4+MySQL+Maven使用注解集成实例中pom.xml 一样。
其中,
- Spring-test : 在测试类中使用 spring-test annotations
- TestNG : 使用testNG作为测试框架
- Mockito : 使用mockito模拟外部依赖, 比如当测试service时mock dao,关于mockito,请参考Mockito教程
- DBUnit : 使用DBUnit管理数据,当测试data/dao层时
- H2 Database : 对数据库层测试,与其说是单元测试不如说是集成测试,使用H2 Database对数据库层进行测试
3 Testing Controller Layer
3.1 com.websystique.springmvc.controller.AppControllerTest
package com.websystique.springmvc.controller; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import org.joda.time.LocalDate;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import static org.mockito.Mockito.atLeastOnce; import org.springframework.context.MessageSource;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import com.websystique.springmvc.model.Employee;
import com.websystique.springmvc.service.EmployeeService; public class AppControllerTest { @Mock
EmployeeService service; @Mock
MessageSource message; @InjectMocks
AppController appController; @Spy
List<Employee> employees = new ArrayList<Employee>(); @Spy
ModelMap model; @Mock
BindingResult result; @BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
employees = getEmployeeList();
} @Test
public void listEmployees(){
when(service.findAllEmployees()).thenReturn(employees);
Assert.assertEquals(appController.listEmployees(model), "allemployees");
Assert.assertEquals(model.get("employees"), employees);
verify(service, atLeastOnce()).findAllEmployees();
} @Test
public void newEmployee(){
Assert.assertEquals(appController.newEmployee(model), "registration");
Assert.assertNotNull(model.get("employee"));
Assert.assertFalse((Boolean)model.get("edit"));
Assert.assertEquals(((Employee)model.get("employee")).getId(), 0);
} @Test
public void saveEmployeeWithValidationError(){
when(result.hasErrors()).thenReturn(true);
doNothing().when(service).saveEmployee(any(Employee.class));
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "registration");
} @Test
public void saveEmployeeWithValidationErrorNonUniqueSSN(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(false);
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "registration");
} @Test
public void saveEmployeeWithSuccess(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(true);
doNothing().when(service).saveEmployee(any(Employee.class));
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "success");
Assert.assertEquals(model.get("success"), "Employee Axel registered successfully");
} @Test
public void editEmployee(){
Employee emp = employees.get(0);
when(service.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(appController.editEmployee(anyString(), model), "registration");
Assert.assertNotNull(model.get("employee"));
Assert.assertTrue((Boolean)model.get("edit"));
Assert.assertEquals(((Employee)model.get("employee")).getId(), 1);
} @Test
public void updateEmployeeWithValidationError(){
when(result.hasErrors()).thenReturn(true);
doNothing().when(service).updateEmployee(any(Employee.class));
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model,""), "registration");
} @Test
public void updateEmployeeWithValidationErrorNonUniqueSSN(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(false);
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model,""), "registration");
} @Test
public void updateEmployeeWithSuccess(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(true);
doNothing().when(service).updateEmployee(any(Employee.class));
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model, ""), "success");
Assert.assertEquals(model.get("success"), "Employee Axel updated successfully");
} @Test
public void deleteEmployee(){
doNothing().when(service).deleteEmployeeBySsn(anyString());
Assert.assertEquals(appController.deleteEmployee("123"), "redirect:/list");
} public List<Employee> getEmployeeList(){
Employee e1 = new Employee();
e1.setId(1);
e1.setName("Axel");
e1.setJoiningDate(new LocalDate());
e1.setSalary(new BigDecimal(10000));
e1.setSsn("XXX111"); Employee e2 = new Employee();
e2.setId(2);
e2.setName("Jeremy");
e2.setJoiningDate(new LocalDate());
e2.setSalary(new BigDecimal(20000));
e2.setSsn("XXX222"); employees.add(e1);
employees.add(e2);
return employees;
}
}

右击该测试类,得到结果如下:
PASSED: deleteEmployee
PASSED: editEmployee
PASSED: listEmployees
PASSED: newEmployee
PASSED: saveEmployeeWithSuccess
PASSED: saveEmployeeWithValidationError
PASSED: saveEmployeeWithValidationErrorNonUniqueSSN
PASSED: updateEmployeeWithSuccess
PASSED: updateEmployeeWithValidationError
PASSED: updateEmployeeWithValidationErrorNonUniqueSSN ===============================================
Default test
Tests run: 10, Failures: 0, Skips: 0
===============================================
解读:
因为被测类AppController依赖EmployeeService , MessageSource, Employee, ModelMap & BindingResult。因此,为了测试AppController,需要提供这些依赖。
@Mock //Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为
EmployeeService service; @Mock
MessageSource message; @InjectMocks //InjectMocks创建这个类的对象并自动将标记@Mock、@Spy等注解的属性值注入到这个中
AppController appController; @Spy //Spy是一个真实的对象,但它可以设置对象行为
List<Employee> employees = new ArrayList<Employee>(); @Spy
ModelMap model; @Mock
BindingResult result;
其中,when..then 模式用于设置对象行为。
另外,需要加入以下代码:
MockitoAnnotations.initMocks(this); //初始化被注释的[@Mock, @Spy, @Captor, @InjectMocks] 对象
4 Testing Service Layer
4.1 com.websystique.springmvc.service.EmployeeServiceImplTest
package com.websystique.springmvc.service; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import static org.mockito.Mockito.when; import org.joda.time.LocalDate;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import com.websystique.springmvc.dao.EmployeeDao;
import com.websystique.springmvc.model.Employee; public class EmployeeServiceImplTest { @Mock
EmployeeDao dao; @InjectMocks
EmployeeServiceImpl employeeService; @Spy
List<Employee> employees = new ArrayList<Employee>(); @BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
employees = getEmployeeList();
} @Test
public void findById(){
Employee emp = employees.get(0);
when(dao.findById(anyInt())).thenReturn(emp);
Assert.assertEquals(employeeService.findById(emp.getId()),emp);
} @Test
public void saveEmployee(){
doNothing().when(dao).saveEmployee(any(Employee.class));
employeeService.saveEmployee(any(Employee.class));
verify(dao, atLeastOnce()).saveEmployee(any(Employee.class));
} @Test
public void updateEmployee(){
Employee emp = employees.get(0);
when(dao.findById(anyInt())).thenReturn(emp);
employeeService.updateEmployee(emp);
verify(dao, atLeastOnce()).findById(anyInt());
} @Test
public void deleteEmployeeBySsn(){
doNothing().when(dao).deleteEmployeeBySsn(anyString());
employeeService.deleteEmployeeBySsn(anyString());
verify(dao, atLeastOnce()).deleteEmployeeBySsn(anyString());
} @Test
public void findAllEmployees(){
when(dao.findAllEmployees()).thenReturn(employees);
Assert.assertEquals(employeeService.findAllEmployees(), employees);
} @Test
public void findEmployeeBySsn(){
Employee emp = employees.get(0);
when(dao.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(employeeService.findEmployeeBySsn(anyString()), emp);
} @Test
public void isEmployeeSsnUnique(){
Employee emp = employees.get(0);
when(dao.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(employeeService.isEmployeeSsnUnique(emp.getId(), emp.getSsn()), true);
} public List<Employee> getEmployeeList(){
Employee e1 = new Employee();
e1.setId(1);
e1.setName("Axel");
e1.setJoiningDate(new LocalDate());
e1.setSalary(new BigDecimal(10000));
e1.setSsn("XXX111"); Employee e2 = new Employee();
e2.setId(2);
e2.setName("Jeremy");
e2.setJoiningDate(new LocalDate());
e2.setSalary(new BigDecimal(20000));
e2.setSsn("XXX222"); employees.add(e1);
employees.add(e2);
return employees;
}
}
右击该测试类,得到结果如下:
PASSED: deleteEmployeeBySsn
PASSED: findAllEmployees
PASSED: findById
PASSED: findEmployeeBySsn
PASSED: isEmployeeSsnUnique
PASSED: saveEmployee
PASSED: updateEmployee ===============================================
Default test
Tests run: 7, Failures: 0, Skips: 0
===============================================
Test Service Layer和Test Control Layer类似,不再详述
5 Testing Data Layer
DAO 或 data Layer测试一直是有争议的话题。我们到底要如何测试?把它当做单元测试的话,就要测试它的每一行代码,这样的话,要mocking所有的外部依赖。但是,我们没有与数据库本身的交互,就没法测data-layer。那么它就变成了集成测试。
通常,我们对DAO Layer做集成测试。这里,我们用in-memory H2 database做集成测试。
5.1 com.websystique.springmvc.configuration.HibernateTestConfiguration
package com.websystique.springmvc.configuration; import java.util.Properties; import javax.sql.DataSource; import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; /*
* This class is same as real HibernateConfiguration class in sources.
* Only difference is that method dataSource & hibernateProperties
* implementations are specific to Hibernate working with H2 database.
*/ @Configuration
@EnableTransactionManagement
@ComponentScan({ "com.websystique.springmvc.dao" })
public class HibernateTestConfiguration { @Autowired
private Environment environment; @Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.websystique.springmvc.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
} @Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
} private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
} @Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}

解读:
- 上面的类与HibernateConfiguration类非常相似,区别仅在 dataSource() & hibernateProperties()这两个方法的实现。
- 在Sources folder中,它做了几乎同样事情:它用dataSource创建了SessionFacoty,其中,dataSource被配置成可与in-memory database H2一起工作。为了使hibernate与H2一起工作,设置hibernate.dialect为H2Dialect。
- SessionFacoty会被注入到AbstractDao,而后当测试EmployeeDaoImpl类时,EmployeeDaoImpl会使用SessionFacoty。
5.2 com.websystique.springmvc.dao.EntityDaoImplTest
该类是所有测试累的基类
package com.websystique.springmvc.dao; import javax.sql.DataSource; import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.BeforeMethod; import com.websystique.springmvc.configuration.HibernateTestConfiguration; @ContextConfiguration(classes = { HibernateTestConfiguration.class })
public abstract class EntityDaoImplTest extends AbstractTransactionalTestNGSpringContextTests { @Autowired
DataSource dataSource; @BeforeMethod
public void setUp() throws Exception {
IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
dataSource);
DatabaseOperation.CLEAN_INSERT.execute(dbConn, getDataSet());
} protected abstract IDataSet getDataSet() throws Exception; }
解读:
- AbstractTransactionalTestNGSpringContextTests在某种程度上可以更JUnit的RunWith等价。这个抽象类集成Spring TestContext support到TestNG environment中。
- 为了在测试中提供数据访问层的支持,它也需要在ApplicationContext中定义datasource和transactionManager。我们已在上面的Configuration类中定义了datasource和transactionManager。
- 由于事物支持,每次测试前一个事物会被默认启动,在每次测试结束后这个事物会被回滚。你可以override这个回滚行为。
- BeforeTest在测试用执行前,我们将使用DBUnit去clean-insert测试数据库[h2]中的数据样例。这样避免各个测试方法之间的影响
- 抽象方法getDataSet会在测试类中实现为了在测试前提供真实的测试数据
5.3 com.websystique.springmvc.dao.EmployeeDaoImplTest
package com.websystique.springmvc.dao; import java.math.BigDecimal; import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.Test; import com.websystique.springmvc.model.Employee; public class EmployeeDaoImplTest extends EntityDaoImplTest{ @Autowired
EmployeeDao employeeDao; @Override
protected IDataSet getDataSet() throws Exception{
IDataSet dataSet = new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Employee.xml"));
return dataSet;
} /* In case you need multiple datasets (mapping different tables) and you do prefer to keep them in separate XML's
@Override
protected IDataSet getDataSet() throws Exception {
IDataSet[] datasets = new IDataSet[] {
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Employee.xml")),
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Benefits.xml")),
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Departements.xml"))
};
return new CompositeDataSet(datasets);
}
*/ @Test
public void findById(){
Assert.assertNotNull(employeeDao.findById(1));
Assert.assertNull(employeeDao.findById(3));
} @Test
public void saveEmployee(){
employeeDao.saveEmployee(getSampleEmployee());
Assert.assertEquals(employeeDao.findAllEmployees().size(), 3);
} @Test
public void deleteEmployeeBySsn(){
employeeDao.deleteEmployeeBySsn("11111");
Assert.assertEquals(employeeDao.findAllEmployees().size(), 1);
} @Test
public void deleteEmployeeByInvalidSsn(){
employeeDao.deleteEmployeeBySsn("23423");
Assert.assertEquals(employeeDao.findAllEmployees().size(), 2);
} @Test
public void findAllEmployees(){
Assert.assertEquals(employeeDao.findAllEmployees().size(), 2);
} @Test
public void findEmployeeBySsn(){
Assert.assertNotNull(employeeDao.findEmployeeBySsn("11111"));
Assert.assertNull(employeeDao.findEmployeeBySsn("14545"));
} public Employee getSampleEmployee(){
Employee employee = new Employee();
employee.setName("Karen");
employee.setSsn("12345");
employee.setSalary(new BigDecimal(10980));
employee.setJoiningDate(new LocalDate());
return employee;
} }
5.4 src/test/resources/Employee.xml
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<employee id="1" NAME="SAMY" JOINING_DATE="2014-04-16" SALARY="20000" SSN="11111" />
<employee id="2" NAME="TOMY" JOINING_DATE="2014-05-17" SALARY="23000" SSN="11112" />
</dataset>
右击该测试类,得到结果如下:
PASSED: deleteEmployeeByInvalidSsn
PASSED: deleteEmployeeBySsn
PASSED: findAllEmployees
PASSED: findById
PASSED: findEmployeeBySsn
PASSED: saveEmployee ===============================================
Default test
Tests run: 6, Failures: 0, Skips: 0
===============================================
我们以saveEmployee为例解读下执行过程:
1. 在测试方法运行前,Spring会通过@ContextConfiguration注释的EntityDaoImplTest类加载text context,还会通过AbstractTransactionalTestNGSpringContextTests创建beans实例。这只会发生一次。
2. 在bean实例创建前,Spring会创建SessionFactory Bean,并且SessionFactory Bean会被注入dataSource bean(在HibernateTestConfiguration类中定义),见下面属性设置
properties.put("hibernate.hbm2ddl.auto", "create-drop");
注意:由于hbm2ddl属性,当SessionFactory被创建,与Model类相关的schema会被验证并导出到数据库。这意味着Employee表会在H2数据库中创建。
3. 在测试前,@BeforeMethod注释的方法会被调用,该方法会通知DBUnit连接数据库执行clean-insert,在Employee表插入两个记录(见Employee.xml内容)
4. 现在测试用例saveEmployee将开始执行,在执行开始前,事物将被启动,saveEmployee方法本身将在事物中运行。一旦saveEmployee方法运行完毕,事物会回滚到默认的setup。
5. 测试用例saveEmployee开始执行。它会调用employeeDao.saveEmployee(getSampleEmployee()),被调用者会通过hibernate插入预先定义的Employee到H2 database中。这是关键的一步。在这一个之后,就会有3条记录在H2 database中。
6. 在下一个用例中,@BeforeMethod又会被调用
7. 当所有用例测试完后,session会被关掉,schema会被去除
用Mockito测试SpringMVC+Hibernate的更多相关文章
- Maven搭建SpringMVC+Hibernate项目详解 【转】
前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...
- Maven搭建SpringMVC+Hibernate项目详解
前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...
- Maven搭建SpringMVC+Hibernate项目详解(转)
前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...
- 框架篇:Spring+SpringMVC+hibernate整合开发
前言: 最近闲的蛋疼,搭个框架写成博客记录下来,拉通一下之前所学知识,顺带装一下逼. 话不多说,我们直接步入正题. 准备工作: 1/ IntelliJIDEA的安装配置:jdk/tomcat等..(本 ...
- Spring+SpringMVC+Hibernate小案例(实现Spring对Hibernate的事务管理)
原文地址:https://blog.csdn.net/jiegegeaa1/article/details/81975286 一.工作环境 编辑器用的是MyEclipse,用Mysql数据库,mave ...
- springmvc+hibernate
<本文摘要他人> 1.设计数据库:设计好表结构,最好符合3NF,采用Hibernate tools将设计好的表自动生成对应的实体entity. 1.创建Maven项目,按需映入Maven包 ...
- spring+springmvc+hibernate 整合
三大框架反反复复搭了很多次,虽然每次都能搭起来,但是效率不高.最近重新搭了一次,理顺了思路,整理了需要注意的地方,分享出来. 工具:Eclipse(jdk 1.7) spring和hibernate版 ...
- Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建
Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...
- spring+springmvc+hibernate 礼品管理系统
spring+springmvc+hibernate template礼品管理系统 1.简单介绍 如标题所示,这篇文章简单写了一个基于spring+springmvc+hibernate templa ...
随机推荐
- 强制修改mysql 中root的密码
/etc/init.d/mysqld stop (service mysqld stop )/usr/bin/mysqld_safe --skip-grant-tables另外开个SSH连接[ro ...
- Golang学习笔记:包制作
golang的包跟java as js之类的大不一样,一定要存在GOPATH里面,GOPATH是专门用于存放golang第三方的库,里面有两个文件夹, src:源代码目录 pkg:编译后的第三方包,这 ...
- CodeForces 616B Dinner with Emma
水题 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; +; ...
- Struts2---声明式异常处理
在service方法里 throw抛出一个异常, 然后再方法声明上加上throws: public List<Category> list() throws SQLException{ C ...
- MySQL 索引的使用
一.or 的使用 (1)MySQL版本大于 5.x 的会使用 index merge 功能,即可以将多个单列索引集合起来使用,不过在查询时使用 or 的话,引擎为 myisam 的会开启 index ...
- Spark1.3.0安装
之前在用Hadoop写ML算法的时候就隐约感觉Hadoop实在是不适合ML这些比较复杂的算法.记得当时写完kmeans后,发现每个job完成后都需要将结果放在HDFS中,然后下次迭代的时候再从文件中读 ...
- NSDateFormatter调整时间格式的代码
NSDateFormatter调整时间格式的代码 在开发iOS程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用NSDateFormatter类来处理.例如://实例化一个NSDat ...
- 苹果应用商店AppStore审核中文指南 分类: ios相关 app相关 2015-07-27 15:33 84人阅读 评论(0) 收藏
目录 1. 条款与条件 2. 功能 3. 元数据.评级与排名 4. 位置 5. 推送通知 6. 游戏中心 7. 广告 8. 商标与商业外观 9. 媒体内容 10. 用户界面 11. 购买与货币 12. ...
- Mac iTerm2使用rz、sz从远程上传下载文件
使用 brew install lrzsz .如果安装遇到错误的话,使用以下方法: 在mac终端下运行: brew install lrzsz (安装教程:http://brew.sh/index_z ...
- Nodejs之使用session
nodejs中使用session的说明. session介绍 为什么使用session: session运行在服务器端,当客户端第一次访问服务器时,可以将客户的登陆信息保存. 当客户访问其他界面时,可 ...