简体   繁体   中英

How can I rewrite this foreach to be a part of the previous Linq expression?

I am decorating my MSTest tests with custom attributes, SpecificationAttribute and VerificationAttribute which I later use to generate html test specification reports.

The attributes look like this:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class SpecificationAttribute : Attribute
{
    public SpecificationAttribute(int step, string text)
    {
        Step = step;
        Text = text;
    }

    public int Step { get; set; }

    public string Text { get; set; }
}

I have written a test which asserts that a test method must have unique steps in the usage of the attributes. For example, the following usage fails my test, as it should:

[Specification(1, "Click Button)]
[Verification(1, "Verify popup")]
[Specification(1, "Click close")]
[Verification(2, "Verify popup disappears")]

My test looks like this:

[TestMethod]
public void TestForNoDuplicateStepsOnMethod()
{
    var specificationMethods = Assembly.GetExecutingAssembly().GetTypes()
        .Where(type =>
            type.GetCustomAttribute<CompilerGeneratedAttribute>() == null &&
                type.GetCustomAttribute<TestClassAttribute>() != null)
        .SelectMany(type => type.GetMethods())
        .Where(methodInfo =>
            methodInfo.GetCustomAttribute<TestMethodAttribute>() != null &&
            methodInfo.GetCustomAttributes<SpecificationAttribute>().Any())
        .ToArray();

    foreach (var method in specificationMethods)
    {
        var specificationAttributes = method.GetCustomAttributes<SpecificationAttribute>();

        var duplicates = specificationAttributes.GroupBy(s => s.Step).Where(grp => grp.Count() > 1).SelectMany(x => x).ToList();

        if (duplicates.Any())
        {
            var initialMessage = $@"{Environment.NewLine}{method.DeclaringType}.{method.Name} contains several SpecificationAttribute with the same step number." + Environment.NewLine + Environment.NewLine;

            var message = duplicates.Aggregate(initialMessage, (s, attribute) => s + attribute.Step + " " + attribute.Text + Environment.NewLine);

            Assert.Fail(message);
        }
    }
}

I would like to rewrite the last part so that it becomes part of the Linq query. Is this possible? Also, my current test only outputs one failed method. It would be desireable if the Linq query could include all methods with failing conditions on unique step number.

if you dont need to do more operation on var specificationMethods you could try this:

 [TestMethod]
    public void TestForNoDuplicateStepsOnMethod()
    {
        Assembly.GetExecutingAssembly().GetTypes()
            .Where(type =>
                type.GetCustomAttribute<CompilerGeneratedAttribute>() == null &&
                    type.GetCustomAttribute<TestClassAttribute>() != null)
            .SelectMany(type => type.GetMethods())
            .Where(methodInfo =>
                methodInfo.GetCustomAttribute<TestMethodAttribute>() != null &&
                methodInfo.GetCustomAttributes<SpecificationAttribute>().Any())
            .ToList().ForEach (method=> 
        {
            var specificationAttributes = method.GetCustomAttributes<SpecificationAttribute>();

            var duplicates = specificationAttributes.GroupBy(s => s.Step).Where(grp => grp.Count() > 1).SelectMany(x => x).ToList();

            if (duplicates.Any())
            {
                var initialMessage = $@"{Environment.NewLine}{method.DeclaringType}.{method.Name} contains several SpecificationAttribute with the same step number." + Environment.NewLine + Environment.NewLine;

                var message = duplicates.Aggregate(initialMessage, (s, attribute) => s + attribute.Step + " " + attribute.Text + Environment.NewLine);

                Assert.Fail(message);
            }
    }
  }
}

(Edited to fix the problem with positive tests) Try this but IMHO it is much harder to read.

[TestMethod]
public void TestForNoDuplicateStepsOnMethod()
{
    var errors = Assembly.GetExecutingAssembly().GetTypes()
        .Where(type =>
            type.GetCustomAttribute<CompilerGeneratedAttribute>() == null &&
                type.GetCustomAttribute<TestClassAttribute>() != null)
        .SelectMany(type => type.GetMethods())
        .Where(methodInfo =>
            methodInfo.GetCustomAttribute<TestMethodAttribute>() != null &&
            methodInfo.GetCustomAttributes<SpecificationAttribute>().Any())
        .Select(method =>
            method.GetCustomAttributes<SpecificationAttribute>()
        .GroupBy(s => s.Step)
            .Where(grp => grp.Count() > 1)
            .Select(x => x.Aggregate(
                $@"{Environment.NewLine}{method.DeclaringType}.{method.Name} contains several SpecificationAttribute with the same step number." 
                + Environment.NewLine,
                (s, attribute) => s + attribute.Step + " " + attribute.Text + Environment.NewLine))
                .Aggregate("", (s, message) => s + message))
            .Aggregate("", (s, message) => s + message);
        Assert.AreEqual("", errors);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM