💡 Get my FREE 7-step guide to help you consistently design great software: arjancodes.com/designguide.
@maephisto
2 жыл бұрын
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.
@nickeldan
2 жыл бұрын
Great series on testing! I would love it if you would do a video or two going through the deeper features of pytest.
@kevinlao3690
2 жыл бұрын
Same here! I would like to see Arjan delve further into unit testing even more
@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!
@roffel06
8 ай бұрын
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! 😂
@jambosuss
2 жыл бұрын
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!
@BhaktiiKiShaktii
6 ай бұрын
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 🙏🏻
@ArjanCodes
6 ай бұрын
Glad to hear the content is helpful!
@BhaktiiKiShaktii
6 ай бұрын
@@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
@Pawl0solidus
2 жыл бұрын
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!
@ArjanCodes
2 жыл бұрын
Thanks so much Paulo, glad it was helpful!
@Formulka
2 жыл бұрын
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.
@rayu526
2 жыл бұрын
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
@mrswats
2 жыл бұрын
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.
@ArjanCodes
2 жыл бұрын
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).
@mrswats
2 жыл бұрын
@@ArjanCodes yeah, yeah, for the video makes sense to do so
@mikeciul8599
2 жыл бұрын
I love freezegun, thanks for posting!
@abdelghafourfid8216
2 жыл бұрын
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
@briancline0
2 жыл бұрын
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.
@naoraspir4896
Жыл бұрын
You are BRILLIANT , thank you for the 2 part videos!
@sparrowhawkguitarboy
2 жыл бұрын
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!
@ArjanCodes
2 жыл бұрын
Thanks, happy that it’s helpful to you!
@kpuano
2 жыл бұрын
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.
@garrywreck4291
2 жыл бұрын
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 ;)
@vikingthedude
2 жыл бұрын
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
@apoca1ypse1
2 жыл бұрын
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?
@vikingthedude
2 жыл бұрын
@@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)”
@apoca1ypse1
2 жыл бұрын
@@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?
@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.
@mikeciul8599
2 жыл бұрын
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?
@garrywreck4291
2 жыл бұрын
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.
@sulfur32066
2 жыл бұрын
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
@sulfur32066
2 жыл бұрын
And of course, even into pytest documentation you could find some things from unittest like patch for example
@mikeciul8599
2 жыл бұрын
@@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?
@sulfur32066
2 жыл бұрын
@@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
@mykolasdudutis5319
2 ай бұрын
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?
@marconeramos1194
6 ай бұрын
Great content! Thank's a lot.
@ArjanCodes
6 ай бұрын
Thank you so much!
@BelgranoK
2 жыл бұрын
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.
@ArjanCodes
2 жыл бұрын
Good suggestion! You could use a fixture for this that delivers the current date/time. Or do you use a separate package for this?
@BelgranoK
2 жыл бұрын
@@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.
@dirkvandam9745
2 жыл бұрын
@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?
@ArjanCodes
2 жыл бұрын
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!
@Shivnaren
2 жыл бұрын
@@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.
@KLM1107
2 жыл бұрын
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?
@aflous
2 жыл бұрын
Totally makes sense to me, you can have a property checking the card validity inside the dataclass
@owenlu6527
Жыл бұрын
This man is a magician
@user-we2pj7un4h
2 жыл бұрын
For .env you can also use python-decouple.
@ArjanCodes
2 жыл бұрын
Thanks for sharing that, looks nice!
@lucasventura2407
2 жыл бұрын
Great Video! Wonderful way to explain, it helped me a lot!
@ArjanCodes
2 жыл бұрын
Thank you Lucas, glad you liked the video and it was helpful!
@olivierserve152
2 жыл бұрын
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.
@DeathBean89
2 жыл бұрын
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.
@olivierserve152
2 жыл бұрын
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).
@bradgeesaman
Ай бұрын
Thanks!
@ArjanCodes
Ай бұрын
You’re welcome, glad you enjoyed it!
@dnlgrhm
10 ай бұрын
As usual, wonderful video and much appreciated!
@ArjanCodes
10 ай бұрын
Thank you, glad you enjoyed the content!
@mutsukira
Жыл бұрын
I really enjoy watching your great videos. Thank you.
@ArjanCodes
Жыл бұрын
Thanks Abdelkarim, happy you’re enjoying the content!
@younesveisi
2 жыл бұрын
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
@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.
@NZ255
2 жыл бұрын
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!
@ArjanCodes
2 жыл бұрын
Yes, good suggestion, that makes more sense than leaving it with the payment processor as it is now.
@nraw_
2 ай бұрын
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?
@AvihayBar
2 жыл бұрын
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!)
@ArjanCodes
2 жыл бұрын
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 ;).
@nateriver8261
2 жыл бұрын
Hey! Can you make a video about proxy and decorator patterns?
@Phaust94
2 жыл бұрын
Great stuff man, always helpful. What's up with those 82% coverage at 16:51 tho?
@oleandreasramsdal5619
2 жыл бұрын
IIRC there were two test-methods with same name in the last video.
@DeathBean89
2 жыл бұрын
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.
@ting7949
Жыл бұрын
It’s amazing…
@MariusOdobasa
Жыл бұрын
You are great!
@curtking70
2 жыл бұрын
Thanks!
@ArjanCodes
2 жыл бұрын
Thanks so much Curt, glad you liked it!
@ugurozcan4886
2 жыл бұрын
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?
@ArjanCodes
2 жыл бұрын
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.
@dirkvandam9745
2 жыл бұрын
@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?
@sulfur32066
2 жыл бұрын
Great video! I guess using freeze_gun for datetime would be better than your trick with date, wdyt?)
@ArjanCodes
2 жыл бұрын
Great suggestion, that's indeed a good, generic solution to dealing with dates in tests.
@mouttiedje4795
2 жыл бұрын
What would be the argument of using pytest vs unittest?
@Shivnaren
2 жыл бұрын
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?
@MrG895
2 жыл бұрын
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.
@JGnLAU8OAWF6
Жыл бұрын
If anything it should be moved to CreditCard class, perhaps even as creation time validator.
@SuperMotaba
2 жыл бұрын
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?
@ArjanCodes
2 жыл бұрын
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.
@deemon710
2 жыл бұрын
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.
@evolagenda
2 жыл бұрын
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?
@kamilbojdo2094
2 жыл бұрын
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.
@ArjanCodes
2 жыл бұрын
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.
@kamilbojdo2094
2 жыл бұрын
Ok. Maybe it is my fault. I looked only into the video, and on KZitem 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.
@tanjt107
2 жыл бұрын
What about saving API keys in a separate json or yaml file?
@ArjanCodes
2 жыл бұрын
Sure, that would work too. The most important thing is to make sure you don't commit the file to the repository.
@_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.
@leaky3955
27 күн бұрын
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.
@marcotroster8247
2 жыл бұрын
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 🤔😅
@Lars16
2 жыл бұрын
Wouldn't that be an integration test? These videos have focused on unit tests.
@troncooo409
2 жыл бұрын
Idea for a next video? How can you be sure, that the refactored code is working as intended?
@marcotroster8247
2 жыл бұрын
@@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 😅
@marcotroster8247
2 жыл бұрын
@@troncooo409 Haha yeah, that's always a good starting point 😂
@mikeciul8599
2 жыл бұрын
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?
@ChrisMasto
2 жыл бұрын
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.
@mikeciul8599
2 жыл бұрын
@@ChrisMasto Is that also called a spy?
@ChrisMasto
2 жыл бұрын
@@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.
Пікірлер: 111