mercredi 4 mai 2016

c# - unit testing, mock and ninject factory extension

I have a console app where the user inputs a number, and I generate a feature according to that number. I have used Ninject.Extensions.Factory for that, here are the bindings:

    Bind<IFeature>().To<FirstFeature>().Named("1");
    Bind<IFeature>().To<SecondFeature>().Named("2");
    Bind<IFeature>().To<ThirdFeature>().Named("3");
    Bind<IFeatureFactory>().ToFactory(() => new UseFirstArgumentAsNameInstanceProvider());

The code I want to test is:

constructor:

public FeatureService(IFeatureFactory featureFactory, IUILayer uiHandler, int howManyFeatures)
    {
        this.featureFactory = featureFactory;
        this.uiHandler = uiHandler;
        this.howManyFeatures = howManyFeatures;
    }

method under test:

public async Task startService()
    {
        bool isBadInput = false;
        string userSelection = null;
        uiHandler.displayMenu(howManyFeatures);
        userSelection = uiHandler.getSelection();
        while (!userSelection.Equals((howManyFeatures+1).ToString()))
        {
            IFeature feature = null;
            try
            {
                feature = featureFactory.createFeature(userSelection);
                isBadInput = false;
            }
            catch (ActivationException ex)
            {
                uiHandler.displayErrorMessage();
                isBadInput = true;
            }
            if (!isBadInput)
            {
                await feature.execFeature();
            }
            uiHandler.displayMenu(howManyFeatures);
            userSelection = uiHandler.getSelection();
        }
    }

As you can see, when I try to createFeature, I catch the ActivationException, meaning that the user has input invalid selection (ninject fails to get the concrete class), and execFeature is not called.

I am trying to write a unit test to test that when a user inputs a valid selection, the method execFeature is called.

Here is the test:

    [TestMethod]
    public void WhenUserEnterValidSelectionExecFeatureCalled()
    {
        //Arrange
        Mock<IFeature> featureMock = new Mock<IFeature>();
        Mock<IConsoleService> consoleServiceMock = new Mock<IConsoleService>();
        // mock user input 7
        consoleServiceMock.Setup(c => c.ReadLine()).Returns("7");
        IUILayer uiLayer = new ConsoleUILayer(consoleServiceMock.Object);
        Mock<IFeatureFactory> featureFactory = new Mock<IFeatureFactory>();
        featureMock.Setup(t => t.execFeature());
        featureFactory.Setup(t => t.createFeature(It.IsAny<string>())).Returns(featureMock.Object);
        // init FeatureService with 3 features
        IFeatureService featureService = new FeatureService(featureFactory.Object, uiLayer, 3);

        //Act
        featureService.startService();

        //Assert
        featureMock.Verify(t => t.execFeature());
    }

As you can see - I an creating a consoleMock with user input of "7", and when I create the FeatureServiceI put 3 in the howManyFeatures - Test should fail (no concrete implementation).

Now, when I run my program normally - if I input "7", the program acts as expected and outputs an error message.

When I run the test, every input to the consoleMock besides the HowManyFeatures + 1 passes the test (HowManyFeatures +1 fails because it doesn't go into the while), and it shouldn't be like that - it should fail for the number that don't have a concrete IFeature implementation (only 1, 2 and 3 have concrete implementations).

How do I solve this? Should I "bring" the Ninject Bindings into the Tests project? should I even test this method? or its all useless?

Any thoughts are appreciated

Aucun commentaire:

Enregistrer un commentaire