Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/KnockOffTests.cs
Deukhoofd d2a82b5fe3
All checks were successful
Build / Build (push) Successful in 3m12s
Many more tests and fixes
2026-08-27 17:11:12 +02:00

136 lines
5.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="KnockOff"/> move script.
/// Behavior is verified against the Bulbapedia page for Knock Off (Generation VII).
/// </summary>
public class KnockOffTests
{
/// <summary>
/// Creates a fully mocked test setup for Knock Off tests. When <paramref name="targetHasItem"/> is true,
/// <see cref="IPokemon.TryStealHeldItem"/> succeeds and yields a mocked item.
/// </summary>
private static (KnockOff script, IExecutingMove move, IPokemon target, IPokemon user, IHitData hitData)
CreateTestSetup(bool targetHasItem)
{
var script = new KnockOff();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
var hitData = Substitute.For<IHitData>();
move.GetHitData(target, 0).Returns(hitData);
target.TryStealHeldItem(out Arg.Any<IItem?>()).Returns(callInfo =>
{
callInfo[0] = targetHasItem ? Substitute.For<IItem>() : null;
return targetHasItem;
});
var user = Substitute.For<IPokemon>();
move.User.Returns(user);
return (script, move, target, user, hitData);
}
/// <summary>
/// Bulbapedia: "Knock Off inflicts damage and knocks off the target's held item if it has one."
/// (Generation V onwards: "Knock Off now removes the held item rather than rendering it unusable".)
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsItem_ItemIsRemoved()
{
// Arrange
var (script, move, target, _, hitData) = CreateTestSetup(true);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
target.Received(1).TryStealHeldItem(out Arg.Any<IItem?>());
hitData.DidNotReceive().Fail();
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "TryStealHeldItem")).IsTrue();
}
/// <summary>
/// Bulbapedia: "Knock Off inflicts damage and knocks off the target's held item if it has one."
/// Against a target without a held item the move simply deals its damage without any further effect; it
/// does not fail.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsNoItem_HitIsNotMarkedAsFailed()
{
// Arrange
var (script, move, target, _, hitData) = CreateTestSetup(false);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
hitData.DidNotReceive().Fail();
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse();
}
/// <summary>
/// Bulbapedia (Generation V onwards): "If the user faints due to the target's Ability (Rough Skin or
/// Iron Barbs) or held Rocky Helmet, it cannot remove the target's held item."
/// Those effects damage the user before the secondary effect runs, so a user that has fainted by then must
/// not remove the item.
/// </summary>
[Test]
public async Task OnSecondaryEffect_UserIsFainted_ItemIsNotRemoved()
{
// Arrange
var (script, move, target, user, _) = CreateTestSetup(true);
user.IsFainted.Returns(true);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "TryStealHeldItem")).IsFalse();
}
/// <summary>
/// Bulbapedia (Generation VI onwards): "If Knock Off is used on a Pokémon that is holding an item that can
/// be knocked off, its base power will be boosted by 50%."
/// With Knock Off's base power of 65 the boosted power is 97 (65 × 1.5, fractions dropped).
/// </summary>
[Test]
public async Task ChangeBasePower_TargetHoldsRemovableItem_BasePowerBoostedByFiftyPercent()
{
// Arrange
var (script, move, target, _, _) = CreateTestSetup(true);
target.HeldItem.Returns(Substitute.For<IItem>());
ushort basePower = 65;
// Act
((IScriptChangeBasePower)script).ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)97);
}
/// <summary>
/// Bulbapedia (Generation VI onwards): "If Knock Off is used on a Pokémon that is holding an item that can
/// be knocked off, its base power will be boosted by 50%."
/// A target without a held item does not grant the boost, so the base power stays unchanged.
/// </summary>
[Test]
public async Task ChangeBasePower_TargetHoldsNoItem_BasePowerUnchanged()
{
// Arrange
var (script, move, target, _, _) = CreateTestSetup(false);
target.HeldItem.Returns((IItem?)null);
ushort basePower = 65;
// Act
((IScriptChangeBasePower)script).ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)65);
}
}