在一个团队里面,有一个非常致关重要的角色,就是产品经理了(Product Manager), 这个角色是衔接需求到产品设计到开发的桥梁。
从用户的角度来说,是不明白产品的,用户有一个或者多个痛点,或者以一系列需求,需要产品的实现一些列功能去解决这些个用户的需求,或者痛点。
这个就是产品的价值定位,所谓价值定位,这个产品如何用一些列功能区解决用户的痛点或者提升用户的核心价值。
那么,从技术实现的角度来说,作为开发人员是不明白这些个产品的需求,从用户和产品设计人员来说是不明白如何才能实现这个产品。
那么,产品经理就是一个桥梁沟通了这2个模块,也就是产品设计到产品实现,他既能明白产品的需求也能明白需要什么样的技术实现。
现代的产品经理的角色已经渐渐模糊,所有的领域行业知识,都由领域专家可以提供,这些都作为一个个产品的场景表现出来,
也就是Specification,这是一个非常有效的产品需求描述文档。
Specification用一种所有人都能明白的语言来告诉开发的人员怎么去完成指定的功能,而不是造成误解。
现在,渐渐地在产品开发团队里面,用Specification的文档化高效地沟通产品和开发双方。
现在,我们来看一个例子:
产品A有一个功能: C端能够通过搜索能够把B端成列在Market里面的货物,上架到C端的小店里面,然后以一个预定的定价规则定价。
那么这个功能需求非常概念化,非常笼统,里面包含了至少3个场景
1. C端搜索Market里面已经有的B端的商品。
2. C端把B端的产品上架到C端的小店
3. C端根据规则对B端的产品定价
这个是3个Specification,在Jira里面作为3个User Story表现出来
Specification用一种共同的语言来表达这些场景
S1. C端搜索Market里面已经有的B端的商品
Feature: 搜索商品
对于商户已经上架的产品,用户个人的小店可以去搜索,
然后把B端的他感兴趣的产品上架到他自己的小店。
Scenario Outline: 用户搜索商家的已经展示的商品
Given
一个用户叫做单斐,他想开一个卖吉他的小店,他去这个在线Market的搜索页面
When
他输入<keyword?>
Then
一系列的<Product?> <should?> 展示出来
Examples:
| keyword | Product | should?
| Flamenco吉他 | flamenco吉他列表 | should
| CD | flamenco吉他列表 | shouldn't
| 古典吉他 | 古典吉他列表 | should
| Flamenco吉他 | 古典吉他列表 | shouldn‘t
| 吉他 | 古典吉他和Flamenco吉他列表 | should
-------------------------------------------------------------------
那么这是一个 Specification,非常清楚地记录了这个需求和用例(Use Case)
谁? 在什么前提下? 做了什么? 得到什么结果?
--------------------------------------------------------------------
这是Gerkin语言定义了这个Specification的规范
Feature: 功能点,
Scenario Outline: 场景描述
Given: 在一种什么样的情况下
When: 做了什么?
Then: 获得什么结果
这是一个规范的简介的文档
Examples: 是基于这个功能的实际场景的实际用例
也就是所有的基本的测试用例的定义
这些可以直接被翻译成代码的测试用例
那么现在有很多现代的BDD的基于Specification的测试框架
1. Cucumber 2. Specification, 3. Spock ...... 等等还有很多
这里我就用其中的Spock做一个例子,把上述的一个Specification的文档,直接翻译成一个Spock的测试用例
用Groovy的代码
@Narritive('''
对于商户已经上架的产品,用户个人的小店可以去搜索,
然后把B端的他感兴趣的产品上架到他自己的小店。
Scenario Outline: 用户搜索商家的已经展示的商品
''')
class SearchCommoditiesSpec extends spock.lang.Specification {
@SearchServiceImpl
ISearchService searchService
@MockData("FlamencoGutiarLists")
List<Product> flamencoGuitars;
@MockData("ClassicalGutiarLists")
List<Product> classicalGuitars;
@MockData("MockProducts")
List<Product> products;
def setup() {
}
def "用户单斐搜索关于吉他的产品"() {
given: "一个用户叫做单斐,他想开一个卖吉他的小店,他去这个在线Market的搜索页面"
User user = new User("Fei")
when: "他输入关键字"
List<Product> products = searchService.search(keyword)
then: " 一系列的产品应该被展示出来"
products.size() > 0
where: "一些实际场景如下"
keyword | products.size || Should?
"Flamenco吉他" | 3 || products.contains(flamencoGuitars)
"CD" | 2 || not products.contains(flamencoGuitars)
"古典吉他" | 10 || products.contains(classicalGutars)
"吉他" | 13 | products.contains(classicalGutars).contains(flamencoGuitars)
}
def teardown() {
}
}
当然这个例子的一些细节需要调整,一些annotation需要定制,但是不是什么大的问题。
接下来就是在TDD开发实现这个Use Case
@Component("SearchService")
public class SearchService implement ISearchService {
@Autowired
IElasticSearchService searchService;
@Override
<T extends Product> List<T> search(String keyword) {
return searchService.search(keyword);
}
}
那么在开发完,我们就可以重新运行一次这个SearchCommoditiesSpec,
看看是不是通过了测试,如果通过了,说明我们的实现正确,
这个时候,就可以把Jira上的任务,mark成Resolved,完成,然后移交给测试人员,做regression test,和intergration test
以及Pressure test,在测试完成后,就可以在jira上mark任务为Done,在这个版本release的时候,就可以把这个开发完的Feature
也就是功能提合并到master分支,再从master分支创建出基于这个Version的Release分支。
然后,把Release分支发布到Staging服务器,最后在Staging服务器完成Beta测试后,就可以直接发布到Production服务器,
最后,这个产品的功能就等于正式交付用户了。