diff --git a/MyNUnit/AttributesMyNunit/After.cs b/MyNUnit/AttributesMyNunit/After.cs
new file mode 100644
index 0000000..d837b06
--- /dev/null
+++ b/MyNUnit/AttributesMyNunit/After.cs
@@ -0,0 +1,11 @@
+namespace AttributesMyNUnit;
+
+using System;
+
+///
+/// Атрибут, которым обозначают методы, которые вызываются после каждого теста
+///
+[AttributeUsage(AttributeTargets.Method)]
+public class After : Attribute
+{
+}
\ No newline at end of file
diff --git a/MyNUnit/AttributesMyNunit/AfterClass.cs b/MyNUnit/AttributesMyNunit/AfterClass.cs
new file mode 100644
index 0000000..227ba29
--- /dev/null
+++ b/MyNUnit/AttributesMyNunit/AfterClass.cs
@@ -0,0 +1,11 @@
+namespace AttributesMyNUnit;
+
+using System;
+
+///
+/// Атрибут, которым обозначают методы, которые вызываются после тестов
+///
+[AttributeUsage(AttributeTargets.Method)]
+public class AfterClass : Attribute
+{
+}
\ No newline at end of file
diff --git a/MyNUnit/AttributesMyNunit/AttributesMyNunit.csproj b/MyNUnit/AttributesMyNunit/AttributesMyNunit.csproj
new file mode 100644
index 0000000..8bf737c
--- /dev/null
+++ b/MyNUnit/AttributesMyNunit/AttributesMyNunit.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net6.0
+ enable
+ enable
+ Attributes
+
+
+
diff --git a/MyNUnit/AttributesMyNunit/Before.cs b/MyNUnit/AttributesMyNunit/Before.cs
new file mode 100644
index 0000000..837c3a5
--- /dev/null
+++ b/MyNUnit/AttributesMyNunit/Before.cs
@@ -0,0 +1,11 @@
+namespace AttributesMyNUnit;
+
+using System;
+
+///
+/// Атрибут, которым обозначают методы, которые вызываются перед каждым тестом
+///
+[AttributeUsage(AttributeTargets.Method)]
+public class Before : Attribute
+{
+}
\ No newline at end of file
diff --git a/MyNUnit/AttributesMyNunit/BeforeClass.cs b/MyNUnit/AttributesMyNunit/BeforeClass.cs
new file mode 100644
index 0000000..67c202a
--- /dev/null
+++ b/MyNUnit/AttributesMyNunit/BeforeClass.cs
@@ -0,0 +1,11 @@
+namespace AttributesMyNUnit;
+
+using System;
+
+///
+/// Атрибут, которым обозначают методы, которые вызываются перед тестами
+///
+[AttributeUsage(AttributeTargets.Method)]
+public class BeforeClass : Attribute
+{
+}
\ No newline at end of file
diff --git a/MyNUnit/AttributesMyNunit/Test.cs b/MyNUnit/AttributesMyNunit/Test.cs
new file mode 100644
index 0000000..a009520
--- /dev/null
+++ b/MyNUnit/AttributesMyNunit/Test.cs
@@ -0,0 +1,20 @@
+namespace AttributesMyNUnit;
+
+using System;
+
+///
+/// Атрибут, которым обозначают методы, которые являются тестами
+///
+[AttributeUsage(AttributeTargets.Method)]
+public class Test : Attribute
+{
+ public Type Expected { get; set; }
+
+ public string? Ignore { get; set; }
+
+ public Test(Type expected, string? ignore = null)
+ {
+ Expected = expected;
+ Ignore = ignore;
+ }
+}
\ No newline at end of file
diff --git a/MyNUnit/ForTests/ForTests.csproj b/MyNUnit/ForTests/ForTests.csproj
new file mode 100644
index 0000000..f532cbb
--- /dev/null
+++ b/MyNUnit/ForTests/ForTests.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0
+ enable
+ enable
+ 10
+ ClassLibrary1
+
+
+
+
+
+
+
diff --git a/MyNUnit/ForTests/Test1.cs b/MyNUnit/ForTests/Test1.cs
new file mode 100644
index 0000000..4a666c2
--- /dev/null
+++ b/MyNUnit/ForTests/Test1.cs
@@ -0,0 +1,77 @@
+namespace ForTests;
+
+using System;
+using AttributesMyNUnit;
+
+public class Test1
+{
+ private int nonStaticCounter = 0;
+
+ [Before]
+ public void NonStaticIncrement() => nonStaticCounter++;
+
+ [Test(null)]
+ public void TestWithoutExpected()
+ {
+
+ }
+
+ [Test(null, "yes")]
+ public void TestShouldBeIgnored()
+ {
+
+ }
+
+ [Test(typeof(ArgumentException))]
+ public void TestWithExpectedException()
+ {
+ throw new ArgumentException();
+ }
+
+
+ [Test(null)]
+ public void TestBefore()
+ {
+ if (nonStaticCounter != 1)
+ {
+ throw new ArgumentException();
+ }
+ }
+
+ [Test(null)]
+ public void NullExpectedButThrowException()
+ {
+ throw new ArgumentException();
+ }
+
+ [Test(typeof(ArgumentException))]
+ public void ExceptionExpectedButWasNull()
+ {
+
+ }
+
+ [Test(typeof(ArgumentException))]
+ public void OneExceptionExpectedButWasAnother()
+ {
+ throw new AggregateException();
+ }
+
+ [BeforeClass]
+ public void NonStaticBeforeClass()
+ {
+
+ }
+
+ [AfterClass]
+ public static void ExceptionInAfterClass()
+ {
+ throw new AggregateException();
+ }
+
+ [After]
+ [Test(null)]
+ public void TestWithIncompatibleAttributes()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/MyNUnit/MyNUnit.Test/MyNUnit.Test.cs b/MyNUnit/MyNUnit.Test/MyNUnit.Test.cs
new file mode 100644
index 0000000..eb5015d
--- /dev/null
+++ b/MyNUnit/MyNUnit.Test/MyNUnit.Test.cs
@@ -0,0 +1,33 @@
+namespace MyNUnit.Test;
+
+using System.Linq;
+using NUnit.Framework;
+
+public class Tests
+{
+ private readonly MyNUnit myNUnit = new ();
+ private readonly string[] answer = new string[]
+ {
+ "Проверка тестов из Test1",
+ "Метод NonStaticBeforeClass содержит атрибут BeforeClass, но не является статическим",
+ "Метод TestWithIncompatibleAttributes имеет два несовместимых атрибута",
+ "В методе ExceptionInAfterClass возникло исключение: System.AggregateException",
+ "Тест TestWithExpectedException пройден успешно",
+ "Тест TestShouldBeIgnored был игнорирован",
+ "Тест TestWithoutExpected пройден успешно",
+ "Тест TestBefore пройден успешно",
+ "Тест ExceptionExpectedButWasNull не пройден: ожидалось исключение типа System.ArgumentException",
+ "Тест OneExceptionExpectedButWasAnother не пройден: ожидалось исключение типа System.ArgumentException, возникло System.AggregateException",
+ "Тест NullExpectedButThrowException не пройден: возникло исключение System.ArgumentException"
+ };
+
+ [NUnit.Framework.Test]
+ public void TestForMessages()
+ {
+ var result = myNUnit.RunTests("../../../../ForTests/bin/Debug/net6.0/");
+ foreach (var m in result)
+ {
+ Assert.IsTrue(answer.Contains(m.Item1));
+ }
+ }
+}
\ No newline at end of file
diff --git a/MyNUnit/MyNUnit.Test/MyNUnit.Test.csproj b/MyNUnit/MyNUnit.Test/MyNUnit.Test.csproj
new file mode 100644
index 0000000..6cd73e0
--- /dev/null
+++ b/MyNUnit/MyNUnit.Test/MyNUnit.Test.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net6.0
+ enable
+
+ false
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MyNUnit/MyNUnit.sln b/MyNUnit/MyNUnit.sln
new file mode 100644
index 0000000..f460980
--- /dev/null
+++ b/MyNUnit/MyNUnit.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyNUnit", "MyNUnit\MyNUnit.csproj", "{1DB5464C-6E66-40C9-87A3-6511DD33C324}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyNUnit.Test", "MyNUnit.Test\MyNUnit.Test.csproj", "{E9C716B6-D491-4B60-BA36-063C4902B355}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ForTests", "ForTests\ForTests.csproj", "{05442C5E-59D8-4BC3-805B-9FF732926D2B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributesMyNunit", "AttributesMyNunit\AttributesMyNunit.csproj", "{9EE0AFA0-1759-431B-9810-E49A29F36499}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1DB5464C-6E66-40C9-87A3-6511DD33C324}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1DB5464C-6E66-40C9-87A3-6511DD33C324}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1DB5464C-6E66-40C9-87A3-6511DD33C324}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1DB5464C-6E66-40C9-87A3-6511DD33C324}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E9C716B6-D491-4B60-BA36-063C4902B355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E9C716B6-D491-4B60-BA36-063C4902B355}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E9C716B6-D491-4B60-BA36-063C4902B355}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E9C716B6-D491-4B60-BA36-063C4902B355}.Release|Any CPU.Build.0 = Release|Any CPU
+ {05442C5E-59D8-4BC3-805B-9FF732926D2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {05442C5E-59D8-4BC3-805B-9FF732926D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {05442C5E-59D8-4BC3-805B-9FF732926D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {05442C5E-59D8-4BC3-805B-9FF732926D2B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9EE0AFA0-1759-431B-9810-E49A29F36499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9EE0AFA0-1759-431B-9810-E49A29F36499}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9EE0AFA0-1759-431B-9810-E49A29F36499}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9EE0AFA0-1759-431B-9810-E49A29F36499}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/MyNUnit/MyNUnit/MyNUnit.cs b/MyNUnit/MyNUnit/MyNUnit.cs
new file mode 100644
index 0000000..996e784
--- /dev/null
+++ b/MyNUnit/MyNUnit/MyNUnit.cs
@@ -0,0 +1,206 @@
+namespace MyNUnit;
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using AttributesMyNUnit;
+
+///
+/// Класс MyNUnit для запуска и проверки тестов
+///
+public class MyNUnit
+{
+ private ConcurrentBag<(string, string)> messages;
+
+ ///
+ /// запуск тестов из .dll
+ ///
+ /// путь до .dll
+ /// Кортеж из (названия теста, результат о нем)
+ public (string, string)[] RunTests(string path)
+ {
+ var dlls = Directory.GetFiles(path, "*.dll");
+ var tasks = new Task>[dlls.Length];
+ messages = new();
+ Parallel.ForEach(dlls, dll => DoTestFromDll(dll));
+ return messages.ToArray();
+ }
+
+ private void DoTestFromDll(string dllPath)
+ {
+ var dll = Assembly.LoadFrom(dllPath);
+ var classes = dll.ExportedTypes.Where(t => t.IsClass);
+ Parallel.ForEach(classes, c => DoWorkInClass(c));
+ }
+
+ private void DoWorkInClass(Type classFromDll)
+ {
+ var testAttributes = new TestAttributes();
+ var methods = classFromDll.GetMethods();
+ Parallel.ForEach(methods, method => GetAttributesAndDoBeforeAndAfterClass(method, testAttributes));
+ RunMethodsWithAttributes(testAttributes.BeforeClass, null);
+
+ if (testAttributes.Tests.Count < 1)
+ {
+ return;
+ }
+ messages.Add(($"Проверка тестов из {classFromDll.Name}", ""));
+
+ Parallel.ForEach(testAttributes.Tests, test => DoTest(test, testAttributes, classFromDll));
+ RunMethodsWithAttributes(testAttributes.AfterClass, null);
+ }
+
+ private void DoTest(MethodInfo test, TestAttributes testAttributes, Type classDll)
+ {
+ var attribute = (Test)Attribute.GetCustomAttribute(test, typeof(Test));
+ if (attribute.Ignore != null)
+ {
+ messages.Add(($"Тест {test.Name} был игнорирован", ""));
+ return;
+ }
+ object classInstance = Activator.CreateInstance(classDll);
+ RunMethodsWithAttributes(testAttributes.Before, classInstance);
+ var message = ("", "");
+ var watch = new Stopwatch();
+ watch.Start();
+ try
+ {
+ test.Invoke(classInstance, null);
+ }
+ catch (Exception exception)
+ {
+ watch.Stop();
+ if (attribute.Expected == null)
+ {
+ message = ($"Тест {test.Name} не пройден: возникло исключение {exception.InnerException.GetType()}", $"Время: {watch.ElapsedMilliseconds} ms");
+ }
+ else if (exception.InnerException.GetType() != attribute.Expected)
+ {
+ message = ($"Тест {test.Name} не пройден: ожидалось исключение типа {attribute.Expected}, возникло {exception.InnerException.GetType()}", $"Время: {watch.ElapsedMilliseconds} ms");
+ }
+ else
+ {
+ message = ($"Тест {test.Name} пройден успешно", $"Время: {watch.ElapsedMilliseconds} ms");
+ }
+ }
+ finally
+ {
+ watch.Stop();
+ if (message.Item1 == "")
+ {
+ if (attribute.Expected == null)
+ {
+ message = ($"Тест {test.Name} пройден успешно", $"Время: {watch.ElapsedMilliseconds} ms");
+ }
+ else
+ {
+ message = ($"Тест {test.Name} не пройден: ожидалось исключение типа {attribute.Expected}", $"Время: {watch.ElapsedMilliseconds} ms");
+ }
+ }
+ }
+ messages.Add(message);
+
+ RunMethodsWithAttributes(testAttributes.After, classInstance);
+ }
+
+ private void AddMethod(Attribute[] attributes, MethodInfo method, List list)
+ {
+ if (attributes.Length != 0)
+ {
+ list.Add(method);
+ }
+ }
+
+ private void RunMethodsWithAttributes(List methods, object classIntstase)
+ {
+ foreach (MethodInfo method in methods)
+ {
+ try
+ {
+ method.Invoke(classIntstase, null);
+ }
+ catch (Exception e)
+ {
+ messages.Add(($"В методе {method.Name} возникло исключение: {e.InnerException.GetType()}", ""));
+ }
+ }
+ }
+
+ private Attribute[][] GetCountAttributes(MethodInfo method)
+ {
+ var types = new Type[] { typeof(Before), typeof(BeforeClass), typeof(Test), typeof(AfterClass), typeof(After)};
+ var typesCount = new Attribute[5][];
+ for (int i = 0; i < types.Length; i++)
+ {
+ typesCount[i] = Attribute.GetCustomAttributes(method).Where(t => t.GetType() == types[i]).ToArray();
+ }
+ return typesCount;
+ }
+
+ private int GetSumOfAttributesInMethod(Attribute[][] array)
+ {
+ var count = 0;
+ for (int i = 0;i < array.Length;i++)
+ {
+ count += array[i].Length;
+ }
+ return count;
+ }
+
+ private void GetAttributesAndDoBeforeAndAfterClass(MethodInfo method, TestAttributes testAttributes)
+ {
+ var countOfAttributes = GetCountAttributes(method);
+ if (GetSumOfAttributesInMethod(countOfAttributes) > 1)
+ {
+ messages.Add(($"Метод {method.Name} имеет два несовместимых атрибута", ""));
+ return;
+ }
+ AddMethod(countOfAttributes[0], method, testAttributes.Before);
+ AddMethod(countOfAttributes[2], method, testAttributes.Tests);
+ AddMethod(countOfAttributes[4], method, testAttributes.After);
+ if (countOfAttributes[1].Length != 0)
+ {
+ if (method.IsStatic)
+ {
+ testAttributes.BeforeClass.Add(method);
+ }
+ else
+ {
+ messages.Add(($"Метод {method.Name} содержит атрибут BeforeClass, но не является статическим", ""));
+ }
+ }
+ if (countOfAttributes[3].Length != 0)
+ {
+ if (method.IsStatic)
+ {
+ testAttributes.AfterClass.Add(method);
+ }
+ else
+ {
+ messages.Add(($"Метод {method.Name} содержит атрибут AfterClass, но не является статическим", ""));
+ }
+ }
+ }
+ private class TestAttributes
+ {
+ public TestAttributes()
+ {
+ After = new();
+ Before = new();
+ BeforeClass = new();
+ AfterClass = new();
+ Tests = new();
+ }
+
+ public List After { get; set; }
+ public List Before { get; set; }
+ public List BeforeClass { get; set; }
+ public List AfterClass { get; set; }
+ public List Tests { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MyNUnit/MyNUnit/MyNUnit.csproj b/MyNUnit/MyNUnit/MyNUnit.csproj
new file mode 100644
index 0000000..5756fd0
--- /dev/null
+++ b/MyNUnit/MyNUnit/MyNUnit.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net6.0
+ Windows
+ 10
+
+
+
+
+
+
+
diff --git a/MyNUnit/MyNUnit/Program.cs b/MyNUnit/MyNUnit/Program.cs
new file mode 100644
index 0000000..8001d21
--- /dev/null
+++ b/MyNUnit/MyNUnit/Program.cs
@@ -0,0 +1,33 @@
+namespace MyNUnit;
+
+using System;
+using System.IO;
+
+class Program
+{
+ static void Main(string[] args)
+ {
+ if (args.Length == 0)
+ {
+ Console.WriteLine("Путь к папке не указан");
+ return;
+ }
+
+ if (Directory.Exists(args[0]))
+ {
+ Console.WriteLine("По данному пути ниичего не найдено.");
+ return;
+ }
+
+ var myNUnit = new MyNUnit();
+ var result = myNUnit.RunTests(args[0]);
+ foreach (var test in result)
+ {
+ Console.WriteLine(test.Item1);
+ if (test.Item2 != null)
+ {
+ Console.WriteLine(test.Item2);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 9809100..ec8ac0d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,5 @@
+image: Visual Studio 2022
+
build_script:
- For /R %%I in (*.sln) do dotnet test %%I
test: of
\ No newline at end of file