Mocking Rust 🤪 and Testing 🧪

Sdílet
Vložit
  • čas přidán 8. 09. 2024

Komentáře • 80

  • @peter9477
    @peter9477 Před rokem +15

    Love the nonchalant making of a function representing a "long-running calculation that results in the answer 42", with no further explanation than that. ;-)

  • @calvinlucian387
    @calvinlucian387 Před 2 lety +35

    As a newbie to Rust and a strong supporter of TDD this is probably the best byte-sized intro to testing on CZcams today imo. Thanks a lot!

  • @GeekMasher
    @GeekMasher Před 2 lety +22

    Great video. One thing I might have added to this is Rusts documentation testing. This is an amazing feature of the language. You will know that the documentation you have written compiles and runs properly as tests in `cargo test`.

    • @codetothemoon
      @codetothemoon  Před rokem +7

      I actually haven't tried this, will check it out!

  • @0xccd
    @0xccd Před 2 lety +12

    Perfect timing. I've been struggling with mocks in Rust. Thanks!

    • @codetothemoon
      @codetothemoon  Před 2 lety +1

      Glad this video could help! Mocking in Rust is definitely a bit tricky.

  • @r31527
    @r31527 Před rokem +4

    You're awesome! I've been procrastinating reading the Rust testing docs & you taught me everything I need to get started :)

  • @tosindaudu6595
    @tosindaudu6595 Před 2 lety +7

    Please do refcell and cell next and soon. Thanks!

  • @ClearerThanMud
    @ClearerThanMud Před 5 měsíci

    Really like your Rust videos! Just wanted to say that I prefer to avoid mocking when possible by breaking a function into two parts: 1) a pure function that figures out what needs to be done and returns a description of that activity, and 2) an interpreter for that description. The interpreter will be straightforward and can often just be left for integration testing. All of the gnarly logic will be in the the pure function, which is now far easier to test: given these inputs, you should say that this needs to be done.

  • @oliverye5833
    @oliverye5833 Před rokem +2

    If I use mockall, it requires mockall be a part of the dependency in the library. The example here wouldn't work if it's in the dev dependency. (Not sure if it's problem because I thought mock should be dev dependency as it's not related to actual context i.e api call)

    • @mikerodent3164
      @mikerodent3164 Před 6 měsíci

      Puzzled Rust mocking newb here: I've just asked the same question one year later. Did you get an answer to this? i.e. how to not include that mockall crate when compiling?

  • @zsoltpasztori8770
    @zsoltpasztori8770 Před rokem

    Ignore can also be used if the integration test needs some hardware to pass. For example, the library is used to calculate average temperature, and you want to test it with an actual sensor.

  • @andythedishwasher1117
    @andythedishwasher1117 Před rokem +2

    Super useful breakdown! This will be an essential skill set as I dive into this idea I recently had. I'm trying to determine the potential advantages, if any, of using Rust to indirectly write Javascript via some kind of translation macro for the purposes of maintaining memory safety in the resulting Javascript. This would be different from what frameworks like Yew are doing (which is definitely awesome) by bundling Rust logic into beautiful web assembly that works reluctantly alongside JS like an incompetent older brother who just happened to inherit the keys to the workshop. We wouldn't get the same kind of performance advantages, but we would still benefit from the memory safety checks quite a bit. I discovered that effect when I was very quickly able to rebuild my calculator app from Yew to Vue 3 with minimal debugging and a lot less code.

    • @codetothemoon
      @codetothemoon  Před rokem +1

      Thanks Andy! That would be an interesting undertaking. I wonder if the brevity of the Vue version is more due to the Vue framework than JavaScript itself. Not sure. Personally when I build with Yew I don't find myself missing JS or React, but I do find myself missing Svelte 🙃

    • @andythedishwasher1117
      @andythedishwasher1117 Před rokem

      @@codetothemoon Svelte is the big one I plan to explore next before I land on a target framework. Working on a rebuild of my HQ domain with Vue to assess the resulting code base, then probably working on a rebuild and/or additional experiment in Svelte depending on how the Vue build goes. Lots of work ahead!

  • @Mustafa-099
    @Mustafa-099 Před rokem

    This is Gold content on youtube!!! Loving every bit of it :)

  • @mikerodent3164
    @mikerodent3164 Před 6 měsíci +2

    Thanks, this was useful. The "mockall" crate seems to be added to "dependencies". But surely that crate shouldn't be included when you compile. If you add it to "dev-dependencies", however, you can't then include "[automock]" in the non-test code. How does this aspect work?

  • @phenanrithe
    @phenanrithe Před rokem +1

    More and more often I find that hosting the unit tests in the same file as the source code is distracting and inconvenient without real benefit, so I'm mostly keeping them in a tests-rs file which is technically the same. Just prepend the file with "#![cfg(test)]". It's easier to switch files than lines in the same file in the test/code design loop, and the tests-rs file is like a detailed spec/example for the whole crate / submodule (or tests_ if it becomes too big). Unless there aren't many tests of course.

    • @phenanrithe
      @phenanrithe Před rokem

      (I have to write test-rs instead of test DOT rs otherwise silly CZcams creates a link, which likely hides the comment as spam...)

    • @dealloc
      @dealloc Před rokem

      I have never seen an example of changing a test file out being an actual use-case. Often because tests should be complete as a whole. Co-location makes it easier to find tests for the corresponding implementations imo.
      The only reason why I'd move unit tests to a separate file would be if the language didn't allow me to have it in the same place.
      If your tests become too big, that is also a good indication that your implementation likely does more than it needs. It makes it easier to discover where separation of concerns could be applied.
      But each to their own, as long as it's consistent I don't see any problem with either way of doing it.

    • @phenanrithe
      @phenanrithe Před rokem +1

      @@dealloc I think it's a question of preference indeed, I just don't like to mix source code with test code. There's just no benefit, only inconvenience unless it's a very small file with very few tests.

  • @necauqua
    @necauqua Před 2 lety +5

    There is one more interesting test binary flag, --nocapture.
    And in general, you didn't mention how the stdout is captured and printed only if the test fails - to keep successful test runs tidy

    • @codetothemoon
      @codetothemoon  Před rokem

      re: nocapture, I actually didn't know about this, thanks for pointing it out. and you're right, I should have mentioned that stdout is for successful tests.

  • @flwi
    @flwi Před rokem +1

    Thanks for this helpful video! This will take some time to get used to. I work mostly in scala and just started learning rust. Mixing code and tests in a single file just feels wrong to me so far.

    • @codetothemoon
      @codetothemoon  Před rokem +1

      Cool - I personally think Scala is a fantastic language - sometimes I wish there was some way to get Rust's performance with a more Scala-like syntax. And yeah, putting tests and code in the same file feels a little weird to me still. But I think there are advantages, like not needing to maintain a test file / directory structure that is essentially a duplicate of the one that already exists for your code.

  • @davidraymond8994
    @davidraymond8994 Před 2 lety +4

    Awesome Rust videos. I am a newbie to Rust and am learning a lot from you thanks so much. Do you have a course offering or anything like that?

    • @codetothemoon
      @codetothemoon  Před rokem +2

      Thanks David! I don't currently have a course offering (I'd love to have one though!) but I've seen many on Udemy and other online learning sites. Not sure which one is best, maybe ask in r/rust! Also Let's Get Rusty offers a course as well but I haven't tried it.

  • @Marcos-tl2vy
    @Marcos-tl2vy Před 9 měsíci

    Thanks!!!!🎉🎉❤

  • @jhiver1978
    @jhiver1978 Před rokem +1

    Another flag worthwhile mentioning IMHO would be cargo test -- --nocapture to occasionally output debug info

    • @codetothemoon
      @codetothemoon  Před rokem

      good point, probably should have included that!

  • @adicide9070
    @adicide9070 Před 9 měsíci

    right so for somehting like this we'd definitely use a manual mock.

  • @masiarek
    @masiarek Před 2 lety +1

    Thank you!

  • @mdshohidurrahman1121
    @mdshohidurrahman1121 Před 2 lety +3

    Brain friendly,as always.

    • @codetothemoon
      @codetothemoon  Před 2 lety +1

      🧠 nice, brain friendliness is a big priority!

  • @mareknetzel
    @mareknetzel Před 2 lety +1

    Your videos are perfect!

    • @codetothemoon
      @codetothemoon  Před 2 lety

      Thanks Marek, very happy you find them valuable!

  • @irlshrek
    @irlshrek Před rokem

    I use --nocapture to print stuff in the tests!

  • @noblenetdk
    @noblenetdk Před rokem +1

    How about the cargo plugin Nexttest? As alwas great explanation

    • @codetothemoon
      @codetothemoon  Před rokem

      Never heard of it, I've put it on the list of things to check out!

  • @mufty9006
    @mufty9006 Před 2 lety +2

    I always have to change my playback speed back to Normal. Can't afford to miss a word

    • @codetothemoon
      @codetothemoon  Před 2 lety

      Nice, I try to avoid any fluff!

    • @theroboman727
      @theroboman727 Před 2 lety +1

      There is a browser extension I use called enhancer for youtube, where one of the features is the ability to change playback speed in 0.1x increments (can be configured) using ctrl+scroll wheel. Thought id mention this if you dont have anything similar already.

  • @SArthur221
    @SArthur221 Před rokem

    11:12 you said we were going to test the get_answer() function, but what seems to have happened is the exact opposite: that BigComputer's implementation gets called the way we prescribe...

    • @jordanwhittle8713
      @jordanwhittle8713 Před rokem +1

      I could be wrong but I think the idea behind the ‘times’ part is to specify the way that get_answer USES BigComputer
      Imagine if BigComputer was actually a database and compute_answer created a record in the database. With times(1), you would be able to specify that get_answer only calls compute_answer once to create 1 record in the database instead of calling compute_answer 1000 times and creating 1000 records. You’re not testing the mocked object, just specifying how it must be used.

    • @SArthur221
      @SArthur221 Před rokem

      @@jordanwhittle8713 good point

  •  Před 9 měsíci

    mockall should be in the devDependencies though right ?

  • @spoq9637
    @spoq9637 Před rokem +1

    Hi, I’m qa automation.
    can i use rust for api tests?
    Thx!

    • @codetothemoon
      @codetothemoon  Před rokem +1

      I don't think there's anything preventing you from doing this! Whether it's the best choice for such a task, I'm not sure.

  • @kentheHar
    @kentheHar Před 2 lety +2

    Do the mocks end up in the normal binary or only the test binary?

    • @codetothemoon
      @codetothemoon  Před rokem +1

      Good question I'm actually not 100% sure about this, but my hope is that they would be pruned out because they are not used. I'd be surprised if they weren't.

  • @NexusGamingRadical
    @NexusGamingRadical Před rokem

    So its impossible to mock structs without changes your code or the dependacy? :(

  • @someon3
    @someon3 Před 2 lety +1

    What are the real benefits of doing mock than construct the object directly?

    • @codetothemoon
      @codetothemoon  Před rokem

      Because constructing the object directly would make the test dependent on the behavior of that object - in unit tests we want to test a specific component in isolation, ideally without being affected by that component's dependencies and transitive dependencies. This is a bit easier to see when the component being tested has 10s or 100s of transitive dependencies.

    • @harleyspeedthrust4013
      @harleyspeedthrust4013 Před rokem

      @@codetothemoon But if the thing being tested can't be isolated in real code because it depends on the thing we're mocking, then how do we expect to be able to test it in isolation? Doesn't that defeat the point of tests - to ensure that our code works when it runs in production?

    • @TehKarmalizer
      @TehKarmalizer Před rokem

      @@harleyspeedthrust4013 that’s the argument. That when you test mocks, all you test is your ability to produce mocks. There are other types of testing, though. Unit testing is intended to test low level behavior.

  • @aazzrwadrf
    @aazzrwadrf Před rokem +1

    what about logging?

    • @codetothemoon
      @codetothemoon  Před rokem

      specifically in a testing context or in general?

  • @enokoner
    @enokoner Před 6 měsíci

    good luck using mockall for nontrivial cases

  • @mikechickenman
    @mikechickenman Před rokem

    Don’t forget `cargo test - -nocapture`

  • @samhanna7382
    @samhanna7382 Před 2 lety +1

    is that the default vs code dark theme ?

  • @vanshkoul1200
    @vanshkoul1200 Před 11 dny +1

    look at me!!

  • @awnion
    @awnion Před rokem

    That's all great until you actually need something like async_trait + trait inheritance 🤣

  • @dr.maninderjitkaur7805
    @dr.maninderjitkaur7805 Před 2 lety +2

    I am too sober for tNice tutorials, I'll be back later..

  • @JosephDalrymple
    @JosephDalrymple Před rokem

    It's all fun and games until you try and reference a trait in your struct so that you can mock it's _async methods_.
    EDIT emphasized. Should've been more specific in the beginning. 😅

    • @dealloc
      @dealloc Před rokem

      This is the same as get_answer, but `impl` abstracts away what is really going on. The expanded version of using `impl` is this:
      ```
      fn get_answer(computer: &C, question: i32) -> String where C: BigComputer {
      // ...
      }
      ```
      With structs it would be the same format:
      ```
      struct GetAnswerMachine where C: BigComputer {
      computer: C;
      }
      ```
      You can also use the short-hand syntax:
      ```
      struct GetAnswerMachine {
      computer: C;
      }
      ```
      This is called dependency injection, because it allows you to switch out what "C" is, as long as it implements the "BigComputer" trait.
      If GetAnswer does not use the entire BigComputer, you can split up the BigComputer trait into smaller traits and compose them where needed. Smaller traits are easier to deal with and decreases the surface area, avoiding breaking changes that could affect all implementations that rely on large traits.

    • @JosephDalrymple
      @JosephDalrymple Před rokem

      @@dealloc Unless your struct uses async methods 😅

  • @jeffg4686
    @jeffg4686 Před rokem

    Mocking Rust is the only way to get through learning it.
    I have full contempt by now, believe me.
    Question regarding asserts outside of tests - just in main code - would you always use debug_assert there. I'm still trying to figure out best practices in rust. My thought is that assert! in regular code is likely a bad idea (since it panics instead of allowing you a change to deal with an error result), but that debug_assert!() is useful since it's only in debug mode, but then that's kinda cheating your way out of having real error handling. So maybe even debug_assert isn't that useful in regular code. Maybe just as a placeholder to say "write some error handling here later"

    • @dealloc
      @dealloc Před rokem

      Panicing is perfectly fine, mostly for internal implementations rather than user-facing APIs.
      It's better to panic and crash a program than ending up in a bad state that is unrecoverable and hard to debug. Assertions are not a replacement of error handling, but rather a compliment to it, by restricting the surface area of an implementation to avoid leaking internal details throughout the entire library/application, especially to user-facing APIs. Panics should be treated as bugs in the implementation. Rather than being a way to validate user input, it should be a way to validate input passed by the internal system.
      While you could pass Results everywhere in place of assertions but it makes it harder to avoid breaking changes in the code base and often easily leaks into user-land, where there is no way for the user to recover.
      Note that "user" in this case can be a user of a library, or a service interfacing with other services or programs.

  • @fleeb
    @fleeb Před rokem +1

    Ooooh... by 'Mocking Rust', you mean using mocks to imitate real objects. I thought this video was going to make fun of the language.

    • @codetothemoon
      @codetothemoon  Před rokem +1

      yeah, it was a somewhat lame play on words 🙃

  • @hiongun
    @hiongun Před rokem +1

    Why everything becomes dead complicated when it comes to Rust?
    Boy, it's just testing. Do you really think this is 'simple' ?

    • @codetothemoon
      @codetothemoon  Před rokem

      I don't think everything is complicated when it comes to Rust. But I think what you need to do to get mocking working is pretty awful. My gut feeling is that this complexity is due to the lack of support for inheritance, but I'm not 100% sure. In any case I hope the necessary language features get implemented that allow mocking to be a bit more concise.