vanderkleij / Smocks

Smocks is a library for mocking the normally unmockable. It can mock static and non-virtual methods and properties, amongst others.
MIT License
114 stars 26 forks source link

NullReferenceException #35

Open krilbe opened 7 years ago

krilbe commented 7 years ago

I have a static class with a single static method, but whenever I try to Setup a mock call to it, I get a NullReferenceException. If I write another method with the same signature, but dummy code, and Setup that one instead, it works. So there must be something within my method that Smocks can't handle correctly.

Here's the complete method:

public static object HämtaParametrar(string[] args, Type parameterklass)
{
    Regex växelmönster = new Regex(@"^[-/](\w+)");
    HashSet<string> ejHittadeObligatoriskaVäxlar = new HashSet<string>(
        parameterklass.GetProperties()
        .Select(pi => pi.GetCustomAttribute<KörningsparameterAttribute>())
        .Where(attr => attr != null && attr.Obligatorisk)
        .Select(attr => attr.Växel)
    );

    object parametrar = Activator.CreateInstance(parameterklass);

    int argnum = 0;
    while (++argnum < args.Length)
    {
        string arg = args[argnum];

        Match träff = växelmönster.Match(arg);
        if (!träff.Success)
            LoggaOchKastaUndantag($"Parametern \"{arg}\" ser inte ut som en växel; ska inledas med - eller /.");

        string växelnamn = träff.Captures[0].Value.Substring(1);

        var propOchAttr = parameterklass.GetProperties()
            .Select(pi => new { Prop = pi, Attr = pi.GetCustomAttribute<KörningsparameterAttribute>() })
            .Where(pa => pa.Attr != null && pa.Attr.Växel == växelnamn)
            .FirstOrDefault();

        if (propOchAttr == null)
            LoggaOchKastaUndantag($"Växeln \"{växelnamn}\" verkar inte existera för angiven körning.");

        PropertyInfo propinfo = propOchAttr.Prop;
        KörningsparameterAttribute attr = propOchAttr.Attr;

        switch (attr.Parametertyp)
        {
            case KörningsparameterTyp.Flagga:
                if (propinfo.PropertyType != typeof(bool))
                    LoggaOchKastaUndantag($"Växeln \"{växelnamn}\" är markerad som {attr.Parametertyp} med är inte av typen bool.");
                propinfo.SetValue(parametrar, true);
                break;
            case KörningsparameterTyp.Text:
                if (propinfo.PropertyType != typeof(string))
                    LoggaOchKastaUndantag($"Växeln \"{växelnamn}\" är markerad som {attr.Parametertyp} med är inte av typen string.");
                ++argnum;
                if (argnum >= args.Length)
                    LoggaOchKastaUndantag($"Växeln \"{växelnamn}\" ska följas av ett textvärde, men står sist bland parametrarna.");
                propinfo.SetValue(parametrar, args[argnum]);
                break;
            default:
                LoggaOchKastaUndantag($"Hittade okänd parametertyp \"{attr.Parametertyp}\" för växeln \"{växelnamn}\"");
                break;
        }

        ejHittadeObligatoriskaVäxlar.Remove(växelnamn);
    }

    if (ejHittadeObligatoriskaVäxlar.Count > 0)
        LoggaOchKastaUndantag($"Följande obligatoriska växlar saknas: {string.Join(", ", ejHittadeObligatoriskaVäxlar.Select(s => $"\"{s}\""))}");

    return parametrar;
}

The method LoggaOchKastaUndantag just logs the message (log4net) and then throws an exception with the same message:

private static void LoggaOchKastaUndantag(string meddelande)
{
    log.Fatal(meddelande);
    throw new ArgumentException(meddelande);
}

Sorry about the Swedish identifiers... What might be "wrong" with the method? It does seem to work as intended outside of Smocks.

krilbe commented 7 years ago

Here's the dummy method that works:

public static object Test(string[] a, Type t)
{
    if (t.Name.StartsWith("I"))
        return a;
    else
        return a.Length;
}

Here's the unit test method I'm trying with:

[TestMethod]
public void HittarKörningsklassen()
{
    Smock.Run(context =>
    {
        string[] args = new string[] { };
        Type paramklass = typeof(TestkörningParams);
        context.Setup(() => Körningsparameterhämtare.HämtaParametrar(args, paramklass)).Returns(6);
        context.Setup(() => Körningsparameterhämtare.Test(args, paramklass)).Returns(6);
    }

If I have the first Setup line there, I get a NullReferenceException on the Smock.Run line. If I comment it out, everything runs just fine.