💡 Get my FREE 7-step guide to help you consistently design great software: arjancodes.com/designguide.
@maephisto2 жыл бұрын
I really admire the way you explain all these concepts. Even the quality of the video itself should not be underestimated: from the way you zoom the source code to the transitions from VS Code to yourself, it is really clear to me how much time and passion you put into this.
@ademhilmibozkurt708522 күн бұрын
Your videos rely on real life examples and I love that. Keep going Arjan!!!
@XxNGameCubexX Жыл бұрын
Phenomenal video about software testing, in python and in general. Also, an amazing demonstration of how unit testing can improve your code design. Subscribed!
@nickeldan2 жыл бұрын
Great series on testing! I would love it if you would do a video or two going through the deeper features of pytest.
@kevinlao36902 жыл бұрын
Same here! I would like to see Arjan delve further into unit testing even more
@roffel06 Жыл бұрын
This two-parter is a great tutorial. Thanks for talking the time and effort to provide us with this content. It's really high quality. I'll have to rewatch the second video another time. Around the 8:00 minute mark I had a light bulb moment how I can turn one of my helper functions into a class and instantiate the object as needed. Not only will this improved my unit test, this was actually a helper function with important functionality that I may change depending on the business need. I can see how i may define different classes based on different use cases. Thanks for that. The video after the 8:00 minute mark was too quick and too dense for me to follow. I'll have to sit down and rewatch what you actually did there. I know this was originally intended as one video but i think you could have spread out the content over 4 videos easily, essentially adding 2 separate videos about refactoring your code. I would have watched those, too! 😂
@MoritzBurmester3 ай бұрын
Great work man. Thanks for sharing your expertise in such high quality videos. Pleasure to watch. :)
@ArjanCodes3 ай бұрын
Glad you enjoy it!
@jambosuss2 жыл бұрын
This was excellent! Thanks for this, I think i'll be watching both videos many times and studying your refactored code so I can properly implement unit tests into my own projects
@neoluis2003 Жыл бұрын
Awesome! Invaluable learning resource. Thank you Arjan!!!
@ArjanCodes Жыл бұрын
Glad you liked it!
@bradgeesaman4 ай бұрын
Thanks!
@ArjanCodes4 ай бұрын
You’re welcome, glad you enjoyed it!
@racrity3 ай бұрын
Damn Arjan you really helped my out a lot! Thanks man! I wish i knew about Unit tests BEFORE starting to write my code lol
@ArjanCodes3 ай бұрын
You're welcome! :)
@mykolasdudutis53196 ай бұрын
I'm curious about the test cases for payment.py. Shouldn't we test if the payment processor charge method was called when testing for paying the order? Since "test_pay_order" would pass even if we remove line 17 "payment_processor.charge(card, amount=order.total)" in payment.py. Similarly, shouldn't we test if the order status wasn't changed if ValueError was raised?
@BhaktiiKiShaktii9 ай бұрын
I am getting deeper understanding about all the core concepts of python for the software development, Great ability to teach complex logic with very ease, Thanks 🙏🏻
@ArjanCodes9 ай бұрын
Glad to hear the content is helpful!
@BhaktiiKiShaktii9 ай бұрын
@@ArjanCodes extensively helpful, in our project we are moving forward with the SOLID principle and you videos helps me to understand everything related it❤️, Thanks
@skl99422 жыл бұрын
Thanks!
@ArjanCodes2 жыл бұрын
Thank you Samuel!
@kpuano2 жыл бұрын
I understand how dependency injection (removing the responsability of creating the dependent objects inside a function/method) makes the tests easier, since it allows passing mocks or stubs directly instead of monkey patching the actual implementation. However, the objects creation was moved to the main function, since they have to be created somewhere. Shouldn't we test it? Would that be an integration test? How does it differ from the unit tests? I always struggle with this because I still end up having to monkeypatch stuff to test the complete flow.
@garrywreck42912 жыл бұрын
Usually, you don't test "main" at all because you should keep it super simple. But you could add a few integration tests if you still think your "main" is not simple enough ;)
@vikingthedude2 жыл бұрын
I think the main function here would be what's called the "composition root". Its job is supposed to be the place where you "wire up" your app's dependency tree, so the code within it should as simple as possible. This usually means its not worth testing. But it depends on your specific situation
@apoca1ypse12 жыл бұрын
This is also something I don't understand. Most applications involve the use of many side effects. Moving all side effects to a main function and not testing them does not seem like a realistic solution. Are there any good resources out there that tackle this question?
@vikingthedude2 жыл бұрын
@@apoca1ypse1 to unit test side effects, you don’t actually test the effects themselves. You just test to make sure that the side effect function was called correctly. You mock the side effect function and run assertions like “mock.wasCalledWithArguments(expected)”
@apoca1ypse12 жыл бұрын
@@vikingthedude I understand that you can mock side effects to test your code. Dependency injection looks to me like a method of moving where you mock rather than how to avoid mocking all together. If my understanding is correct then how do you decide where the best place for mocking is?
@naoraspir4896 Жыл бұрын
You are BRILLIANT , thank you for the 2 part videos!
@Pawl0solidus2 жыл бұрын
Amazing video! The last part about env variables was really great because I needed it for a long time and was only defining env variables in terminal using export. Thank you very much for your help!
@ArjanCodes2 жыл бұрын
Thanks so much Paulo, glad it was helpful!
@mikeciul85992 жыл бұрын
I asked this last week, but I still want to know - what are your thoughts on unittest vs pytest? Who here uses which one, and why do you prefer it to the other? I use both together sometimes, but I worry that this could lead to trouble. What do you think?
@garrywreck42912 жыл бұрын
I'll share my experience, maybe it will be useful. I started with pytest but then switched to unittest because grouping related things under a TestCase seemed very nice and mainainable. But I still used "assert" and "pytest.raises" because they are prettier ;) but then I discovered that when a project grows, TestCases become in some way noisy. Also, you will always find out that, for example, "self" is not used (i.e. it could be a simple function). The next step for me was to return back to plain functions. I replaced TestCases by more complex packages/modules hiararchy and in the end it seems much better. About pytest fixtures... I tried them hard but in the end I abandoned it. I find them not flexible enough because they don't accept parameters (ofc you can make them to accept parameters but it looks hacky). So I use helper functions usually stored under tests/utils package. The only thing I use fixtures for is setup/teardown. And almost always those are fixtures with "session" scope and autouse=True located under conftest py. For example, loading of dummy SQL to a database.
@sulfur320662 жыл бұрын
Pytest is much bigger, have more features to use, and good solution as well, has good documentation and support. Unittest Library is kind of local or small story and pytest is a complete solution for really big and complex things
@sulfur320662 жыл бұрын
And of course, even into pytest documentation you could find some things from unittest like patch for example
@mikeciul85992 жыл бұрын
@@sulfur32066 That makes me think I'm missing out on a lot of what pytest can do. I like unittest `self.assertEqual`, `assertIn`, `assertRaises`, and 'assertLogs`... I'm not sure I know the idiomatic way to do all of those things in pytest, and I'm not sure what kind of context it gives when assertions fail - pytest users, do you ever test for log statements? Is there a pytest way? I also like using `Mock.assert_called_with`. Since I started watching this channel, I've been replacing patching with dependency injection. But I still want to assert that the right calls were made, so I'm still using unittest.Mock... what are your thoughts?
@sulfur320662 жыл бұрын
@@mikeciul8599 for such cases I personally prefer to use patch from unittest.mock inside pytests, this method allows you to patch specific method call and return whatever you want as a result and you could use built-in things like .call_count, .call_args etc on patched method
@abdelghafourfid82162 жыл бұрын
Great video as always, a side note is that I always wonder what are the shortcuts you keep using on your videos when coding, it really makes your coding looks very smooth and easy, I hope you could cover that in a future video
@briancline02 жыл бұрын
They sound almost identical to vim shortcuts/keybinds (especially copy/paste for instance -- sounds like he's using the `yy` keybind to copy a line, then `p` to paste it after current line). Most IDEs with a plugin ecosystem have a vim keybindings plugin, so give that a shot. They can take a while to learn, but once you get used to them they really help do things quickly.
@sparrowhawkguitarboy2 жыл бұрын
Amazing video. So much to take away and put into practice in my current projects. Thank you very much. I’ve watched several videos about TDD, and this if by far the most useful one, I’ve spent my time watching. Thank you very much!
@ArjanCodes2 жыл бұрын
Thanks, happy that it’s helpful to you!
@dirkvandam97452 жыл бұрын
@arjan love your videos they are both informative and fun and have helped me a lot. I’m currently working on writing unit tests and am using pytest. I was wondering what the advantage was of using a pytest fixture as opposed to just defining a constant with the object at the top of the unit test script?
@ArjanCodes2 жыл бұрын
Hi Dirk, glad to hear you like the videos! If you use a fixture, a fresh, new object is created for each test. This is better than using a constant, which creates an object only once, when loading the script. You don't want that one test accidentally influences another test by changing the object that both tests rely on.
2 жыл бұрын
Excellent question Dirk, I was wondering the exact same thing. Thanks for the answer Arjan!
@Shivnaren2 жыл бұрын
@@ArjanCodes Thanks a lot for the videos, Arjan-I've been learning tons. One thing I like to do sometimes is define a _function_ that returns a fresh object--almost like a constructor. Similar to the current setup in the video, just without the decorator. Works just as well as a pytest fixture, but is a bit more explicit (in terms of linting support), I feel.
@lucasventura24072 жыл бұрын
Great Video! Wonderful way to explain, it helped me a lot!
@ArjanCodes2 жыл бұрын
Thank you Lucas, glad you liked the video and it was helpful!
@NZ2552 жыл бұрын
Would the validate card and luhn functions be better placed in the CreditCard class? As they’re credit card specific? Thanks very much for the great videos!
@ArjanCodes2 жыл бұрын
Yes, good suggestion, that makes more sense than leaving it with the payment processor as it is now.
@rouhollahabolhasani18532 ай бұрын
Great tutorial. Thanks
@ArjanCodes2 ай бұрын
Glad it was helpful!
@dnlgrhm Жыл бұрын
As usual, wonderful video and much appreciated!
@ArjanCodes Жыл бұрын
Thank you, glad you enjoyed the content!
@owenlu6527 Жыл бұрын
This man is a magician
@mutsukira2 жыл бұрын
I really enjoy watching your great videos. Thank you.
@ArjanCodes2 жыл бұрын
Thanks Abdelkarim, happy you’re enjoying the content!
@KLM11072 жыл бұрын
Really informative video! One thing I do for sensitive variables like API keys is to use the keyring module, that way you can avoid any extra files or plaintext storage of secrets. Another thought, would it not make sense to validate the credit card number as part of the dataclass?
@aflous2 жыл бұрын
Totally makes sense to me, you can have a property checking the card validity inside the dataclass
@mrswats2 жыл бұрын
For dates one package I love is freezegun (and there is the appropriate pytest plugin for it as well) that it allows you to monkeypatch the today date so you do not ever have to worry about dates anymore. And, another thing, I would argue that hardcoding API keys for testing is fine (given that they are not real api keys) and that you mock the requests to the API itself so you also solve a whole lot of problems with that part specifically. But, with good design, it should be possible to sort that out easily.
@ArjanCodes2 жыл бұрын
Thanks for the tip! And indeed, you won't always need an API key directly in the testing code (I did this in the example as well, using the PaymentProcessorMock).
@mrswats2 жыл бұрын
@@ArjanCodes yeah, yeah, for the video makes sense to do so
@mikeciul85992 жыл бұрын
I love freezegun, thanks for posting!
@rayu5262 жыл бұрын
Great video! I'm just binge watching all of them :) Note: I would consider inverting also dependency to datetime.now(). Thanks to that you can mock in your tests what day is today and test i.e. how PaymentProcessor would behave on Feb 29th or Dec 31st
@Formulka2 жыл бұрын
This is an excellent example, I would just stress how important is to keep in mind the actual functionality of the software, as presented the tests all pass but the main function includes original and non-functional call to pay_order that is missing arguments.
@Phaust942 жыл бұрын
Great stuff man, always helpful. What's up with those 82% coverage at 16:51 tho?
@oleandreasramsdal56192 жыл бұрын
IIRC there were two test-methods with same name in the last video.
@DeathBean892 жыл бұрын
If you go back to Part 1 @9:30, you can see that he has two test methods with the same name in test_order.py. The one that is defined first is not being run, since the second one is overwriting it in the namespace. As a result, 3 lines of test code are not being run and are counted as misses in the report.
@BelgranoK2 жыл бұрын
Great! I worked a lot with dates and timestamps. I suggest to inject time to functions instead of give them the responsability of getting the right time data. Thats makes code more testable and eiser to reuse.
@ArjanCodes2 жыл бұрын
Good suggestion! You could use a fixture for this that delivers the current date/time. Or do you use a separate package for this?
@BelgranoK2 жыл бұрын
@@ArjanCodes I do not use fixture because function depends heavily on current date/time. It is important to test the behavior for limit values like last day of the month, last day of the year, leap years, etc. In some cases we implemented a function to provide the current time in order to ease mocking for integration tests.
@Shivnaren2 жыл бұрын
At 15:40, the line `API_KEY = os.getenv("API_KEY") or ""` New to me, and pretty cool. Is there documentation for this, or some resource explaining it?
@marconeramos11949 ай бұрын
Great content! Thank's a lot.
@ArjanCodes9 ай бұрын
Thank you so much!
@AdoniasAlbuquerqueEng Жыл бұрын
Very good and helpful, thanks a lot a little feedback, its kind harder to focus on the code if you zooming on your face like that, would be better to have the code on the screen all the time.
@ЕгорПарамонов-б9о2 жыл бұрын
For .env you can also use python-decouple.
@ArjanCodes2 жыл бұрын
Thanks for sharing that, looks nice!
@olivierserve1522 жыл бұрын
Excellent video as usual ! A little thought : Sometimes I feel like dataclasses without any methods could really be named tuples. That seems especially the case for the creditCard class, as it would be realistic that the values (number, month, year) should never change once the card is initialized.
@DeathBean892 жыл бұрын
You're right, the CreditCard class here could just be a named tuple. There isn't really much difference between a named tuple and a basic dataclass in practice, since both are meant to be simple wrappers for data. I personally prefer the dataclass approach, as I have always just felt that tuples are objects that were poorly defined. Plus, if you use a dataclass then you can easily add things like equality checking and ordering, without needing to define a separate function to handle that behavior correctly. You can just add the appropriate dunder methods to the class so it's all in the same spot.
@olivierserve1522 жыл бұрын
Hi,@@DeathBean89 thanks for confirming the feeling. I think I understand your point of view. I guess that choosing data class over named tuple allows more flexibility in later developments and if required it is always possible to have it frozen (and make it hashable).
@AvihayBar2 жыл бұрын
Love your content! I know it's off topic, but I like you better in a T-shirt. you just feel more comfortable that way. (congrats on the new studio, btw!)
@ArjanCodes2 жыл бұрын
Haha, yes I went fancy for the first batch of videos in my new studio. I'm going back to t-shirts and hoodies for the next batch ;).
@nraw_6 ай бұрын
You mentioned in the previous video that you're testing your own code rather than 3rd party APIs. But in general, I kind of need to do both? There are times where I want to abstract that away and there are times where I'd like to have it. What's the best way to represent this with code?
@nateriver82612 жыл бұрын
Hey! Can you make a video about proxy and decorator patterns?
@younesveisi2 жыл бұрын
Hi. I want to say that I really got a lot more information and views about testing and software, and I was able to program with a better and more accurate view through the 7 steps you mentioned for software design. But I have a request: if it is possible for you to extend this project to "bdd in Python" so that we can make more use of this knowledge Thank you very much, Younes
@dirkvandam97452 жыл бұрын
@arjan, thanks for replying to my previous comment, appreciate it and using it successfully. I had another question, if you have the time. Again on the subject of unit tests :) Described simply I have a class that contains a data cube (3-D) that contains methods used for plotting different 2-D slices of the cube (index and axis, and also a scrollable interactive window). I was wondering if there is an appropriate way to pytest figures/axes objects made by matplotlib. Would I have to monkey patch it / is there a good / standard way of asserting proper figures/axes objects?
@ugurozcan48862 жыл бұрын
Hello, Arjan. Thank you for amazing video. You explained most of things about the unit testing. I like your code refactor. I want to ask a question about this video. You used dotenv library to read API_KEY. Can we use .ini file and read this file with configparser library? Which method is more generic?
@ArjanCodes2 жыл бұрын
Hi Ugur, Sure, using an ini file with the key is also possible. You just have to make sure that the file is only placed in locations where it's secure. In systems deployed in the cloud, it's common practice to use environment variables for this. That's why I used a .env file in this example.
@maga3055 Жыл бұрын
An even better way to take care of the date issue: Inject a datetime object into the validate_card function. Then you can create unit tests with a fake date.
@MariusOdobasa Жыл бұрын
You are great!
@SuperMotaba2 жыл бұрын
Hey Aryan, when you create the PaymentProcessorMock class, aren’t you no longer testing the actual code? But a simplified version of the real class? If you ever change the actual class, won’t you have to change the mock class, thus defeating the purpose of having these tests?
@ArjanCodes2 жыл бұрын
Hi Johnny, the place where I use the PaymentProcessorMock class is in the code where I test the pay_order function. So I use the mock to make testing the original code (which is the pay_order function) a lot easier. As I mention near the end of the video: it’s important to always make sure that you’re testing the right thing at the right level. It’s really easy to make the mistake where you’re testing built in Python functionality, or as you write, you’re testing mock classes and functions instead of the actual code.
@mouttiedje47952 жыл бұрын
What would be the argument of using pytest vs unittest?
@evolagenda2 жыл бұрын
how do you get away with pay_order(order) when the function expects pay_order to have 2 positional args order and processor. I tried replicating this pattern in my code and the tests work because I can mock a processor or some function and pass it like pay_order(order, ProccessorMock()) but when I try to run the real code it fails saying I didn't pass a processor. Does that mean you have to import the real Processor function and pass that whenever you want to use pay_order()? So you only get rid of the import dependency for the test, the live code would still need the import?
@MrG8952 жыл бұрын
For when you refactor PaymentProcessor.luhn_checksum into a module-level method, what are your thoughts in refactoring it to a staticmethod of the class instead? Arguably the method is logically related to the class and you wouldn't need to create a new PaymentProcessor object to access/test it if it is static, but I could see where having it as a module function allows for more flexibility.
@JGnLAU8OAWF62 жыл бұрын
If anything it should be moved to CreditCard class, perhaps even as creation time validator.
@ting7949 Жыл бұрын
It’s amazing…
@sulfur320662 жыл бұрын
Great video! I guess using freeze_gun for datetime would be better than your trick with date, wdyt?)
@ArjanCodes2 жыл бұрын
Great suggestion, that's indeed a good, generic solution to dealing with dates in tests.
@tanjt1072 жыл бұрын
What about saving API keys in a separate json or yaml file?
@ArjanCodes2 жыл бұрын
Sure, that would work too. The most important thing is to make sure you don't commit the file to the repository.
@deemon7102 жыл бұрын
Does anyone know how to setup one's environment in WSL so pytest recognizes python 3.10 syntax such as the new match / case stuff? I'm having a heck of a time not finding a solution when I'm pretty sure one exists.
@marcotroster82472 жыл бұрын
Hey Arjan, did you miss out on the actual use case test for "making a payment via credit card"? I'm not so sure anymore if you added such a test for validating the whole thing 🤔😅
@Lars162 жыл бұрын
Wouldn't that be an integration test? These videos have focused on unit tests.
@troncooo4092 жыл бұрын
Idea for a next video? How can you be sure, that the refactored code is working as intended?
@marcotroster82472 жыл бұрын
@@Lars16 You can basically put as much code together as you like. If you're creating larger sized components where the payment is just a small submodule, it can be a legitimate "unit test" 😂 So I'd say don't tunnel too much on testing single classes, codefiles or functions. Rather test meaningful behavior that has a real-world use case behind it 😄 If you're just asserting that each line is doing what you've written in your implementation, your tests will be so closely coupled to the code that refactoring will be very painful 😅
@marcotroster82472 жыл бұрын
@@troncooo409 Haha yeah, that's always a good starting point 😂
@mikeciul85992 жыл бұрын
When I create an object to replace an injected dependency, I often call it a Fake. But I don't really know the difference between a fake and a mock. Can anyone tell me?
@ChrisMasto2 жыл бұрын
It depends on who you ask. I'd say most people don't care and use them interchangeably. But one way to classify them, for those who draw a distinction: mocks and stubs are two kinds of fakes: a stub is what was shown in this video, and what you're describing: a do-nothing object created to satisfy a dependency. Mocks, on the other hand, are used to test the other code's interaction with the object. For example, the PaymentProcessorMock here is really a stub because it doesn't contribute to testing anything. But if you had it keep track of when its charge() method is called, and at the end of the pay_order test you assert that charge() was called exactly once and with the correct arguments, then it would be functioning more like a mock.
@mikeciul85992 жыл бұрын
@@ChrisMasto Is that also called a spy?
@ChrisMasto2 жыл бұрын
@@mikeciul8599 I'm not familiar with that term. But a quick web search suggests that a spy is a wrapper around the original object to intercept or track its usage. Whereas a mock is more of a brand new object that fakes the behavior while keeping track of its usage.
@kamilbojdo20942 жыл бұрын
I am confused. Why lots of your tests are not asserting anything? Virtually you have 100% coverage, but with no asserts you never test if your pay_order function even calls any method of payment procesor. You can delete whole code that is interacting with payment procesor from the function, and your tests will not fail.
@ArjanCodes2 жыл бұрын
I don't understand what you mean. When I look at the tests in the repository, all of them have an assert statement in them, except for the ones where I'm checking that an action raises an error using pytest.raises (in which case adding an assert doesn't make sense). There's one test that doesn't have an assert or a pytest.raises, which is test_charge_card_valid. This one could be wrapped with a 'not pytest.raises' statement, but all the other tests seem fine to me.
@kamilbojdo20942 жыл бұрын
Ok. Maybe it is my fault. I looked only into the video, and on KZbin I don't see these asserts, but on repository they are added. Anyway, issue that I mention seems still existing. You can delete line 17 from payment.py file (charging of card) and tests will still not fail, but the card will not be charged.
@_noirja Жыл бұрын
Do you have to be a bit careful mocking certain parts of code in your tests? Could that not lead to tests that pass without actually testing the intended functionality? I guess the developer just has to think carefully.
@leaky39554 ай бұрын
Great points and I agree with your conclusion. To expand on thinking carefully; thinking specifically about what is being tested. Mocking should allow the test to be laser focused on validating that thing by abstracting the other parts that serve as dependencies for the thing to happen. I.E. When “this happens”, my code should react “this way”. I’m mocking to ensure “this happens” and leaving my test to focus on validating the reaction is “this way” as expected. This point also draws the distinction between unit tests and integration tests. A unit test’s purpose is to validate some code reacts as expected when faced with various scenarios, so I control the environment to mock the scenarios. Integration tests focus on the interaction between two components, to ensure if one has changed, you become aware how you have inadvertently broken the other so you’re prompted to fix things and probably also update unit tests. My newbie understanding, anyway.
@alvaroe27043 ай бұрын
@@leaky3955 Good approach I think. From a newbie, also