Giter Site home page Giter Site logo

blog's People

Contributors

copie avatar

Stargazers

 avatar

Watchers

 avatar  avatar

blog's Issues

Elasticsearch 学习笔记

Elasticsearch 学习笔记

路由一个文档到一个分片中

shard = hash(routing) % number_of_primary_shards

routing 通常是id
number_of_primary_shards 主分片的数目

主分片一般不可以扩展,不通过手段的话

一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中

主分片和副本分片如何交互

每个节点都知道任意一个文档的位置,他可以将请求进行转发,
当发送请求的时候, 为了扩展负载,更好的做法是轮询集群中所有的节点。
? 我思考这里使用 nginx 进行负载均衡岂不是很好

新建、索引和删除文档

新建、索引和删除 是写操作必须在主分片上操作,然后复制到副本分片
具体过程是:

请求 -> 转发 —> 索引 -> 所有的副分片与主分片同步 -> 返回结果给转发的节点 -> 返回给客户端

取回单个文档

请求 -> 轮询转发 -> 查询 -> 返回结果给转发的节点 -> 返回给客户端

如果建立索引返回了 那么 主分片 和 副分片的数据是一致的。

局部更新文档

请求 -> 转发 —> 修改(如果有别的进程修改会在有限次数内重试)(重新建立索引) -> 所有的副分片与主分片同步 -> 返回结果给转发的节点 -> 返回给客户端

同步的时候用的是一个文档的全部,因为这个同步是异步的如果只同步修改可能会导致问题

多文档模式

mget

请求 —> 拆分 -> 转发 -> 处理 -> 搜集 -> 返回结果

bulk

请求 -> 拆分/组和 —> 转发 -> 修改 -> 同步 -> 搜集结果 -> 返回给客户端

bulk 对于格式要求严格 是性能上面的考虑

多索引, 多类型

/_search
在所有的索引中搜索所有的类型

/gb/_search
在 gb 索引中搜索所有的类型

/gb,us/_search
在 gb 和 us 索引中搜索所有的文档

/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型

/gb/user/_search
在 gb 索引中搜索 user 类型

/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型

/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型

映射和分析

_all 字段是 string
如果 Elasticsearch 猜测到 一个字段是 data 类型你用 str 的方式搜索会导致搜索不到结果。

精确值 VS 全文

精确值就像 sql 查询那样 是就是是,不是就是不是。
全文的话就是说获取到文档和查询的匹配度。
全文搜索我们更加希望获得更多相关的信息

倒排索引

所谓的倒排索引就是将所有的文档进行分词然后对每个词和所对应的文档建立索引。
类似与下面这个样子

Term      Doc_1  Doc_2
-------------------------
Quick   |       |  X
The     |   X   |
brown   |   X   |  X
dog     |   X   |
dogs    |       |  X
fox     |   X   |
foxes   |       |  X
in      |       |  X
jumped  |   X   |
lazy    |   X   |  X
leap    |       |  X
over    |   X   |  X
quick   |   X   |
summer  |       |  X
the     |   X   |
------------------------

如果对同义词和相同词根的词进行合并的话

Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
dog     |   X   |  X
fox     |   X   |  X
in      |       |  X
jump    |   X   |  X
lazy    |   X   |  X
over    |   X   |  X
quick   |   X   |  X
summer  |       |  X
the     |   X   |  X
------------------------

如果我们对用户的搜索词也采用这种方法进行统一就可以快速的获得结果。

分词和标准化叫做分析

复杂核心域类型

多值域

{ "tag": [ "search", "nosql" ]}

类型必须相同, 自动确定类型为第一个, 保存没有顺序

空域

Lucene 中不能存储 null 值

"null_value":               null,
"empty_array":              [],
"array_with_null_value":    [ null ]

都是就空域不会被索引

多层级对象

一个对象内可以有其他对象

{
    "tweet":            "Elasticsearch is very flexible",
    "user": {
        "id":           "@johnsmith",
        "gender":       "male",
        "age":          26,
        "name": {
            "full":     "John Smith",
            "first":    "John",
            "last":     "Smith"
        }
    }
}

内部对象映射

一个内部对象的 type 是 object。
其他都是一样

