1

I have some sample c# code that demonstrates feature management in .net core.

I have this class that exposes an endpoint:

namespace Widgets.Controllers
{
    [Authorize]       
    public class FeatureToggleSampleController : ControllerBase
    {

        private readonly IFeatureManagerSnapshot featureManager;

        public FeatureToggleSampleController(
                              IFeatureManagerSnapshot featureManager)
        {
            this.featureManager = featureManager;
        }

        [Route("toggles/demo")]
        [HttpGet]
        [FeatureGate(nameof(WidgetToggles.sampleToggleName))]
        public string ShowSampleToggle(MeFunctionsRequest request)
        {
              return "sample toggle is enabled";

        }
    }
}

we are using a filter, to have the feature mana

[FilterAlias("WidgetsFeatures")]
public class WidgetFeaturesFilter : IWidgetFeaturesFilter
    {
        private readonly IServiceScopeFactory scopeFactory;

        public WidgetFeaturesFilter(IServiceScopeFactory scopeFactory)
        {
            this.scopeFactory = scopeFactory;
        }

        public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
        {
            using var scope = scopeFactory.CreateScope();
            var featureRepo = scope.ServiceProvider.GetService<IGenericRepository<WidgetFeature>>();
            var feature = featureRepo.Retrieve(filter: "[Name] = @FeatureName", filterParameters: new { context.FeatureName }).FirstOrDefault();
            return Task.FromResult(feature != null && feature.Enabled);
        }
    }

In our startup, we add the service like this:

services.AddFeatureManagement(Configuration.GetSection("FeatureManagement")).AddFeatureFilter<WidgetsFeaturesFilter>();

And, this is the json file where we define the toggles:

{
  "FeatureManagement": {
    "sampleToggleName": {
      "EnabledFor": [
        {
          "Name": "WidgetsFeatures"
        }
      ]
    }
  }
}

When the bool is true in the database, hitting http://localhost/toggles/demo returns the "sample toggle is enabled" method message. And when I change the value in the database to false, it 404s in the browser. Everything seems to be working as far as the toggle logic itself.

Now I want to demo how to unit test controllers and other modules that use feature toggles.

So far I have written two unit test code based on a sample i found. The first one works - although I'm not sure if it's because i've written the test correctly. But gives me the string I'm looking for.

The second one fails because the mock is not working. I've asked the mockFeatureManager to return false as far as the toggle status, but it still behaves as if the toggle is enabled.

[Collection("UnitTests")]
public class FeatureToggleSampleControllerShould
{


    [Fact]
    public async void Return_String_If_Toggle_Enabled()
    {
        var mockFeatureManager = new Mock<IFeatureManagerSnapshot>();
        mockFeatureManager
            .Setup(x=>x.IsEnabledAsync(nameof(WidgetsFeatureToggles.sampleToggleName)))
            .Returns(Task.FromResult(true));

        var featureManager = mockFeatureManager.Object;
        Assert.True(await featureManager.IsEnabledAsync(nameof(WidgetsFeatureToggles.sampleToggleName)));

        var sampleControler = new FeatureToggleSampleController(TestHelper.Appsettings,featureManager);
        Assert.Equal("sample toggle is enabled",sampleControler.ShowSampleToggle());
    }

    [Fact]
    public async void Return_404_If_Toggle_Enabled()
    {
        var mockFeatureManager = new Mock<IFeatureManagerSnapshot>();
        mockFeatureManager
            .Setup(x=>x.IsEnabledAsync(nameof(WidgetsFeatureToggles.sampleToggleName)))
            .Returns(Task.FromResult(false));

        var featureManager = mockFeatureManager.Object;
        Assert.False(await featureManager.IsEnabledAsync(nameof(WidgetsFeatureToggles.sampleToggleName)));
        var sampleControler = new FeatureToggleSampleController(TestHelper.Appsettings,featureManager);
        try{
            sampleControler.ShowSampleToggle();
        } catch (Exception ex) {
            Assert.Contains("404",ex.Message);
        }
    }
}

I don't think the bug is with the actual code, but the unit test. But any tips would be appreciated.

EDIT 2

This was the post I was using as an example: IFeatureManager Unit Testing

In my case, I'm not using azure ...

dot
  • 14,928
  • 41
  • 110
  • 218
  • I dont think FeatureGate will use your private `featureManager`. I would think you would need to mock it before it gets added to the container. – mxmissile Aug 29 '22 at 17:46
  • @mxmissile any idea on how I might do that? – dot Aug 29 '22 at 17:54
  • This has been bugging me all day, I thought it "should" be simple, but turns out simply calling `sampleControler.ShowSampleToggle();` wont fire any attributes unless your test runner is setup to do so. You'd need to spin up a complete WebApplication instance to inject your mock `featureManager`, would be more of an integration test at that point. Probably better off just testing the attribute against a ActionContext instead: https://www.dotnetnakama.com/blog/creating-and-testing-asp-dotnet-core-filter-attributes/ (bottom of article) Sorry wasnt more help. – mxmissile Aug 29 '22 at 21:56
  • no worries. I'll check out the article. I've updated the original post too to include more set up information, just in case. But no worries if you have no additional comments @mxmissile – dot Aug 30 '22 at 14:46

0 Answers0