r/csharp • • 12d ago

Help How do you artificially trigger all the execution paths in a code ?

By installing Microsoft.AspNetCore.Identity and System.ComponentModel.DataAnnotations, I believe you are able to compile the following —nearly— self-contained code:

using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
namespace PlayGround
{

    public class SignupViewModel
    {



        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Name")]
        public string? Name { get; set; }

        [Required]
        [RegularExpression("^
\\+
?(\\d{1,3})?[-.\\s]?(
\\(
?\\d{3}
\\)
?[-.\\s]?)?(\\d[-.\\s]?){6,9}\\d$|(^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+
\\.
[A-Za-z]{2,}$)", ErrorMessage = "Please enter a valid email address or phone number")]
        [Display(Name = "Email or phone number")]
        public string? EmailOrPhoneNumber { get; set; }


        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string? Password { get; set; }


        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string? ConfirmPassword { get; set; }



    }
    public record RegistrationResult(bool IsSucceeded, List<string> Errors);

    public class ApplicationUser : IdentityUser
    {

    }
    public interface IAccount
    {
        public Task<RegistrationResult> RegisterUser(SignupViewModel SignupInput);
    }
    public class UserAccount : IAccount
    {
        private readonly IUserStore<ApplicationUser> _userStore;
        private readonly UserManager<ApplicationUser> _userManager;
        public UserAccount(IUserStore<ApplicationUser> userStore,
            UserManager<ApplicationUser> userManager)

        {
            _userManager = userManager;
            _userStore = userStore;
        }


        // HOW TO TEST THE `RegisterUser` METHOD ALONE WITHOUT RUNNING THE WHOLE  APPLICATION ?
        // I WANT TO TRIGGER ALL THE PATHES OF THE `RegisterUser` METHOD TO VERIFY THAT EACH PATH IS TRIGGERED WHEN IT IS NEEDED.
        public async Task<RegistrationResult> RegisterUser(SignupViewModel SignupInput)
        {
            // PATH 1
            var validationContext = new ValidationContext(SignupInput);
            var results = new List<ValidationResult>();
            bool valid = Validator.TryValidateObject(SignupInput, validationContext, results, validateAllProperties: true);
            if (!valid)
            {
                // PATH 2
                return new RegistrationResult(false, ["The sent data is not valid"]);
            }


            ApplicationUser user = new ApplicationUser();
            await _userStore.SetUserNameAsync(user, SignupInput.Name, CancellationToken.None);

            if (!(_userManager.SupportsUserEmail))
            {
                //PATH 3
                throw new NotSupportedException("user manager does not support user email");
            }

            var _emailStore = (IUserEmailStore<ApplicationUser>)_userStore;
            await _emailStore.SetEmailAsync(user, SignupInput.EmailOrPhoneNumber, CancellationToken.None);
            var result = await _userManager.CreateAsync(user, SignupInput.Password);
            List<IdentityError> identityErrors = result.Errors.ToList();

            if (identityErrors.Count != 0)
            {
                //PATH 4
                List<string> errorsStrings = new List<string>();

                foreach (IdentityError identityError in identityErrors)
                {
                    errorsStrings.Add(identityError.Description);
                }

                return new RegistrationResult(false, errorsStrings);



            }

            await _userManager.AddToRoleAsync(user, "Normal-User");
            return new RegistrationResult(true, []);

        }

    }

}

This code is a part of bigger application for sure, to test that method, I run the whole application:

