CSharp Allure classic NUnit with improvements

Бета, в которой была добавлена поддержка атрибута [AllureStep] в .NET Core проектах:

Настоятельно рекомендую тем, кто всё ещё сидит на .NET < 4.6.1 переходить на более высокие версии, т.к. в будущих релизах останется только поддержка netstandard2.0.

PS: Буду рад фидбэку.

1 лайк

Привет! Юзаю "Noksa.NUnit.Allure" version="3.0.6" targetFramework="net472"

Если метод void - падает ексепшн юзая [AllureStep("’")]
System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'

Приходится писать через RunStep - с ним все ок работает

Привет.
Тело метода какое?

Сорри! То я не обновился :smile: Работает на 3.1.0

1 лайк

Добрый день! Подскажите

  1. используя Ваш Allure, уже нет необходимости качать Allure.Commons?
  2. Может есть пример того как добавить логи Rest Clients respons и request?
  3. Так же у меня в отчет не попадает все что находиться в базовом классе OneTimeSetUp(TearDown), в чем может быть причина?

Спасибо!

Доброго дня.

  1. Всё верно, такой необходимости нет. Все лишние пакеты надо удалить, оставив только один этот.
  2. Привести такой пример проблематично, т.к. всё зависит от того, что вы используете для отправки запросов и получения ответов.
    Лично я использую RestSharp и у меня добавляется практически всё :slight_smile:

Вот парочка приватных методов для примера.

  1. Добавляет хидеры запроса как аттачмент в текущий шаг.
    Аккуратно! Для избежания проблем с безопасностью, если к вашим отчетам есть доступ у многих людей, здесь следует добавить выпиливание из списка хидеров авторизацию и все кастомные авторизационные токены.