内部对象索引

{
    "tweet":            [elasticsearch, flexible, very],
    "user.id":          [@johnsmith],
    "user.gender":      [male],
    "user.age":         [26],
    "user.name.full":   [john, smith],
    "user.name.first":  [john],
    "user.name.last":   [smith]
}

可以通过 . 来引用比如:

tweet.user.name.first

索引建立不会有复杂的结构

内部对象数组

会丢失对象内部数据的关联性
使用 嵌套对象解决

{
    "followers": [
        { "age": 35, "name": "Mary White"},
        { "age": 26, "name": "Alex Jones"},
        { "age": 19, "name": "Lisa Smith"}
    ]
}

会被解释为:

{
    "followers.age":    [19, 26, 35],
    "followers.name":   [alex, jones, lisa, smith, mary, white]
}

查询

  • 查看所有的 index

    curl -X GET "http://localhost:9200/_cat/indices?v"
    
  • 查看一个 index 的信息

    curl -X GET "http://localhost:9200/_cat/indices/tests"
    
  • 删除一个索引

    curl -X DELETE  "http://127.0.0.1:9200/tests"
    
  • 查看 tests 索引下的 test 类型下的所有数据

    curl -X GET "http://127.0.0.1:9200/tests/test/_search"
    
  • 查看 id 是 1 的数据

    curl -X GET "http://127.0.0.1:9200/tests/test/1"
    
  • 只查看部分数据

    curl -X GET "http://127.0.0.1:9200/tests/test/1?_source=a,b"
    
  • 不显示 元数据

    curl -X GET "http://127.0.0.1:9200/tets/test/1/_source"
    
  • 检查文档是否存在

    curl -X HEAD "http://localhost:9200/tests/test/1"
    
  • 更新文档 文档不可改变 所以所谓的更新就行进行重新创建

    curl -X PUT "http://localhost:9200/megacorp/employee/1" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music"
        ]
    }
    '
    
  • 创建文档

    // 创建一个文档如果 _id 已经存在创建失败 201/409 op_type=create
    // curl -X PUT "http://localhost:9200/megacorp/employee/4?op_type=create"
    // 也可以完成相同的工作
    
    curl -X PUT "http://localhost:9200/megacorp/employee/5/_create" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John jj",
        "last_name": "Smith kk",
        "age": 90,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music",
            "sf"
        ]
    }
    '
    
  • 删除文档

    curl -X DELETE  "http://localhost:9200/megacorp/employee/5" -H 'Content-Type: application/json'
    
  • 更新文档

    // 更新文档 使用 version 进行乐观的并发控制
    // 409 存的版本不同于提供的版本
    // 200 成功
    
    curl -X PUT "http://localhost:9200/megacorp/employee/1?version=9" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music"
        ]
    }
    '
    
    // 外部版本号控制并发
    // 可以提供比现在版本号高的版本号不可以低
    // 409/200
    curl -X PUT "http://localhost:9200/megacorp/employee/1?version=14&version_type=external" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music"
        ]
    }
    
    curl -X POST "http://localhost:9200/megacorp/employee/1/_update" -H 'Content-Type: application/json' -d'
    {
        "doc":{
            "age":26
        }
    }
    
  • 使用脚本更新文档

    curl -X POST "http://localhost:9200/megacorp/employee/1/_update" -H 'Content-Type: application/json' -d'
    {
        "script":"ctx._source.age+=1"
    }
    
  • 通过脚本给数组添加数据

    curl -X POST "http://localhost:9200/megacorp/employee/1/_update" -H 'Content-Type: application/json' -d'
    {
        "script" : {
            "inline" : "ctx._source.interests.add(params.new_interest)",
            "params" : {
                        "new_interest" : "search"
                }
        }
    
    }
    '
    
  • 批量获得多个文档, 相应数据和请求数据顺序相同

    curl -X GET "http://localhost:9200/_mget" -H 'Content-Type: application/json' -d'
    {
        "docs" : [
            {
                "_index" : "megacorp",
                "_type" :  "employee",
                "_id" :    2
    
            },
            {
                "_index" : "megacorp",
                "_type" :  "employee",
                "_id" :    1,
                "_source": ["age","about"]
            }
        ]
    }
    '
    
  • 在url中给出 _index 或者 _type, url中给定的可以被覆盖

    curl -X POST "http://localhost:9200/megacorp/employee/_mget" -H 'Content-Type: application/json' -d'
    {
        "docs":[
            {"_id":1},
            {"_id":2},
            {"_id":3},
            {
                "_id":4,
                "_index" : "megacorp",
                "_type" :  "employee"
            }
        ]
    }
    '
    
  • _index 和 _type 相同 而且只请求 id 的话直接使用 ids, 如果指定的 _id 不存在的话 found=false

    curl -X POST "http://localhost:9200/megacorp/employee/_mget" -H 'Content-Type: application/json' -d'
    {
        "ids":["1",2,3,"88"]
    }
    
  • 代价小的批量操作

    // 代价小的批量操作
    // { action: { metadata }}\n
    // { request body        }\n
    // { action: { metadata }}\n
    // { request body        }\n
    // delete 不需要 request body
    // create 只有不存在才会成功
    // index 创建新的或者是修改
    // update 脚本部分更新
    // http 状态码是 200
    // 具体的每个操作的状态码是各自的。
    // 该操作不具有有原子性
    curl -X POST "http://localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
    { "delete": { "_index": "megacorp", "_type": "employee", "_id": "1" }}
    { "create": { "_index": "megacorp", "_type": "employee", "_id": "1" }}
    { "first_name": "你好啊","last_name": "我不好","age": 73,"about": "他是一个73岁的老人", "interests": ["玩游戏","LOL"]}
    '
    // 不用重复的指定 _index, _type
    // 也可以覆盖
    // 具体多大就要看数据的大小,和机器性能了
    // 一个好的办法是开始时将 1,000 到 5,000 个文档作为一个批次
    curl -X POST "http://localhost:9200/megacorp/employee/_bulk" -H 'Content-Type: application/json' -d'
    { "delete": { "_id": "1" }}
    { "create": { "_id": "1" }}
    { "first_name": "你好啊","last_name": "我不好","age": 73,"about": "他是一个73岁的老人", "interests": ["玩游戏","LOL"]}
    '
    
  • 空搜索

    // 空搜索
    // hits.total 匹配到的文档总数
    // hits._score 文档与查询的匹配程度 默认降序
    // max_score 所有 hits._score 的最大值
    // took 搜索请求耗费了多少毫秒
    // shards 各个分片的状态, 灾难级别的故障,可能丢失部分分片,返回部分能够查到的数据并且报告失败  
    // 可以指定超时,超时前会返回给客户端部分或全部结果,其他分片继续查询,虽然结果已经返回了。
    curl -X GET "http://localhost:9200/_search?timeout=1ms"
    
  • 分页

    // size
    //      显示应该返回的结果数量,默认是 10
    // from
    //      显示应该跳过的初始结果数量,默认是 0
    // 分页深度问题,获得分页深度越大,需要消耗的性能越多,而且是指数型的。
    // 因为查询需要获取多个分片的结果。
    // 理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。
    // 现在假设我们请求第 1000 页--结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
    // 可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
    
    curl -X GET "http://localhost:9200/_search?size=5&from=10"
    
    • 前缀表示必须与查询条件匹配。类似地, - 前缀表示一定不与查询条件匹配
      curl -X GET "http://localhost:9200/_search?q=+age:25 -last_name:Smith"
      
  • 建立一个 mapping

    curl -X POST http://localhost:9200/index/fulltext/_mapping -H 'Content-Type:application/json' -d'
    {
            "properties": {
                "content": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "ik_max_word"
                }
            }
    
    }'
    
  • 查看 mapping

    curl -X GET http://localhost:9200/index/fulltext/_mapping
    
  • 打开一个索引

    curl -XPOST http://localhost:9200/index/_open
    
  • 查看分词器的效果

    curl -XGET "http://localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
    {
        "text":"百度一下你就知道","tokenizer": "ik_max_word"
    }'
    
  • 新建一个索引,并且指定一个type中的一个域的分析器

    // 不要将 type 设置为 string 因为它被废弃了,可以使用 text or keyword 
    curl -X PUT "http://localhost:9200/index" -H 'Content-Type: application/json' -d'
    {
        "mappings": {
        "fulltext" : {
            "properties" : {
            "content" : {
                "type" :    "text",
                "analyzer": "ik_max_word",
                "search_analyzer": "ik_max_word"
            },
            "date" : {
                "type" :   "date"
            },
            "name" : {
                "type" :   "keyword",
                "index": false
            },
            "user_id" : {
                "type" :   "long"
            }
            }
        }
        }
    }
    '
    
  • 获取一个 type 的 mapping

    curl -X GET "http://localhost:9200/products/_mapping/product"
    
  • 不可以直接修改一个type的 mapping

    // 不可以直接修改一个type的 mapping
    // 因为如果修改了类型,以前的索引全部都失败了
    // 但是可以添加一个字段,并设置类型
    // 如果想要修改的话需要用到  **Reindex API**
    curl -X PUT "http://localhost:9200/index/_mapping/fulltext" -H 'Content-Type: application/json' -d'
    {
        "properties" : {
            "tag" : {
            "type" :    "long",
            "index":    false
            }
        }
        }
    '
    
  • 匹配所有文档

    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "match_all": {}
        }
    }
    '
    
  • 简单的组合查询

    // 简单的组合查询
    // must, must_not, should, filter
    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query":{
        "bool":{
            "must":[{"match":{"tn":"百度一下"}},{"match":{"cn":"你就知道"}}]
        }
        }
    
    }
    '
    
  • 验证查询

    curl -X GET "http://localhost:9200/_validate/query?explain" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "bool": {
                "must": [
                    {
                        "multi_match": {
                            "query": "百度一下,你就知道",
                            "fields": ["a", "b", "c"],
                            "type": "cross_fields",
                            "minimum_should_match": "75%"
                        }
    
                    }
                ],
                "should": [
                    {
                        "multi_match": {
                            "query": "百度一下,你就知道",
                            "fields": ["a", "b", "c"],
                            "boost": 0.4,
                            "analyzer": "ik_smart",
                            "minimum_should_match": "100%"
                        }
                    }
                ]
            }
        }
    }
    '
    
  • 聚合 提取其他数据

    curl -X GET "http://localhost:9200/c2h4/tests/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query":{
            "bool":{
                "must":{ 
                        "multi_match": { 
                            "query": "百度一下, 你就知道", 
                            "fields":   [ "a", "b","c" ],
                            "minimum_should_match": "85%"
                            
                        } 
                }
            }
        },
        "aggregations":{
            "name" : {
                "terms" : { 
                    "field" : "x"
                },
                "aggregations":{
                    "types":{
                        "top_hits":{
                            "size":1,
                            "_source" : ["a","c"]
                        }
                    }
                }
            }
        },
        "size":0
    }
    '
    
  • 自定义评分

    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "function_score": {
                "query": {
                    "match": { "a": "百度一下" }
                },
                "script_score" : {
                    "script" : {
                        "source": "0.8*Math.log(1+doc[\u0027ppp\u0027].value) + Math.log(1+doc[\u0027rrr\u0027].value)+doc[\u0027aaa\u0027].value"
                    }
                }
            }
        }
    }
    '
    
  • 自定义评分指定计算

    // 这个例子会在 原来的评分基础上面加 100
    // 具体如何运算结合上面的那个例子一起用
    curl -X GET "http://localhost:9200/_search?pretty&&explain" -H 'Content-Type: application/json' -d'
    {
        "query":{
            "function_score": {
                    "query": {
                        "bool": {
                            "must": [
                                {
                                    "multi_match": {
                                        "query": "百度一下, 你就知道",
                                        "fields": ["a", "n", "ne"],
                                        "type": "cross_fields",
                                        "minimum_should_match": "85%",
                                        "analyzer": "ik_max_word"
                                    }
    
                                }
                            ],
                            "should": [
                                {
                                    "multi_match": {
                                        "query": "百度一下,你就知道",
                                        "fields": ["bn", "tn", "bne"],
                                        "boost": 1.4,
                                        "analyzer": "ik_smart",
                                        "type": "most_fields"
                                    }
                                }
                            ]
                        }
                    },
                    "script_score":{
                        "script":{
                            "source":"100"
                        }
                    },
                    "boost_mode":"sum"
            }
        }    
    }
    '
    
  • 过滤非空排序

    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "bool": {
                "must": [
                    {
                        "multi_match": {
                            "query": "百度一下,你就知道",
                            "fields": ["a", "n", "ne"],
                            "type": "cross_fields",
                            "minimum_should_match": "75%",
                            "analyzer": "ik_max_word"
                        }
    
                    },
                    {"exists" : { "field" : "p" }}
                ]
            }
        },
        "sort":[{"p":"asc"}, "_score"],
        "min_score": 10
    }
    '
    
  • 自定义字段的权重

    curl -X GET "http://localhost:9200/tests/test/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "bool": {
                "must": [{
                    "multi_match": {
                        "query": "百度一下,你就知道",
                        "fields": ["t^20", "s", "c^1","a^10"],
                        "minimum_should_match": "75%"
                    }
                }]
            }
        },
        "from": 0,
        "size": 15
    }
    '
    
  • 使用 painless 脚本进行排序