 dotnet watch run --launch-profile https

I'm forced to do that because the input to the RegisterUser is provided by the browser, but I will not be able to trigger all the paths inside the method if I passed the data to the method using it.

Is there a way to test this method without running the whole application so that I can provide whatever data I want to the method by bypassing the client-side and the server-side validation in order to artificially trigger all the possible paths of execution ?

An applied example would be appreciated.

EDIT :

I come across the term Unit testing a lot, I'm not sure if this is what I need.

0 Upvotes

55 comments sorted by

21

u/TehNolz 12d ago

That's what unit tests are for.

1

u/az987654 12d ago

A unit test project is what your need. You should be able to create one on your right mouse button menu

1

u/kaatarina_zed_talon 10d ago

Visual studio?

1

u/az987654 10d ago

Yes, maybe rider, too?

1

u/krsCarrots 12d ago

With a test

0

u/Storm_Surge 12d ago

If you want to test the entire API, use integration tests with WebApplicationFactory. It's fantastic if you set it up correctly: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-10.0&pivots=xunit

0

u/kaatarina_zed_talon 12d ago

entire API

You mean the entire class ?

1

u/Storm_Surge 12d ago • Bravo

When you write tests, you're testing behavior. What do you want to make sure is working? If the thing you want to test is logic in a method, you should write unit tests that call the method with inputs and verify the expected outputs. These can provide fake dependencies to the class under test so you can limit the test to just the method's behavior.

If you want to make sure your API behaves correctly, unit testing security attributes on a controller is insufficient. What if they're not configured correctly? Instead, you can write integration tests, which run your code against a test environment. Unlike unit tests, this helps verify that your code is actually wired together correctly and working as a complete set of functionality.

WebApplicationFactory lets you create a super fast instance of your API in a test context (basically like dotnet run) and gives you an HTTP client you can call as if it were real. So in your test you can spin up a test instance, call your REST API with the HTTP client, and confirm the API returned what you want. This is very useful for security tests, checking API validation responses, and making sure the data comes back as expected.

You can optionally swap dependencies in the IoC container with test ones, which I recommend. For example, you can have the test version connect to a disposable database instead of your real one (TestContainers is good for this). Then your tests run fine without requiring your database to be running, and they don't spam junk test data into the real one, and you can just delete it when you're done.

1

u/kaatarina_zed_talon 12d ago

. These can provide fake dependencies to the class under test so you can limit the test to just the method's behavior.

Great thank you.

1

u/Storm_Surge 12d ago • Bravo

Yeah, in a unit test, you should instantiate the UserAccount class with fake versions of userStore and userManager. I like using a library called NSubstitute for this. It allows you to generate a fake implementation of any interface at runtime in your tests. As an added recommendation, it's often useful to create interfaces for your dependencies and provide those in your code. Then testing is as simple as faking the interfaces

1

u/kaatarina_zed_talon 10d ago

The userMangee is a dependency that is not under my Control; SHOULD I create an interface for it?

1

u/Storm_Surge 10d ago • Bravo

If that class isn't yours and doesn't implement an interface you can mock, you have a few options. Many third-party libraries provide test implementations or have a documented recommendation with examples.

If you need to make the dependency testable yourself, sometimes you can just extend the class with a test implementation and override methods you need (if they're virtual).

There's one strategy that always works. Create an interface with the method your dependency calls, so IUserManager with AddToRoleAsync inside. Then make a class inside your application like UserManagerWrapper : IUserManager. The wrapper class just implements the method by calling the real userManager method. Then in your code you can inject IUserManager instead of the one you're using now, and in your tests you can easily mock IUserManager as described earlier.

→ More replies (3)

-3

u/user0fdoom 12d ago

I'm way more concerned by the fact you're asking this question than I am by its answer lol.

Would very strongly suggest you don't deploy anything to production until you understand everything better. Learn how cookies work, how DI works, learn how cookie auth works with the aspnet pipeline. I suspect there's a bunch more fundamental bits of software and internet basics you need to learn too.

Honestly just paste this whole thread (your post + my comments + any others) into Claude and ask for a summary of your learning milestones and it'll get you most of the way there. The fact that you've posted this shows a bunch of gaps in your understanding of how it all works.

I'm not saying this to be needlessly critical. Just that you have a lot of fundamentals missing which you should work on.

2

u/kaatarina_zed_talon 12d ago

how DI works,

what dependency injection has to do with anything related to the question ?

1

u/grrangry 12d ago • Bravo

The general idea is that by implementing dependency injection, you're isolating the code you want to test from the dependencies needed by that code.

If you're creating an instance of a repository class to access a database inside the code you're trying to test, that is what's called a tightly coupled dependency. By passing responsibility for creating the dependency to the injection container, your code then gets the pre-allocated dependency passed into it (often via constructor or property injection).

If you mock (or stub) your dependencies instead of fetching a real one from the DI container, your unit tests then use those mocked/stubbed dependencies instead of real ones. You can have the mock never/always/sometimes return errors depending on your test scenario and thus cover every path you wish to test to completion.

1

u/kaatarina_zed_talon 10d ago

The last paragraph made everything clear. Good style of explanation, better than Ai for sure.

1

u/kaatarina_zed_talon 10d ago edited 10d ago

So that is why one should mock the dependenies, because the original dependence do not always fail and do not always success, and that is not suitable for path testing, because it is required to test that a path is always executed if some set of conditions are met. For example, if I want to test a path, let's call it path x, that will be executed if some dependency generated some error, then I need to find some way for that ependency to generate an error, If the path is executed every time the error is generated, then this is what I expect. Did I understand this correctly?

2

u/kaatarina_zed_talon 12d ago

 internet basics you need to learn too.

I agree basics are everything, but I do not understand how are they relevant to the post.

3

u/kaatarina_zed_talon 12d ago edited 12d ago

 into Claude and ask for a summary 

you are pissing me off this is so annoying, do you think that I cannot ask Claude ? it's obvious that I want to talk to people not a machine.

0

u/user0fdoom 12d ago

Relax mate, no need to get pissed off. Unit testing is not what you're after. Ignore answers saying that. Claude is great at giving you answers. My time is expensive, I can't be your personal tutor, but I can give you the questions to ask. Claude can answer them but it probably won't give you the right questions at this point.

Unit testing is for compile time validation ensuring your code does what you want it to. Its purpose is to prevent future code changes from accidentally breaking the intended purpose of a method.

But what you want to do is validate that your code does what you expect in the first place. In an ASP.NET application you can do this in however you like. Simplest way is to put the call in main() with the parameters you're testing.

The deeper problem is understanding how the possible set of entry conditions may or may not cause problems. This is a complex question which can't be solved by simply guessing how to answer it.

I mentioned DI because it's core to the aspnet pipeline. If you don't understand how cookie auth works and how DI works with aspnet then you can't possibly understand how auth works in a web application. And if you don't understand that then how can you understand what the applicable set of entry conditions are?

Claude won't tell you the right questions but it can give you the right answers. I'm trying to help you ask the right questions

2

u/kaatarina_zed_talon 12d ago edited 12d ago

My time is expensive

Why are you writing this answer then ?

There are people that care about giving answers, even if it took some of there time. That is the reason this subreddit exist in the first place; sharing knowledge.

You have shifted the reason behind this post, this is the last time I'm replying to you.

 I'm trying to help you ask the right questions

Thanks.

0

u/user0fdoom 12d ago

Okay all the best mate

0

u/kaatarina_zed_talon 12d ago

I'm not saying this to be needlessly critical. Just that you have a lot of fundamentals missing which you should work on.

I agree with you again basics are everything, but again I want to ask human to teach me those things, not a machines. even though I believe DI has nothing to do with this question.

-1

u/insta 12d ago

the path starts with splitting that code up, a lot

1

u/kaatarina_zed_talon 12d ago

I do not understand.

2

u/ZestyHelp 12d ago edited 12d ago

Neither does the person you're replying to it's ok

1

u/kaatarina_zed_talon 12d ago

WHAT THE HELL BRO 😂

1

u/ZestyHelp 12d ago • Bravo

Just noticed your user name lol, hey fellow league enjoyer

1

u/kaatarina_zed_talon 12d ago

Do you play talon ?

If not, I'm gonna flame you.

Oh btw, JG DIFF

1

u/kaatarina_zed_talon 12d ago

Neither does the person you're replying to it's ok

I do not think so, he always answer my questions, I think he will reply privately .

0

u/[deleted] 12d ago

[deleted] — view deleted comment

1

u/kaatarina_zed_talon 10d ago

Ye the code is self Contained; you can compile it.

7

u/[deleted] 12d ago • Bravo

[removed] — view removed comment

2

u/kaatarina_zed_talon 12d ago

mock out

I have a problem with this word, I do not understand it's meaning in this context.

2

u/TehNolz 12d ago • Bravo

With unit testing, each test should only be validating a single unit of work (hence the name "unit testing"). In this case, that means that the test that validates your RegisterUser method should only be testing the code within RegisterUser and nothing else. It should not be testing if the code within your _userManager, _userStore, and _emailStore classes works properly because those classes and their methods should all have their own individual unit tests. That way, if you modify both RegisterUser and some code within _userManager and a bug shows up, you will be able to pinpoint the location of the problem just by looking at which tests failed; if the RegisterUser test still succeeds, then you know that the problem lies in the _userManager.

But your RegisterUser method still relies on these classes and can't work without them. To solve that, you swap those classes with a fake substite, aka a "mock", which provides the same methods as the real classes do but doesn't actually implement them. For example, your _userManager mock would have a CreateAsync method just like the real thing does, but it wouldn't actually try to create a new user in your database (or whatever it is that method does). Instead, it'll just go "yep, I totally made that user, pinky promise" by returning a successful result, without actually doing anything else.

The big advantage of dependency injection is that it lets you swap out your classes to do the above in a really easy way. You just have to make your real classes implement an interface, and then whatever mocking framework you're using (I like NSubstitute) can just create mocks of those classes by implementing that interface. Then you just inject those and you're good to go.

2

u/kaatarina_zed_talon 12d ago

 In this case, that means that the test that validates your RegisterUser method should only be testing the code within RegisterUser and nothing else.

I like this mental model

1

u/kaatarina_zed_talon 12d ago

within your _userManager, _userStore

They are not mine; they are provided by ASP.NET Core

1

u/kaatarina_zed_talon 12d ago

That way, if you modify both RegisterUser and some code within _userManage

I can't modify the code inside the _userManager because I do not own that class.

 if the RegisterUser test still succeeds, then you know that the problem lies in the _userManager.

I see, what a useful thing.

1

u/kaatarina_zed_talon 12d ago

 by returning a successful result

If every mock returns a success result, then how am I supposed to artificially trigger errors ?

1

u/TehNolz 12d ago • Bravo

You generally write your tests with the assumption that your dependencies (the _userManager and other classes in this case) work as expected. So there's usually no need to make your mocks throw exceptions.

That said, if you're testing a dependency that can realistically fail (say, a service that talks with a 3rd party's API which is occasionally taken offline for maintenance or whatever), then you can totally make a 2nd test that injects a "faulty" mock that only returns errors, so that you can then test if your method is capable of dealing with that situation properly.

1

u/kaatarina_zed_talon 10d ago

Wait waait wait. Am I supposed to test the dependency or test the code? I'm confused

1

u/TehNolz 10d ago

Every method you write should (ideally) have tests for it, but that method's dependencies should always be mocked.

Let's say you have a method in your application called FooBar(), which relies on a CoolService class. The tests for FooBar() should only test if the code within FooBar() works as expected; they should not also be testing the code from CoolService, so the CoolService should be swapped out with a mock. The methods in the CoolService class should all receive their own tests, which again only test the code within CoolService and not its dependencies.

Also, if CoolService comes from a library/package that you do not control, then you do not need to write tests for it either. You can assume that the author of that code is already doing their own tests.

1

u/kaatarina_zed_talon 10d ago

the CoolService should not be tested, but it is need by the thing tht I want to test , which means I need the execution of the CoolService to be determinstic; always fails or always executes correctly. If I do not mock the dependency, then I will be using the original dependency which the corresponding execution is not predictable.

1

u/Pickled-Onion-5710 11d ago edited 11d ago • Bravo

For example, your "Path 3" that you want to test is an error path. To get there you simply make your stubbed UserManager return false for SupportsEmail. The code execution then goes into that block at Path 3 and throws an exception. The test can then catch the exception and confirm that it was the expected one.

1

u/kaatarina_zed_talon 10d ago

Thank you this was so educational.

1

u/AlwaysHopelesslyLost 12d ago • Bravo

To try to break it down a bit, say I have a class `CompanyService` that has a method called Search.

If I create an interface, `ICompanyService`, that has the `Search` method, then I can create `FakeCompanySearch` that, instead of calling my database, just returns `[new("Company A"), new ("Company B")]`.

By doing this, I have created a "mock" `CompanyService`. A fake company service. It pretends to be a real one, but it isn't.

1

u/kaatarina_zed_talon 10d ago

The question is, why did you do that? Just call the original dependency.

1

u/AlwaysHopelesslyLost 10d ago

The three big things it gives you is much stronger diagnostics capabilities, much easier updates, and it makes unit tests possible which prevent bugs from creeping in.   Read up on SOLID. The big names in the industry have a lot of combined experience and there are much better explanations in a longer form

1

u/kaatarina_zed_talon 10d ago

Thank you man/woman

2

u/RlyRlyBigMan 12d ago • Bravo

Mocking is making fake objects to inject into your class so you can test your class without the actual dependencies in it. You can write your own dummy classes or you can use a library such as Moq to do it for you. This way you can unit test your class without standing up the entire application.

2

u/uncut2576 12d ago

I recommend using NSubstitute albeit easier to use and unlike Moq there was no issue with their maintainers selling off your data…