ekenman
ekenman

Reputation: 995

How to mock AuthenticationStateProvider

I have a class that takes an AuthenticationStateProvider in the constructor. I'd like to write unit test and mock this.

I'd like to be able to set what user is returned from the call GetAuthenticationStateAsync.

  const string userId = "123";
  const string userName = "John Doe";
  const string email = "[email protected]";

  var claims = new List<Claim>
  {
    new Claim(ClaimTypes.NameIdentifier, userId),
    new Claim(ClaimTypes.Name, userName),
    new Claim(ClaimTypes.Email, email),
 };

 var identity = new Mock<ClaimsIdentity>(claims);
 var principal = new Mock<ClaimsPrincipal>(identity.Object);

 var mockOfAuthenticationStateProvider = new Mock<AuthenticationStateProvider>();
 var mockOfAuthState = new Mock<AuthenticationState>();
          
 mockOfAuthenticationStateProvider.Setup(p => 
   p.GetAuthenticationStateAsync()).Returns(Task.FromResult(mockOfAuthState.Object)); 

I get this errormessage:

Testfunction threw exception: System.ArgumentException: Can not instantiate proxy of class: Microsoft.AspNetCore.Components.Authorization.AuthenticationState. Could not find a parameterless constructor. (Parameter 'constructorArguments') ---> System.MissingMethodException: Constructor on type 'Castle.Proxies.AuthenticationStateProxy' not found.

Upvotes: 1

Views: 1707

Answers (2)

Wolfware
Wolfware

Reputation: 111

The error message is pretty clear. You need to use the correct constructor of AuthenticationState.

The source of AuthenticationState has this constructor:

public AuthenticationState(ClaimsPrincipal user)
{
    User = user ?? throw new ArgumentNullException(nameof(user));
}

So you can just create you test objects directly:

var mockOfAuthenticationStateProvider = new Mock<AuthenticationStateProvider>();
var claimsPrincipal = new ClaimsPrincipal();
var authState = new AuthenticationState(claimsPrincipal);
      
mockOfAuthenticationStateProvider.Setup(p => 
    p.GetAuthenticationStateAsync()).Returns(Task.FromResult(authState));

As Scott Hannen stated, you can also create a ClaimsPrincipal using a ClaimsIdentity with specified claims if needed.

Upvotes: 0

Scott Hannen
Scott Hannen

Reputation: 29262

AuthenticationStateProvider is abstract, so if you can't mock it you can create an implementation of it, like this:

public class FakeAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ClaimsPrincipal _principal;

    public FakeAuthenticationStateProvider(ClaimsPrincipal principal)
    {
        _principal = principal;
    }

    // This static method isn't really necessary. You could call the 
    // constructor directly. I just like how it makes it more clear
    // what the fake is doing within the test.
    public static FakeAuthenticationStateProvider ForPrincipal(ClaimsPrincipal principal)
    {
        return new FakeAuthenticationStateProvider(principal);
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        return Task.FromResult(new AuthenticationState(_principal));
    }
}

You can set it up like this:

const string userId = "123";
const string userName = "John Doe";
const string email = "[email protected]";
var claims = new List<Claim>
{
    new Claim(ClaimTypes.NameIdentifier, userId),
    new Claim(ClaimTypes.Name, userName),
    new Claim(ClaimTypes.Email, email),
};

// These don't need to be mocks. If they are the test likely
// won't behave correctly.
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
var authenticationStateProvider = 
    FakeAuthenticationStateProvider.ForPrincipal(principal);

and then pass it to any other class that depends on AuthenticationStateProvider.

When we create a fake implementation instead of a mock it's reusable and also easier to set up, so it keeps the test a little bit smaller.

For example, if the reason you're setting this up is so that the mock/fake returns a user with certain claims, you could have the fake just take a set of claims in its constructor. Then the constructor does the rest of the work, making your test even smaller.

For example,

public class FakeAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ClaimsPrincipal _principal;

    public FakeAuthenticationStateProvider(ClaimsPrincipal principal)
    {
        _principal = principal;
    }

    public static FakeAuthenticationStateProvider ForPrincipal(ClaimsPrincipal principal)
    {
        return new FakeAuthenticationStateProvider(principal);
    }

    public static FakeAuthenticationStateProvider ThatReturnsClaims(params Claim[] claims)
    {
        var identity = new ClaimsIdentity(claims);
        var principal = new ClaimsPrincipal(identity);
        return new FakeAuthenticationStateProvider(principal);
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        return Task.FromResult(new AuthenticationState(_principal));
    }
}

Now your test can have less clutter:

var claims = new []
{
    new Claim(ClaimTypes.NameIdentifier, "123"),
    new Claim(ClaimTypes.Name, "John Doe"),
    new Claim(ClaimTypes.Email, "[email protected]"),
};

var authenticationStateProvider = FakeAuthenticationStateProvider.ThatReturnsClaims(claims);

I usually end up with a folder called "Fakes" in my test project for them.

Upvotes: 6

Related Questions