curl -X GET "http://localhost:9200/tests/_search?pretty" -H 'Content-Type: application/json' -d'
{
    "query": {
        "bool": {
            "must": [
                {
                    "multi_match": {
                        "query": "百度一下, 你就知道",
                        "fields": ["a", "n", "ne"],
                        "type": "cross_fields",
                        "minimum_should_match": "2<85%"
                    }
                }
            ]
    },
    "_source": ["u","p"],
    "sort": {
        "_script":{
            "type":"number",
            "script":{
                "lang": "painless",
                "source":"if(doc[\u0027p\u0027].value > 0){return doc[\u0027p\u0027].value-params.factor;}else{return 0 ;}",
                "params" : {
                    "factor" : 9999999
                }
            },
            "order" : "asc"
        }
    },
    "size":1000
}
'
  • 同义词的使用
    curl -X PUT "http://localhost:9200/tests" -H 'Content-Type: application/json' -d'
    {
        "settings": {
            "index":{
                "max_result_window" : 90,
            "analysis" : {
                "analyzer" : {
                    "synonym" : {
                        "tokenizer" : "ik_max_word",
                        "filter" : ["synonym"]
                    }
                },
                "filter" : {
                    "synonym" : {
                        "type" : "synonym",
                        "synonyms_path" : "analysis/synonym.txt"
                    }
                }
            }
            }
        },
        "mappings": {
            "test": {
                "properties": {
                    "n": {
                        "type": "text",
                        "analyzer": "ik_max_word",
                        "search_analyzer": "synonym"
                    }
                }
            }
        }
    }
    '
  • 繁体字统一转换为简体字
curl -X PUT "http://localhost:9200/ps" -H 'Content-Type: application/json' -d'
{
        "settings": {
            "index":{
                "max_result_window": 600,
                "analysis": {
                    "analyzer": {
                        "ik_max_word":{
                            "tokenizer": "ik_max_word",
                            "char_filter": ["tsconvert"]
                        }
                    },
                    "tokenizer" : {
                        "tsconvert" : {
                            "type" : "stconvert",
                            "convert_type" : "t2s"
                        }
                    },
                    "char_filter" : {
                        "tsconvert" : {
                            "type" : "stconvert",
                            "convert_type" : "t2s"
                        }
                    }
                }
        }
    },
    "mappings": {
        "p": {
            "properties": {
                "name": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "ik_max_word"
                }
            }
        }
    }
}
'
  • 过滤单字