private static void AddRequestHeadersToReport(
    IRestRequest request)
{
    var listOfHeaders = request.Parameters.Where(p => p.Type == ParameterType.HttpHeader).ToList();
    if (!listOfHeaders.Any()) return;
    var formattedHeaders = listOfHeaders.ToDictionary(p => p.Name, p => p.Value);
    var json = JsonConvert.SerializeObject(formattedHeaders, Formatting.Indented,
        new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
    AllureLifecycle.Instance.AddAttachment("Headers", AllureLifecycle.AttachFormat.Json, json);
}
  1. Тоже самое с хидерами ответа:
private static void AddResponseHeadersToReport(IRestResponse response)
{
    var headersAnon = response.Headers.ToDictionary(p => p.Name, p => p.Value);
    var headers = JsonConvert.SerializeObject(headersAnon, Formatting.Indented,
        new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
    AllureLifecycle.Instance.AddAttachment("Headers", AllureLifecycle.AttachFormat.Json, headers);
}
  1. Общая инфа об ответе:
private static void AddResponseInfoToReport(IRestResponse response)
        {
            var info = new
            {
                response.StatusCode,
                StatusDesciption = response.StatusDescription,
                Encoding = response.ContentEncoding,
                response.ErrorMessage,
                response.IsSuccessful,
                response.ResponseUri,
                response.ContentType,
                response.ContentLength,
                response.ResponseStatus
            };
            var json = JsonConvert.SerializeObject(info, Formatting.Indented);
            AllureLifecycle.Instance.AddAttachment("Response common info", AllureLifecycle.AttachFormat.Json, json);
        }
  1. Добавление тела запроса в отчет:
private static void AddRequestContentToReport(
    IRestRequest request, bool addRequestBodyToReport = true)
{
    if (!addRequestBodyToReport) return;
    var param = request.Parameters.Find(e => e.Type == ParameterType.RequestBody);
    if (param?.Value == null) return;
    var content = param.Value.ToString();
    var contentType = request.RequestFormat == DataFormat.Json
        ? AllureLifecycle.AttachFormat.Json
        : AllureLifecycle.AttachFormat.Xml;
    var fileExt = $".{contentType.ToString().ToLower()}";
    if (contentType == AllureLifecycle.AttachFormat.Json) content = JsonHelper.FormatJson(content);
    AllureLifecycle.Instance.AddAttachment($"Body ({request.RequestFormat})",
        $"application/{contentType.ToString().ToLower()}",
        Encoding.UTF8.GetBytes(content), fileExt);
}
  1. Добавление тела ответа в отчет
private static void AddResponseContentToReport(
    IRestResponse response)
{
    if (string.IsNullOrEmpty(response.Content))
    {
        AllureLifecycle.Instance.AddAttachment("Контент пустой - txt формат)",
            AllureLifecycle.AttachFormat.Txt, response.Content);
    }
    else if (string.IsNullOrEmpty(response.ContentType))
    {
        AllureLifecycle.Instance.AddAttachment("Контент распарсить не получилось - txt формат)",
            AllureLifecycle.AttachFormat.Txt, response.Content);
    }
    else
    {
        var contentType = AllureLifecycle.AttachFormat.Txt;
        if (response.ContentType.ToLower().Contains("html"))
        {
            var bytes = Encoding.UTF8.GetBytes(response.Content);
            AllureLifecycle.Instance.AddAttachment("Formatted content", "text/html", bytes, ".html");
        }
        else
        {
            if (response.ContentType.ToLower().Contains("json"))
                contentType = AllureLifecycle.AttachFormat.Json;
            if (response.ContentType.ToLower().Contains("xml"))
                contentType = AllureLifecycle.AttachFormat.Xml;
            AllureLifecycle.Instance.AddAttachment("Formatted content", contentType, response.Content);
        }
    }

ПО поводу третьего вопроса - нужны подробности.

1 лайк

Спасибо еще раз! Вот еще один вопрос, может не совсем в тему, но касается репортов.
А связан вопрос с дата провайдерами, в общем если в дата провайдер засунуть какой нить объект, то в обозревателе тестов будет виден только один тест, только обозреватель решарпера показывает нормально, собственно и в отчете будет только одна строка, что не очень удобно.

В отчёте необходимо, чтобы название теста различалось. Иначе оно будет как retry, если не ошибаюсь.
Для этого надо чтобы дата провайдер предоставлял разное имя тестам. Либо у тестов были аргументы с разными значениями.

В NUnit самый тру вэй всегда тест-кейсы пилить через TestCaseData (по моему скромному мнению).

Простой пример:

public static IEnumerable<TestCaseData> GetBadLoginsAndPassword()
        {
            var data = new List<(string login, string password)>
            {
                ("EnglishBad", "EnglishBad"),
                ("Русскийочень", "Русскиййй")
            };
            foreach (var (login, password) in data)
            {
                var testCase = new TestCaseData(login, password);
                testCase.SetName("Попытка входа с неправильными данными");
                yield return testCase;
            }
        }

Тут в примере можно наблюдать, что каждому тесту проставляется одно и тоже имя.
В таком случае в Visual Studio Test Explorer такой тест будет показывать как один, но в allure (во всяком случае в моём пакете) они будут разные, т.к. в название теста в отчете автоматически добавляются параметры (в данном случае логин и пароль).
Естественно, что можно хоть из БД данные тянуть, хоть откуда-то ещё, либо генерить в апи и возвращать, тут как душе угодно.

Но я на своём опыте убедился в том, что генерить огромное количество данных используя БД/апи плохо. Просто из-за того, что это медленно, и при каждом запуске дебага какого-то теста, всё это добро каждый раз отрабатывает (тут наверное это минус NUnit, либо можно как-то избежать этого, я не стал вдаваться в то время в такие тонкости - т.е. NUnit при дебаге 1 теста дискаверит их все).

Спасибо, попробую в ближайшее время.

Привет! Использую версию Noksa.NUnit.Allure 3.1.0

Не работает Retry атрибут NUnit

	[Test, Retry(5)]
	public void Test_retry()
	{
		var cnt = 0;
		Assert.Fail(cnt++.ToString());
	}

Падает с ексепшеном:

  Message: 
    System.ArgumentNullException : Argument value must not be null
    Parameter name: value
  Stack Trace: 
    Guard.ArgumentNotNull(Object value, String name) line 45
    PropertyBag.Set(String key, Object value) line 73
    AllureReport.StartAllureLogging()

@Noksa Можешь посмотреть пожалуйста.

1 лайк

Привет!
Спасибо за фидбэк.
Не знаю когда смогу посмотреть, месяца полтора назад я поменял место работы и у меня совсем нет времени заниматься этими библиотеками в данный момент :frowning:

Мы нашли как обойти это и написали свой кастомный ретрай.Будет время, посмотри плз. Спасибо! :slightly_smiling_face: Удачи на новом месте :+1:

1 лайк

Всем привет! Подскажите пожалуйста, как изменить название шага в Tear Down секции с имени метода на кастомную строку? И как получить uuid текущего теста?

через allure.commons

Можно поподробнее?

ну в классическом аллюре AllureLifecycle.Instance.UpdateTestCase(x => какая-то переменная = x.uuid), вроде так

как у Александра - не знаю, но скорее всего что-то вроде этого

Получить Uuid теста можно через NUnit: TestExecutionContext.CurrentContext

По поводу изменения названия секции TearDown то если мне не изменяет память сделать это сейчас нельзя.

Запускал ли кто-либо тесты на dot net core 3.1? Я запускаю тесты командой dotnet test и, если хотя бы один тест падает, получаю ошибку сборки:

 /usr/share/dotnet/sdk/3.1.402/Microsoft.TestPlatform.targets(32,5): error MSB4181: The "Microsoft.TestPlatform.Build.Tasks.VSTestTask" task returned false but did not log an error.
    Build FAILED.
    /opt/buildagent/system/dotnet/.nuget/noksa.allure.stepinjector/2.0.1/build/Noksa.Allure.StepInjector.targets(13,5): warning MSB3073: The command "
    /opt/buildagent/system/dotnet/.nuget/noksa.allure.stepinjector/2.0.1/build/Noksa.Allure.StepInjector.targets(13,5): warning MSB3073:       dotnet "/opt/buildagent/work/c2ea3faf2355f052/BrowserTests/bin\Debug\netcoreapp3.1\Noksa.Allure.StepInjector.dll" "/opt/buildagent/work/c2ea3faf2355f052/BrowserTests/bin/Debug/netcoreapp3.1/BrowserTests.dll"
    /opt/buildagent/system/dotnet/.nuget/noksa.allure.stepinjector/2.0.1/build/Noksa.Allure.StepInjector.targets(13,5): warning MSB3073:     " exited with code 1.
    /opt/buildagent/system/dotnet/.nuget/noksa.allure.stepinjector/2.0.1/build/Noksa.Allure.StepInjector.targets(13,5): warning MSB4181: The "Exec" task returned false but did not log an error.

        2 Warning(s)
        1 Error(s)

    Time Elapsed 00:00:27.74
    
Build finished

    /usr/share/dotnet/sdk/3.1.402/Microsoft.TestPlatform.targets(32,5): error MSB4181: The "Microsoft.TestPlatform.Build.Tasks.VSTestTask" task returned false but did not log an error.

При этом результаты тестов генерируются как при наличии ошибок, та и при их отсутствии. Но проблема в том, что ломается цепь сборки в Teamcity.

Вроде разобрался в чем причина, но не знаю, как закостылить, так как не работал раньше с msbuild

Заменил в файле “Noksa.Allure.StepInjector.targets” эту директиву

<_Command
      Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' OR '$(TargetFrameworkIdentifier)' == '.NETStandard'">
      dotnet "$(ProjectDir)$(OutDir)Noksa.Allure.StepInjector.dll" "$(TargetPath)"
    </_Command>

на эти две

<_Command Condition="'$(OS)' == 'Windows_NT' AND ( '$(TargetFrameworkIdentifier)' == '.NETCoreApp' OR '$(TargetFrameworkIdentifier)' == '.NETStandard' )">
            dotnet "$(ProjectDir)$(OutDir)Noksa.Allure.StepInjector.dll" "$(TargetPath)"
        </_Command>
        <_Command Condition="'$(OS)' != 'Windows_NT' AND ( '$(TargetFrameworkIdentifier)' == '.NETCoreApp' OR '$(TargetFrameworkIdentifier)' == '.NETStandard' )">
            dotnet "$(ProjectDir.Replace('\', '/'))$(OutDir.Replace('\', '/'))Noksa.Allure.StepInjector.dll" "$(TargetPath.Replace('\', '/'))"
        </_Command>

Проверка на MacOS прошла. Сделал бы pull request, но не нашел репозитория StepInjector :frowning:

К сожалению, теперь другая проблема, при вызове метода AllureLifecycle.Instance.AddAttachment возникает недорабатываемое исключение без стек-трейса:

System.InvalidProgramException : Common Language Runtime detected an invalid program.

При этом, если не отрабатывал тот шаг сборки с вызовом dotnet Noksa.Allure.StepInjector.dll $(TargetPath), то метод отрабатывал корректно, но не работали как минимум атрибуты AllureStep