sheet2graph command-line program
Ep.14 - Writing tests
32 minutes
After the testing infrastructure and helpers are ready, we are writing the tests for our commandline application.
• Testing input files
• Testing location of output files
• Testing generated image sizes
• Testing graph types
Notes
• Source code in Githubhttps://github.com/fromzerotofullstack/sheet2graph/tree/tests_implementation
Transcript
finally we can start writing the tests so let's create a
branch for this and we call it test implementation
okay then we want to start adding here real tests
so let's just start let's remove the fake tests and we're gonna
start testing different ones um we need to test like different
functionalities so do not forget let's structure them like in the
following way so basically we want to test things
related to the output of files we also want to test
what else the sizes else uh there is also
the kind of graph so if it's like a bar or lines
sorry like these graph types and the input formats so for now
this will do so the input formats will be like sometimes it's like a google
docs sometimes an extern and when the test all of we test the the
main parts of the application right and we can because we have all
these methods and we should be able to test it
so let's write one first test that is real and then we
will we'll see if it passes so i'm going to
call this one test output folder default and what we
want here is to test the the default uh way of calling our
command so um let's move this here
yeah so like something like this like it's not the default but maybe more
like one of these
one with the output file name so something like this
something like this would be more the default
like this and without the graph type and probably also without the output
file name um maybe with the output folder so let's
say something like output folder
and then like a folder right so something like this so we're gonna test
something like this so let's see this output folder exactly
so the output folder option default so first we're gonna need a command
okay and then okay we'll do the following thing because we're gonna use
an input all the time so this will be like series
data.csv but most of the tests or all of them
will need it right so let's add here another variable with
a with one of these inputs so csv file
and then it should be like sales data dot csv but i think also
it's a lot of things here so we have the files just thrown here
but i think it's better
okay i will check this now i think it's better if we just put here my folder
test data and then we just copy them here so
this way this contains the data we use for testing and it's not in the middle
of of everything else um
okay so for now we do it like this and then here we can load it from test
data so it's a bit neater
and we're not just going to be a csv file we're also going to be
needing the ods file which is the same but dot ods
and the x lsx file
okay so this will be all the all our files that we'll be
inputting and actually [Music]
we we will also need the google docs right
so let's set that so this is the our public uh docs that
we use to test um and i think this should cover it so
let's continue with the testing the output one so basically
instead of our calling here an input we're gonna use a variable
input and and then when the rest of the options so
the output folder folder then
okay and then we need the out and the out will be
um based on this folder right we said that everything is gonna be
um outputted inside this folder so let's see how that looks like
basically it will look something like this and
then as the input we use the output folder for the output
exactly and as the out we use the
now as the output we use the folder output it's
confusing but we're going to be copy pasting this and as the input we use the
csv exactly so now it's correct
and then this out is going to be one two three for the output test so we
need more folders there so let's say we create it in output
texts so this is the same as if we would be just passing like this only it's all
based in this in this folder so we can remove it
conveniently so we can think of it as as this column
if we're going to test this command output test we can just
write it like this with the with this other command prefixing it
okay so here then we have our first command
and we need to run that command so we have already this
nice helper and we just pass through the command
the command doesn't include the this part
the python or my command.pi because that is added already by by run command right
you see here so only the continuation the params
so this will run our command and then after it runs our command we need to
test so what can we test and we can test
that the file is created and that the folder is created
so to do that we're gonna need a couple of utility methods
so uh basically there's this these utilities
inside os path uh code is here
and this file so this is very convenient basically it needs to check if
if a file exists already actually this utility overloads this so
i would say we don't need we don't need this one anymore
it's literally the same let's see what it does exactly
in os path this file
here both of them there's a strife but it's an existing regular file okay so
this is exactly the same that we did before so
we can remove this code and we will use this
file or easter then here we want to check if it
generates a file so which file
the following file the file starting with out
and then continuing with output slash tests so the same way test we're
putting here so that's the folder and then uh the default
name and the default name was output um so let's add this
so let's say output output and then dot png
i think it's the default format and then we need to interpolate this
with like out equal self folder output
okay so this one should be okay for the file and then we also want to check that
the directory has been created it's a bit redundant
but just to just to double check and then this would
be so the folder exists and the file exists so we have our first real test
and the next ones are going to be easy so don't worry we're just going to be
copy pasting this and doing some variations and we're benefiting
benefiting it benefiting from the this utility is right so now running a
command is much quicker and getting the inputs is all a lot
quicker so let's start by testing now this
by running the tests okay so it failed there's a key error
params in test okay so i think here we need to add this
key because we could call it by like this
and then then it would just be positional but
because we added the name i think we need
the pass name like this so let's try again
okay it breaks again but for a different reason
i set equal missing one required position argument second
let's see is so here we don't want the set equal we want to start true
and now just checking if this condition is true so
let's try again and now we have one test one real test
that run so this is actually checking something
so that's good news but we need to add a lot more
let's add a few more uh starting from the
basic one so first here we're testing the output folder
so it would be cool if we would test also the the output
the filename option basically we want to test all the things
that we had here before so this we're not going to be using
anymore but we're going to test all these options so the size
the this one the output file name is one we're gonna test right now
so all of them uh all the output formats and then we can get rid of this
so because now this will be covered in the tests so we've been just keeping
them there for our friends so we can start removing
some of these okay so a lot cleaner and
and then they will also not show up when we do
so now this looks good um then file name so here
we have output file name output
file name and then we can pass a file name so for instance here we can put
out png the rest is the same csp folder
and we run the command and we check and the only thing different
here is that this is called out instead of output
it should be the same other than this so let's try this
so the output folder before and now the output
it's running it could be our second test test
okay and it run both tests so it seems to be doing okay
um then what else do we need to test um there's this thing here where one of
these options overrides the other right here
so filename overwrites output folder if present
so let's add a test for that too because it's useful to know if the
overwrite is working so let's name this overwrite
output folder and uh for this we need to pass both
options so we're gonna pass an output folder
and it's gonna be like out let's say test two and then we're gonna
pass an output file name same as before with uh
output tests out okay and then we're gonna check check first
that the folder exists then that the the file exists so same as
the previous one just these two but we also want to check
that no folder was created so that the overwrite
work so for this let's say i set fonts and then we use
this test right so here output two should be out
out test2 so the same as here okay let's run this
again we see here one dot for each test so
three tests three seconds now okay let's continue
um what else do we have to test so we're in the output section right so it
would be cool to test the output format so png jpg
svg so let's let's do that um okay let's copy one of these
and we're gonna create one for each format so first
you can call this output we don't need to test the output folder
anymore so we're mostly going to be using the
file name which is more convenient yeah output test out so this is the same
one we had and here we can just start testing the
file we're going to test the folder every
time so this will be for png and then we're gonna have two more so
we're gonna have two one for the
for the jpg and one and one for the svg
so this should do it so with this we're testing the main
output options okay let's just try to run them again
and six tests and it's running um let's commit this for now
then we can start testing now the sizes the graph types the input formats so all
of all of what we have here um
okay so let's do that let's continue with the sizes we have this nice
function this helper method here that get image resolution so we're going to
use this to test the sizes so it shouldn't be too complicated let's
see first of all
[Music] let's say the default size
so we have an input with a file name no extra options
and we want to see what size so we can do this and then um
then first what is the default size so the default is 700
horizontal and 500 vertical for the size so we need to check that that's that's
the case so there will be here something like assert true
like with equals equals 500 so it will be something like
this and what we need to do now is to get the
width and height and we can use the get image resolution
exactly and here we only need to pass the file name so the same one
output the same one you can see here output test
so here we're getting the resolution with height
the file we're checking that it exists and then we also
check the resolution so let's run this one again
and while we wait we're going to do the same
for the for non-default sizes and for other
formats right okay so one failed no such file directory output tests
out okay let's see and i think we forgot or i forgot
to put here to replace the outlet so
the output i think that's it yeah because otherwise just the string
curly brackets out so okay and the other one is replaced yes
so let's run it again and as i was saying we'll need to do the
same for four different sizes right okay so this time they run now let's add
the same but for different sizes so we're gonna need one
that is let's say we can remove the default so
this is like a specific size and we can try here like a size
i don't know we could say maybe like twice this
so like per like a thousand
and then the rest is pretty much the same only the test
here needs to be 14 and 10. so this should work
we're gonna try it now and then we also do the same thing but with a different
extension so below i don't think it works with
vector graphics so for now we're just gonna test with the formats that uh with
the bitmap format formats so here which is that jpg
okay and then the size is also going to be the big one maybe we can try
different size here also just for fun so we cover more more cases
so let's say 400 300 and then here we use the same 400
and 300 so if we didn't make any mistake all of the size tests here should
shoot run so let's give it a try okay
it's looking good yes all of them run 11 seconds so each time it's taking a bit
longer makes sense and since this
seems to be working let's do now the the graphs so we can start testing that
uh i mean of course we cannot test that the
graph is actually a bar graph or a line graph because
uh i don't know if any library to do that um
but we could check that the commands finish that the file exists
etc so let's take this one as an example and we can test first the graph type bar
so here we can do like first we can move the
the size and then we're gonna add here like a
graph type bar we could test like a lot more things we
could test each of these commands with a long or
the short variation so graph type had like a short variation
this gt but uh i mean since we're using here this
library we know this variation works so of course we could test it but we're
going to find something reasonable between
testing things that we know work and testing the actual behavior and if we
would have so many tests generated manually and we changed
something we also need to go back and change those stats so
so we need to find like a compromise that's why i'm focusing first on all the
options and the main things and then it could be expanded to a lot
more things like the short version of the comments uh probably i'm
missing out on the the size of the vector graphics if it
even makes sense that it's inside the contents of the file
but uh for now we're focusing on the main on the main ones
so as i was saying here we have the graph bar
and we need to test here just that the file exists
so here is not gonna be any resolution check and
it's literally the same thing so let's just do it for
for the three types so there's gonna be bar there's gonna be
line line and it's going to be scatter
scatter and that's it graph types okay so let's see if this works and then
a whole category is already tested so we run it again
it's going to take a bit longer and while we wait
the only one we have left test is the input formats
so this will be what we will be implementing now
okay so let us work 12 tests already and let's test the input formats so
we'll copy one of these here we want to test like all the
different inputs so sometimes it's csv sometimes it's excel
so let's start by removing all the extra things that we don't need
we're keeping the out.png okay so this one is actually very
similar but just make it clear we can keep it
for the csv and then we're gonna need others right
for the other formats so the other formats
well here it says all of this but actually we're accepting
only a few of those so let me just check all of this okay well let's start
by there by the most common ones these ones exactly so we're accepting
csv xls xlsx and obs
no okay so um let's add support for this csv and xlsx
and and maybe also this ods that we had here
okay okay so let's just create at least two more of these
one is going to be this ods so um so here we go
just to change the input and the other one is going to be
the x that's it
so this should cover these two and then we also need to test the google
docs so let's have one for the google docs
um we can okay let's let's add here just the name so if you
don't forget and we'll comment it out for now let's
see if the other ones work first okay so this one we will comment let's
see if this runs with xlds etc
let's say make tests i think some of the this might break
but we will see now okay one failed already
okay so one failed and the one that failed
is test input ods okay so the audience failed and it says uh
missing optional dependency or dfpi okay so basically
what this is saying is that if we want support for all the s
we need to install this this library so
i think ods is not like a very main file since we can always have like uh
excel or like csv so any of the applications that support all the social
support csv and we don't want to introduce just
dependencies just because so i think it's better that we just
remove support for ods in our file in our library
in our comment so we remove the ods from here and then we also don't need
this one probably and we don't need to leave the test here
actually let's just remove this test and we just have this command here to
document so that later if we later wonder why we didn't add it
so we can see here and remember so let's say
these like this exactly and then we have like the
xslx okay then for a second because we know all these tests pass
so let's just comment them out so we don't have to wait
a few minutes now and we will keep checking the new ones okay
so we removed the ods support this means we need to come here somewhere
here there's an error and remove odds right accepted input files
r and here we can remove a few um so basically here
like csv and xlsx so i just keep it like this for now
because these two files should cover most of
them of the use cases um and auds is not supported
okay csb and xlsx for extensions let's see if
there's anymore yeah these were not using anymore
input file okay yeah so this we can just remove
also just checking this fix
and then here we got the input extension
um [Music]
read csv okay okay so this should make it so
let's see okay so these two run you know because
we just run them and we wanted to do also the the google
docs right now they're only missing one so
let's let's see them in that let's see the option for google docs
what's called ah there's no specific option so it's
just if we pass a google docs perfect um true so we just need to add that
google docs as an input and it will download it so actually the
test looks exactly the same as the other ones perfect
so we can just run these last three tests and then we'll uncomment the rest
and then we have already a pretty strong test suit so let's see
it's gonna take not too long because it's only three tests but
it's downloading the the file from google docs so
that part can take a bit a bit more perfect so
all three tests run well so let's uncomment all the other tests
and now we got all these tests that are actually checking out our app
we can trigger it just by passing this run tests
and there's only one thing here that uh i'm not sure
i don't know if it's good that we have this empty string is a bit ugly
but maybe for now we can leave it but at some point we want to get rid of it
because it's uh there's really no meaning in this
there's no input file so we just put the run test but maybe we'll deal
with it later but for now we have all the tests
and uh and some coverage of our program already
so let's commit this and continue basically
we want to add this and we're going to commit like
tests for and it was like size graph type
and input format and there was a more
more input format graph types sizes
exactly an output was already there okay so it's already good
perfect and then we're gonna go to master
master and rematch test implementation
Clone the repository
git clone https://github.com/fromzerotofullstack/sheet2graph
and get the episode branch
git checkout tests_implementation
Episodes
Ep.1 - Introduction, repo features and environment
(17 minutes)
We explain how the project will be structured and what we are trying to accomplish.
• Settings up the project
• Configuring a virtual environment
• Making a simple command that outputs a string
Ep.2 - Loading csv files
(8 minutes)
We create a Virtual environment and setup the project.
• Organizing commands with a Makefile
• Creating our Virtualenv
• Installing packages
• Using requirements.txt
• git branching
Ep.3 - First graphs
(19 minutes)
We save out first graph from the spreadsheet data.
• Installing Plotly to generate graphs
• Pip freeze requirements
• Solving dependency problems
• Using Pandas to do simple data processing
Ep.4 - Saving in a folder
(5 minutes)
We will create a folder to save our image to.
• Saving the image in a folder programmatically
• Using Pathlib
• Add folder to .gitignore
Ep.5 - First commandline options
(19 minutes)
We will add our first command line flags/options.
• Using argparse to parse commandline arguments and generate help
• Optional and required flags
• Add input file and graph type options
• Graph different graph types
• autogenerated help
Ep.6 - Output options
(22 minutes)
Here we add an option for different output locations.
• Using argparse to parse commandline arguments and generate help
• Combine options to output with a filename and with a folder
• Precendence of command flags
Ep.7 - Output format
(13 minutes)
We add Scalable Vector Graphics (SVG) output support.
• Add several output formats (svg, jpg, png) to our command
Ep.8 - Generated image size
(6 minutes)
We will add output options to specify the size of the generated graphs.
• New option for output size
• Default and custom graph sizes
Ep.9 - Refactoring and type checking
(22 minutes)
We start refactoring what we have until now, and add type annotations.
• Add basic annotations to functions
• Use Typeguard to enforce the annotations at runtime
• Annotations as a kind of documentation
• Add a new type of graph: the scatter graph
Ep.10 - Reading excel files
(16 minutes)
We will add support for Excel files (.xlsx).
• add dependencies: Openpyxl and Xlrd for Excel support
• Fix type errors
Ep.11 - Reading a file from Google Drive
(15 minutes)
In addition to a local file (.csv, .xlsx), we accept a Google Drive public document as input.
• Parsing the url address of the Google Drive document
• Adding the input option transparently for the user
Ep.12 - First tests
(16 minutes)
We will setup testing in our project to check the features we implemented.
• The unittest module in Python
• Add a test target to the Makefile
• Test loaders
• Assertions in testing
• Adding tests cache to .gitignore
Ep.13 - Testing helpers
(16 minutes)
Once we have the infrastructure for testing, we will write a few helpers to make the tests more concise and easier to write.
• Setup and teardown in a test suite
• Using Shutil to deal with filesystem operations
• Checking that a file is created in a specific path
• Checking the size of an image with PIL
• Running a command line application from a test
Ep.14 - Writing tests
(32 minutes)
After the testing infrastructure and helpers are ready, we are writing the tests for our commandline application.
• Testing input files
• Testing location of output files
• Testing generated image sizes
• Testing graph types
Ep.15 - Print version
(24 minutes)
We will be making our command a bit friendlier, improving the default behaviour with informative messages for the user.
• New option to print help of command
• Print version of the command
• Default behaviour. No flags prints the version and exits
• Testing the output of our command with os.system and subprocess
Ep.16 - Print data
(33 minutes)
After using hardcoded column names, we will start making the spreadsheet processing generic. This way our command will work with any spreadsheet file. The first step is to print the data of our input file, so the user can preview it.
• Print-only option to print the input file provided by the user
• Transforming the data with Pandas to index it by letter and 1-based integer (as in spreadsheet applications like Excel)
• Adding tests for the new indexing by letters and integer for columns and rows
Ep.17 - Testing data selection
(1 hour 3 minutes)
For each axis, we will allow the user to use expressions like 'b4,b5,b6,b7' or 'B4:B7' to select cells or ranges to graph.
• Add options '-x' and '-y' to select the data to be graphed
• Making the expressions case-insensitive
• Implementing a comma separated selection option
• Implementing a range selection option
• Adding tests first and making them pass after implementation, as in Test-Driven-Development (TDD)
• Better and more informative user messages in case of error
• Verifying and fixing problems in our Pandas implementation
Ep.18 - Adding labels to graphs
(25 minutes)
We will check everything is working so far and add extra options to set the axis labels to a custom user-defined value.
• Debugging broken tests and making all tests pass after all our changes
• Adding an x label and y label options to our command, to specify the labels in the horizontal and vertical axis
• Debugging column types in pandas
• Using exceptions to deal with unreliable cases
Ep.19 - Distributing our command
(57 minutes)
We will start preparing our command for distribution in pypi for it to be installable with pip, and testing the distribution in a test environment.
• Finding a proper name for our command
• Folder structure and necessary files
• Changes in the entry point of our program
• Generating a good README file in Markdown
• Examples and documentation
• Choosing a license
• Dependencies and versioning
• Setup.cfg and setup.py
Ep.20 - Packaging and uploading
(20 minutes)
We have all the files. We will test the distribution at test.pypi.org before publishing it in the real repository. This way we will be able to fix any mistakes.
• How to test in the test.pypi.org environment
• Using Twine to distribute your module
• Creating a new account at testing (test.pypi.org)
• Secure tokens in .pypirc or interactively for testing
• Versioning increases with each code change
• Fixing errors and testing the new version in the test environment
• Problems with images as documentation in the README file
Ep.21 - Testing install
(19 minutes)
After testing at test.pypi.org, we will fix an error with the example image in the documentation and distribute our command 'sheet2graph' to the real production environemnt at pypi.org.
• Fixing error with images in README.md at test.pypi.org
• Creating a new account at production (pypi.org)
• New secure tokens in .pypirc or interactively for production
• Solving problems with secure tokens
Ep.22 - Uploading to production
(6 minutes)
In this episode we put everything together and install our own command by typing 'pip install sheet2graph'. We test it both in a virtual environment and globally. We also talk about what this means to distribute code you develop easily to the world, something that now should be a lot more approachable. As always, we need to be mindful of publishing useful and tested code, and in general to play well within the ecosystem.
If you made it here I hoped you liked it. Subscribe or Connect with me on Twitter for updates on fromzerotofullstack.
• Solving problems with secure tokens
• Installing our new command using pip
• Testing on a new virtual environment
• Etiquette of publishing your modules and libraries