curl -X PUT "http://localhost:9200/ps" -H 'Content-Type: application/json' -d'
{
    "settings": {
        "index":{
            "max_result_window": 600,
            "analysis": {
                "analyzer": {
                    "ik_max_word":{
                        "tokenizer": "ik_max_word",
                        "char_filter": ["tsconvert"]
                    },
                    "synonym": {
                        "tokenizer": "ik_max_word",
                        "filter": ["synonym"],
                        "char_filter": ["tsconvert"]
                    },
                    "synonym_no_one":{
                        "tokenizer": "ik_max_word",
                        "filter": ["synonym", "no_one"],
                        "char_filter": ["tsconvert"]
                    }
                },
                "filter": {
                    "synonym": {
                        "type": "synonym",
                        "synonyms_path": "analysis/synonym.txt"
                    },
                    "no_one": {
                        "type": "length",
                        "min": 2
                    }
                },
                "tokenizer" : {
                    "tsconvert" : {
                        "type" : "stconvert",
                        "convert_type" : "t2s"
                    }
                },
                "char_filter" : {
                    "tsconvert" : {
                        "type" : "stconvert",
                        "convert_type" : "t2s"
                    }
                }
            }
        }
    },
    "mappings": {
        "pt": {
            "properties": {
                "name": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "synonym"
                }
            }
        }
    }
}'

