Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
300 views
in Technique[技术] by (71.8m points)

tdd - Clarification of Test Driven Development in Python

I am wondering if I can get some clarification on TDD and just Testing in general. I'm using python to write my program, and pytest as my testing framework.

So far, the way I have been doing it, writing these tests first, has started to get my brain to take a pragmatic approach towards certain tasks.

  1. Everything apart from 3 class variables were written before the tests were written, does that mean I'm technically not following TDD?
  2. Just for clarity, am I supposed to write the entire test for an unimplemented method first, then create the method and functionality so the tests pass?
  3. Is my main objective following TDD to have every single test pass, no matter what I change in the test data?(Which it does)(hence eliminating the potential for bugs?)
  4. Are tests for a class supposed to number greatly(depending on the size of the class)?
  5. Based on the code below, can you tell me if I am on the correct path

Code for your reference

import pytest
import os
import sys

path = os.path.dirname(os.path.abspath(__file__))
path = path.replace("\pytest", "")
sys.path.append(path)
path += "\pyffi"
sys.path.append(path)

from NifExplorer import NifExplorer
from NifExplorer import NifFormat

@pytest.fixture(autouse=True, scope='session')
def setup_nifExplorer():
    Explorers = []

    explorer = NifExplorer()
    explorer.SetBlockType(NifFormat.NiNode)
    explorer.SetResultPath("\pytest\results")
    explorer.SetSearchPath("\pytest\nif\base")

    explorer2 = NifExplorer()
    explorer2.SetBlockType(NifFormat.ATextureRenderData)
    explorer2.SetResultPath("\pytest\results")
    explorer2.SetSearchPath("\pytest\nif\base")

    explorer3 = NifExplorer()
    explorer3.SetBlockType("NiNode")
    explorer3.SetResultPath("\pytest\testResults")
    explorer3.SetSearchPath("\pytest\nif\base")

    Explorers.append(explorer)
    Explorers.append(explorer2)
    Explorers.append(explorer3)


    return Explorers   

@pytest.mark.usefixtures("setup_nifExplorer")
class TestNifExplorer:
    def NifExlorer_BlockType_Is_Not_None(self, setup_nifExplorer):
        assert setup_nifExplorer.BlockType != None

    def NifExplorer_SearchPath_Is_Not_None(self, setup_nifExplorer):
        assert setup_nifExplorer.SearchPath != None

    def NifExplorer_ResultPath_Is_Not_None(self, setup_nifExlorer):
        assert setup_nifExlorer.ResultPath != None
        
    @pytest.mark.parametrize('funcs', (NifExplorer_SearchPath_Is_Not_None, NifExplorer_ResultPath_Is_Not_None, NifExlorer_BlockType_Is_Not_None))
    def test_NifExplorer_Variables_Equal_Not_None(self, setup_nifExplorer, funcs):
        for obj in setup_nifExplorer:
            funcs(self,obj)
        
    def NifExplorer_ResultPath_Directory_Exists(self, setup_nifExplorer):
        assert os.path.exists(setup_nifExplorer.ResultPath) == True

    def NifExplorer_SearchPath_Directory_Exists(self, setup_nifExplorer):
        assert os.path.exists(setup_nifExplorer.SearchPath) == True

    def NifExplorer_SearchPath_Directory_Contains_No_Forward_Slashes(self, setup_nifExplorer):
        assert setup_nifExplorer.SearchPath.count('/') < 1

    def NifExplorer_ResultPath_Directory_Contains_No_Forward_Slashes(self, setup_nifExplorer):
        assert setup_nifExplorer.ResultPath.count('/') < 1

    @pytest.mark.parametrize('funcs', [NifExplorer_ResultPath_Directory_Exists, NifExplorer_SearchPath_Directory_Exists, NifExplorer_SearchPath_Directory_Contains_No_Forward_Slashes, NifExplorer_ResultPath_Directory_Contains_No_Forward_Slashes])
    def test_NifExplorer_Directories_Exist_And_Paths_Contain_No_Forward_Slashes(self, setup_nifExplorer, funcs):
        for obj in setup_nifExplorer:
            funcs(self,obj)

    def NifExplorer_SearchPath_Contains_Nif_Files_Recursively(self, setup_nifExplorer):
        assert setup_nifExplorer.DirectoryContainsNifRecursively(setup_nifExplorer.SearchPath) == True

    @pytest.mark.parametrize('funcs', [NifExplorer_SearchPath_Contains_Nif_Files_Recursively])
    def test_NifExplorer_SearchPath_Contains_Nif_Files(self, setup_nifExplorer, funcs):
        for obj in setup_nifExplorer:
            funcs(self,obj)
if __name__ == "__main__":
    pytest.main()
question from:https://stackoverflow.com/questions/65661437/clarification-of-test-driven-development-in-python

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Everything apart from 3 class variables were written before the tests were written, does that mean I'm technically not following TDD?

If you are writing any "real" code before you have a test that needs it, then you aren't following TDD. That can be OK - TDD isn't necessarily the best tool to use in all situations. But when you are in a situation where TDD makes sense, then the test is written first.

am I supposed to write the entire test for an unimplemented method first, then create the method and functionality so the tests pass?

It depends on whose guidance you are following. Robert Martin advises that you write just enough of the test for it to fail, and then make that much pass, then write more test, then make that pass, and so on, until the test is complete. He called this the nano cycle. Martin suggested this as a technique for reproducing the experience of working with Kent Beck.

However, when Kent Beck himself describes TDD, he talks about writing one complete test, and then fixing the problems in it one at a time.

I myself find that Beck's style works better for me, especially in a coding environment that can automatically fill some of the missing code. So I'll concentrate exclusively on the design of the test until I am satisfied with it, then let the IDE create the skeleton code I will need to pass the test, and the finally fill in the details.

Is my main objective following TDD to have every single test pass

Tests are really "mistake detectors"; the objective is to create code that delivers value without mistakes.

One of the benefits of TDD is that the ritual greatly reduces the number of "mistakes" you have to deal with at any one time. And yes, that means that the discipline is that we are almost always in one of two states, either (a) all of the tests we have written are passing, or (b) all except one of the test we have written are passing.

Are tests for a class supposed to number greatly(depending on the size of the class)?

Not necessarily size; complexity might be a better gauge - how many different input classes are there? You will normally need at least one sample from each to ensure that all of the behaviors you need are present.

A common illustration here: imagine a function that takes a Gregorian year as input, and returns true if that year is a leap year. The input classes we care about

  • year not divisible by 4
  • year divisible by 4, but not by 100
  • year divisible by 100, but not by 400
  • year divisible by 400

So four "tests"?

can you tell me if I am on the correct path

Hard to say. There are a couple ideas that stand out, however.

Most real work includes both "code that is complicated" and "code that is hard to test". One useful trick is to design your code so that the complicated code is easy to test, and the code that is hard to test is so simple that there are obviously no deficiencies.

"Hard to test" includes behaviors that depend on shared mutable state (like a file system). So we'll often choose designs where the file system code is too simple to break, put all the branching somewhere else, and then thoroughly test the branching code.

Furthermore, test design is a thing. In particular, we normally want to prioritize making the tests easy to understand at a glance. This in turn will often mean that you'll keep all of the code for one test together, rather than having a bunch of "set up" code in one place and your asserts somewhere else.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...