stryker-mutator / stryker-net

Mutation testing for .NET core and .NET framework!
https://stryker-mutator.io
Apache License 2.0
1.76k stars 175 forks source link

Cannot kill binary expression mutant #441

Closed alexbunardzic closed 5 years ago

alexbunardzic commented 5 years ago

I have the following conditional statement in my code:

if(x > y)

Stryker.net mutates it to:

if(x >= y) and the unit test fails to kill it.

When I modify the original condition to:

if(x >= y) Stryker mutates it back to if(x > y) and the unit test fails to kill it.

So it seems to be going in circles. How to kill that mutant?

dupdob commented 5 years ago

could please provide the full function?

rouke-broersma commented 5 years ago

Could you provide the unit test you think should be covering that statement as well? Since you say you changed the source code rather than the unit test it would be possible that your unit test doesn't actually cover either case.

alexbunardzic commented 5 years ago

Here is the implemented code:

    public double CalculateAdmissionPrice(int age) {
        var price = 0.0;
        if(age >= 18) {
          price = 50;
        } else if(age <= 18) {
          price = 25;
        } else {
            price = 0;
        }
        return price;
    }

Here is the unit test:

    [Fact]
    public void GivenOneAdultCalculatePrice()
    {
        var expected = 50.00;
        var calculator = new PriceCalculator();
        var actual = calculator.CalculateAdmissionPrice(35);
        Assert.Equal(expected, actual);
    }

Here is Stryker output:

All mutants have been tested, and your mutation score has been calculated

dupdob commented 5 years ago

thanks for providing the source code and the test. To kill another mutant, you should add a test for an age of 18 (to be confirmed, see below why it may also be 17 or 19). To kill the remaining two, you need to modify the source code.

When I read the code, I realise that 18 is an ambiguous age, because it works both for age >= 18 and age<=18. From a practical point of view, the result will be 50.0, as it is the first condition checked. But there is also the strangeness of the third path, where you return 0.0. From a logical perspective, someone is either less than 18 years old or at least 18 years old. There is no third situation. So, from my point of view, the code should be:

public double CalculateAdmissionPrice(int age) {
    var price = 50.0;

    if(age < 18) {
      price = 25;
    } 
    return price;
}

Which can also be simplified down to:

public double CalculateAdmissionPrice(int age) {
    return age < 18 ? 25.0 : 50.0;
}

Based on what you presented, of course

richardwerkman commented 5 years ago

Dubdop has covered this already very well. It seems your attempt to kill the mutant failed because you changed the source code and not your tests. To be clear, these are the steps when you encounter a survived mutant:

If you do this for every survived mutant Stryker.NET finds your test quality should improve greatly.

Since this issue seems unrelated to Stryker.NET itself I will close this issue :+1:

alexbunardzic commented 5 years ago

Thanks. What if it's not a binary choice, but we have several tiers. For example, if age < 5 price=0, if age >= 5 and age < 18 price = 25, if age >= 18 and age < 65 price= 50, if age >=65 price = 30?

rouke-broersma commented 5 years ago

Add unit tests for all your edge cases, and the mutants should be killed. The purpose of mutation testing is to see if you have exhausted all possible code paths so the fix for unkilled mutants is almost always to add unit tests or fix problems in existing unit tests. The only reason to edit source code is if examining the non-killed mutant shows you that your code has some business logic flaw.

alexbunardzic commented 5 years ago

Thanks for your kind and gentle guidance :) Being relatively new to Stryker, I really appreciate your expertise!

rouke-broersma commented 5 years ago

You can read more about mutation testing here if you haven't read it already: https://stryker-mutator.io

alexbunardzic commented 5 years ago

Not sure where to ask this, but what are the plans to add Block Statement mutation to Stryker.NET? Not having Block Statement mutation is the only thing holding it back from being the de facto mutating test framework for .NET

rouke-broersma commented 5 years ago

See #208

We prioritized dotnet framework support first basically. We have that now so now we can focus on other features.

Not having Block Statement mutation is the only thing holding it back from being the de facto mutating test framework for .NET

What framework would you say fills that role right now? :)

alexbunardzic commented 5 years ago

Stryker.NET is the only serious contestant at the moment. But what we really need to cement it is the Block Statement.

richardwerkman commented 5 years ago

@alexbunardzic thank you for your input! The block mutator will come soon 👍 Until now our focus was mainly .net framework support, since we want to support all .net projects.