pytest 笔记

pytest 笔记

  • 使用 assert 进行断言

  • 使用 pytest file1.py file2.py 去测试一个单独的文件

  • 使用 pytest filepath 测试这个文件夹下的所有测试文件

  • 使用 pytest 测试当前文件夹下的所有测试文件

  • 格式

    • 文件名 test_<something>.py 或者 <something>_test.py
    • 方法或者函数名 test_<something>
    • 类名 Test<Something>
  • pytest 的输出

    • PASSED (.) 成功
    • FAILED (F) 失败
    • SKIPPED (s) 跳过 需要使用装饰器 @pytest.mark.skip 或者
      @pytest.mark.ifskip()
    • xfail (x) 使用装饰器 @pytest.mark.xfail() 如果测试失败显示 xfail
      如果成功就是 XPASS(X).
    • XPASS (X) 不去跳过 pass, ran, passed
    • ERROR (E) 错误
  • 测试一个测试文件中的一个测试例 pytest test_something.py::test_nihao

命令行参数

  • 使用 pytest -v 选项显示详细的信息
  • --collect-only 只进行收集所有的测试例
  • -k 匹配测试例 匹配字符串用 'or' 分割 pytest "asdict or defaults"
  • -m 只测试某些有特殊标记的测试例
    import pytest
    
    ...
    @pytest.mark.你好
    def test_member_access():
    ...
    使用 pytest -m 你好 去测试
  • -x 遇到失败立刻退出
  • --tb=no 不显示错误信息,还有其他的一些参数
  • --maxfail
  • –-lf, -–last-failed
  • –ff, –failed-first
  • -q 静默模式
  • -l
  • -s 输出测试文件中的print

  • 捕获异常
    def xxx():
        raise TypeError
    with pytest.raises(TypeError):
        xxx()

  • 给测试函数打标记然后 通过 -m 参数去筛选出我们想要测试的测试函
    import pytest
    
    @pytest.mark.user
    @pytest.mark.role
    def xxx():
        pass
    
    @pytest.mark.user
    @pytest.mark.roles
    def yyy():
        pass
    -m 的筛选字符串可以写成下面的样子:
        "user"
        "user or roles"
        "user and role"
        "user and not role"

  • 添加上下文,比如连接数据库等
    import pytest
    
    @pytest.fixture(autouse=True)
    def xxx(tmpdir):
        print(tmpdir)
        yield
        print("结束")
    
    def test_xx():
        print("开始")
  • 跳过测试用例
    通过 -rs 参数可以输出reason
    import pytest
    
    x = 200
    
    @pytest.mark.skip(reason="我就是想要跳过这个测试")
    def test_xxx():
        assert None is None
    
    @pytest.mark.skipif(x != 100,reason="x 不等于100所以跳过")
    def test_yyy():
        assert None is None
    除了 -rs 还有 -rp 等等
    (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed,
    (p)passed, (P)passed with output, (a)all except pP.
  • 当我们预期到可能存在错误时可以使用 xfail 标记
    import pytest
    
    x = 100
    
    @pytest.mark.xfail(x == 100,reason="x 等于 100")
    def test_xxx():
        assert None is not None
    
    @pytest.mark.xfail()
    def test_yyy():
        assert None is None
    
    @pytest.mark.xfail(reason="我预期到这里要失败")
    def test_zzz():
        assert None is not None

  • 测试单个实例 pytest -v tests/func/test_api_exceptions.py::TestUpdate::test_bad_id, 单个文件,文件夹,类都照着这个写.

  • 多次测试不同的函数,但是每一次有不同的参数
    import pytest
    
    @pytest.mark.parametrize("a,b,c", [(1,2,3),(4,5,6)])
    def test_xxx(a,b,c):
        assert a in range(100) and b in range(100) and c in range(100)

pytest Fixtures

  • fixture 简单用
    import pytest
    
    @pytest.fixture()
    def some_data():
        return 42
    def test_some_data(some_data):
        assert some_data == 42
  • 通过 conftest.py 共享 fixture. 通常情况下将 conftest.py 放到
    测试文件夹的根目录,当然其他子目录也可以,但是根目录会更加的有意义. pytest
    会自动的找 这个文件所以不用在测试文件中导入这个模块
  • 用 fixture 配置 Setup 和 Teardown
    import pytest
    
    class Data:
        def b(self):
            print("开始")
            print(self)
        def e(self):
            print("结束")
            print(self)
    d = Data()
    
    @pytest.fixture()
    def tasks_db():
        d.b()
        yield
        d.e()
    
    def test_xxx(tasks_db):
        print("开始测试")
    
    def test_yyy(tasks_db):
        print("开始测试了")
  • 指定 fixture 的范围
    import pytest
    
    @pytest.fixture()
    def data_1():
        return (1, 2, 3)
    
    @pytest.fixture()
    def data_2():
        return (4, 5, 6)
    
    
    @pytest.fixture(scope="session")
    def data_3():
        print("开始3")
        yield "你好"
        print("结束3")
    
    
    @pytest.fixture(scope="session")
    def data_4():
        print("开始4")
        yield
        print("结束4")
    
    
    def test_xxx(data_1, data_2, data_3, data_4):
        print(data_1)
        print(data_2)
        print(data_3)
    
    def test_yyy(data_1, data_2, data_3, data_4):
        print(data_1)
        print(data_2)
        print(data_3)
    
    范围一共有 function, class, module, session.
    表示的是 在什么范围内只运行一次.
  • 类使用 fixture
    import pytest
    
    @pytest.fixture(scope='class')
    def class_scope():
        """A class scope fixture."""
        print("你好")
        yield
        print("结束")
    
    @pytest.mark.usefixtures('class_scope')
    class TestSomething():
        """Demo class scope fixtures."""
    
        def test_1(self):
            """Test using a class scope fixture."""
        def test_2(self):
            """Again, multiple tests are more fun."""
  • 习惯上总是使用 autouse
    """Demonstrate autouse fixtures."""
    
    import pytest
    import time
    
    
    @pytest.fixture(autouse=True, scope='session')
    def footer_session_scope():
        """Report the time at the end of a session."""
        yield
        now = time.time()
        print('--')
        print('finished : {}'.format(time.strftime('%d %b %X', time.localtime(now))))
        print('-----------------')
    
    
    @pytest.fixture(autouse=True)
    def footer_function_scope():
        """Report test durations after each function."""
        start = time.time()
        yield
        stop = time.time()
        delta = stop - start
        print('\ntest duration : {:0.3} seconds'.format(delta))
    
    def test_1():
        """Simulate long-ish running test."""
        time.sleep(1)
    
    
    def test_2():
        """Simulate slightly longer test."""
        time.sleep(1.23)
    
    可以使用 autouse=True 来使 这个 fixture 被自动的使用,和直接写相比,不能够传入参数了.
    如果这样写十分的有必要不然的话,建议是直接写名字
  • 重命名 fixture
    通常情况下我们使用函数名当作 fixture 的名字,也可是使用 @pytest.fixture(name="lue") 这样来指定 fixture 的名字.
  • fixture 参数
    import pytest
    
    datas = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
    data_ids = ["test_1","test_2","test_3"]
    
    @pytest.fixture(params=datas, ids=data_ids)
    def f_a(request):
        return request.param
    
    def test_a(f_a):
        print(f_a)
    
    解释一下这里的 ids 就是为了我们测试的时候知道显示出来的字符串,就是为了看起来方便.
    ids 可以是一个函数,函数的参数,就是 datas 中的一项
    可以在 f_a 中添加对数据的处理.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.