Simple solution would be to add the asmdef to the top folder of your 34 script folders.
If they are all across Assets folder then you can create that Script folder and move them all in there. That should not break your project as Unity will update all connections.
The long term solution you may have to go for is creating abstract/interface in assembly that current code would implement.
Say you have script Player in player.asmdef and you want to test it. But it has a dependency to Inventory which is not in any asmdef. You could move Inventory but it also has its set of dependencies and so on.
Instead of moving Inventory, you create a base inventory as abstract and interface in the manager.asmdef and add this one to player.asmdef. Assuming Player.cs uses
List<Item> Inventory.GetInventory();
void Inventory.SetItem(Item item);
Your IInventory.cs could look like so
public abstract class InventoryBase : MonoBehaviour, IInventory
{
// if those methods were self contained, meaning they don't use any outside code
// the implementation could be moved here
public abstract List<Item> GetInventory();
public abstract void SetItem(Item item);
}
public interface IInventory
{
List<Item> GetInventory();
void SetItem(Item item);
}
public class Item
{
public string id;
public int amount;
public string type;
}
Then the Inventory class
public class Inventory : InventoryBase
{
// Implementation is already there since it was used
// but requires the override on the methods
}
It may feel like adding extra useless layers but this adds a second advantage of major importance, you can mock the IInventory object in your player test:
[Test]
public void TestPlayer()
{
// Using Moq framework but NSubstitute does same with different syntax
Mock<IInventory> mockInventory = new Mock<IInventory>();
Mock<IPlayer> mockPlayer= new Mock<IPlayer>();
PlayerLogic player = new PlayerLogic(mockPlayer.Object, mockInventory.Object);
mock.Setup(m=> m.GetInventory).Returns(new List<Item>());
}
This assumes the Player class is decoupled between the MonoBehaviour and the logic:
public class Player : MonoBehaviour ,IPlayer
{
[SerializedField] private InventoryBase m_inventory;
PlayerLogic m_logic;
void Awake()
{
m_logic = new PlayerLogic(this, m_inventory);
}
}
public interface IPlayer{}
public class PlayerLogic
{
IPlayer m_player;
IInventory m_inventory
public PlayerLogic(IPlayer player, IInventory inventory)
{
m_player = player;
m_inventory = inventory;
}
// Do what you need with dependencies
// Test will use the mock objects as if they were real
}
Notice that Player uses InventoryBase since it cannot see Inventory not being in an assembly. But as you drop in the Inventory object, the compiler will use the code down there even if Player type is not aware of Inventory type.
If you were to use another method from Inventory into Player, then you'd need to add the abstract to the base class and the declaration in the interface for testing.
PlayerLogic uses the interface instead of the base type to make the testing possible.