public void Attack_ShouldThrowInvalidOperationExceptionWhenTheHeroHasNoHealth()
        {
            //Arrange
            IHero hero     = new HeroBuilder().WithHealth(0).Build();
            IHero opponent = new HeroBuilder().Build();

            //Act + Assert
            Assert.That(() => hero.Attack(opponent), Throws.InstanceOf <InvalidOperationException>());
        }
        public void DefendAgainstAttack_SuperMode_ShouldDecrementHealthWithHalfAttackStrength()
        {
            //Arrange
            int   health         = Random.Next(50, 100);
            IHero hero           = new HeroBuilder().WithSuperModeLikeliness(1).WithHealth(health).Build();
            int   attackStrength = Random.Next(1, hero.Health);

            //Act
            hero.DefendAgainstAttack(attackStrength);

            //Assert
            Assert.That(hero.Health, Is.EqualTo(health - attackStrength / 2));
        }
        public void Attack_SuperMode_ShouldAttackOpponentWithDoubleStrength()
        {
            //Arrange
            IHero        hero         = new HeroBuilder().WithSuperModeLikeliness(1.0f).Build();
            Mock <IHero> opponentMock = new HeroMockBuilder().Build();

            //Act
            hero.Attack(opponentMock.Object);

            //Assert
            opponentMock.Verify(opponent => opponent.DefendAgainstAttack(hero.Strength * 2), Times.Once,
                                $"Should call 'DefendAgainstAttack' of opponent with strength {hero.Strength * 2}.");
        }
        private static void AssertSuperModeLikelinessForDefend(float superModeLikeliness)
        {
            int numberOfDefends       = 200;
            int numberOfNormalDefends = 0;
            int numberOfSuperDefends  = 0;

            for (int i = 0; i < numberOfDefends; i++)
            {
                int startHealth    = Random.Next(60, 101);
                int attackStrength = Random.Next(2, 20);
                if (attackStrength % 2 != 0)
                {
                    attackStrength += 1;
                }

                IHero hero = new HeroBuilder().WithSuperModeLikeliness(superModeLikeliness).WithHealth(startHealth).Build();
                hero.DefendAgainstAttack(attackStrength);

                int expectedHealthNormal    = startHealth - attackStrength;
                int expectedHealthSuperMode = startHealth - attackStrength / 2;
                Assert.That(hero.Health, Is.EqualTo(expectedHealthNormal).Or.EqualTo(expectedHealthSuperMode),
                            $"After defending an attack of strength {attackStrength} with an initial health of {startHealth}, " +
                            $"the health of the hero should be {expectedHealthNormal} or {expectedHealthSuperMode}.");

                if (hero.Health == expectedHealthNormal)
                {
                    numberOfNormalDefends++;
                }
                else
                {
                    numberOfSuperDefends++;
                }
            }

            string staticRandomTip =
                "Tip: make sure all 'Hero' instances use the same 'Random' instance by declaring a static field like this: 'private static Random Random = new Random();'";

            Assert.That(numberOfNormalDefends, Is.GreaterThan(0),
                        $"Out of {numberOfDefends} defends, no normal defend happened. That is not random enough. {staticRandomTip}");
            Assert.That(numberOfSuperDefends, Is.GreaterThan(0),
                        $"Out of {numberOfDefends} defends, no supermode defend happened. That is not random enough. {staticRandomTip}");
            double actualSuperMode = numberOfSuperDefends / (double)numberOfDefends;

            Assert.That(actualSuperMode, Is.EqualTo(superModeLikeliness).Within(0.15),
                        $"After {numberOfDefends} defends the supermode likeliness seems to be around {Convert.ToInt32(actualSuperMode * 100)}%. " +
                        $"It should be around {Convert.ToInt32(superModeLikeliness * 100)}%. {staticRandomTip}");
        }
        private void AssertSuperModeLikelinessForAttack(float superModeLikeliness)
        {
            IHero hero = new HeroBuilder().WithSuperModeLikeliness(superModeLikeliness).Build();

            int numberOfAttacks       = 200;
            int numberOfNormalAttacks = 0;
            int numberOfSuperAttacks  = 0;

            for (int i = 0; i < numberOfAttacks; i++)
            {
                Mock <IHero> opponentMock = new HeroMockBuilder().Build();
                opponentMock.Setup(opponent => opponent.DefendAgainstAttack(It.IsAny <int>()))
                .Callback((int attackStrength) =>
                {
                    if (attackStrength == hero.Strength)
                    {
                        numberOfNormalAttacks++;
                    }
                    else if (attackStrength == hero.Strength * 2)
                    {
                        numberOfSuperAttacks++;
                    }
                });
                hero.Attack(opponentMock.Object);
            }

            Assert.That(numberOfNormalAttacks, Is.GreaterThan(0),
                        $"Out of {numberOfAttacks} attacks, no normal attack happened. That is not random enough.");
            Assert.That(numberOfSuperAttacks, Is.GreaterThan(0),
                        $"Out of {numberOfAttacks} attacks, no supermode attack happened. That is not random enough.");
            Assert.That(numberOfSuperAttacks + numberOfNormalAttacks, Is.EqualTo(numberOfAttacks),
                        $"The amount of normal attacks ({numberOfNormalAttacks}) " +
                        $"added with the number of supermode attacks ({numberOfSuperAttacks}) " +
                        $"should be equal to the total number of attacks ({numberOfAttacks}).");
            double actualSuperMode = numberOfSuperAttacks / (double)numberOfAttacks;

            Assert.That(actualSuperMode, Is.EqualTo(hero.SuperModeLikeliness).Within(0.15),
                        $"After {numberOfAttacks} attacks the supermode likeliness seems to be around {Convert.ToInt32(actualSuperMode * 100)}%. " +
                        $"It should be around {Convert.ToInt32(hero.SuperModeLikeliness * 100)}%.");
        }
        public void ShouldNotifyChangesInHealth()
        {
            HeroBuilder builder = new HeroBuilder().WithHealth(100);
            IHero       hero    = builder.Build();

            INotifyPropertyChanged notifier = hero as INotifyPropertyChanged;

            Assert.That(notifier, Is.Not.Null,
                        "Hero class should implement an interface that enables property change notifications.");

            IList <string> changedProperties = new List <string>();

            notifier.PropertyChanged += (sender, args) =>
            {
                changedProperties.Add(args.PropertyName);
            };

            //trigger Health change
            builder.WithHealth(50);

            Assert.That(changedProperties.FirstOrDefault(), Is.EqualTo(nameof(IHero.Health)), "A change in Health is not notified.");
        }