{"id":2365,"date":"2023-11-22T10:10:54","date_gmt":"2023-11-22T10:10:54","guid":{"rendered":"https:\/\/wp-dev.speednet.pl\/?p=2365"},"modified":"2024-01-05T09:44:12","modified_gmt":"2024-01-05T09:44:12","slug":"xunit","status":"publish","type":"post","link":"https:\/\/speednetsoftware.com\/xunit\/","title":{"rendered":"xUnit: using non-public types as test cases for parameterized tests"},"content":{"rendered":"\n<p>One of the most useful features of&nbsp;<a href=\"https:\/\/xunit.net\/\" rel=\"nofollow\">xUnit<\/a>&nbsp;test framework is the&nbsp;<code>[Theory]<\/code>&nbsp;attribute, which allows us to write parameterized tests. This way, we can inject multiple test cases into a single test method and test our&nbsp;<strong>SUT<\/strong>&nbsp;(<em>system under test<\/em>) with a variety of test data representing multiple use cases &#8211; both typical and edge-cases. There are plenty of tutorials on various ways to inject test cases into a&nbsp;<code>Theory<\/code>, but I haven\u2019t yet found one on how to use test cases that use data marked with&nbsp;<code>internal<\/code>&nbsp;or other non-public access modifiers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The problem<\/strong>\u200b<\/h2>\n\n\n\n<p>Let\u2019s consider the following scenario: we need to consume some input data (e.g. coming from an external API) and translate it into some internal representation optimized for doing our work. For the purpose of data encapsulation, that translated representation needs to be truly internal &#8211; that is, marked with an&nbsp;<code>internal<\/code>&nbsp;or other non-public access modifier.<\/p>\n\n\n\n<p>For the rest of this article, we\u2019ll work on the following data model:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>public record SourceData\n{\n    public string CommaSeparatedValues { get; set; }\n}\n\ninternal record TranslatedData\n{\n    public IReadOnlyCollection&lt;string> Values { get; set; }\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>And our&nbsp;<strong>SUT<\/strong>&nbsp;(<em>system under test<\/em>) will look like this:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>internal class Translator\n{\n    internal TranslatedData Translate(SourceData source)\n    {\n        \/\/ doing some complicated work here\n        return new TranslatedData { Values = source.CommaSeparatedValues.Split(\",\") };\n    }\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>Of course we need to mark the assembly containing the&nbsp;<code>TranslatedData<\/code>&nbsp;and&nbsp;<code>Translator<\/code>&nbsp;classes with&nbsp;<code>[assembly: InternalsVisibleTo(\"MyTestProject\")]<\/code>&nbsp;attribute, so that our tests are able to discover and call the&nbsp;<code>Translate()<\/code>&nbsp;method. There are multiple tutorials on how to do it, so I won\u2019t cover it here.<\/p>\n\n\n\n<p>As for injecting test cases into our test method, xUnit gives us three useful attributes:&nbsp;<code>InlineData<\/code>,&nbsp;<code>MemberData<\/code>&nbsp;and&nbsp;<code>ClassData<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u200b<strong>Attempt 1: InlineData<\/strong><\/h2>\n\n\n\n<p>The most straightforward way to inject test cases into a&nbsp;<code>Theory<\/code>&nbsp;is to inject them directly with an&nbsp;<code>[InlineData]<\/code>&nbsp;attribute. This works well with simple CLR types, like&nbsp;<code>int<\/code>&nbsp;or&nbsp;<code>string<\/code>:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>&#91;Theory]\n&#91;InlineData(\"foo\", \"bar\", \"foobar\")]\ninternal void Test_concatenation_using_InlineData(string first, string second, string expected)\n{\n    var actual = string.Concat(first, second);\n    Assert.Equal(expected, actual);\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>However, the compiler doesn\u2019t allow us to use complex types with&nbsp;<code>InlineData<\/code>, as that attribute\u2019s parameters all need to be constant values. So, the following code produces a compiler error&nbsp;<code>CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type<\/code>:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>&#91;Theory]\n&#91;InlineData(\n    new SourceData { CommaSeparatedValues = \"foo\" },\n    new TranslatedData { Values = new&#91;] { \"foo\" } }\n)] \/\/ error CS0182\n&#91;InlineData(\n    new SourceData { CommaSeparatedValues = \"foo,bar\" },\n    new TranslatedData { Values = new&#91;] { \"foo\", \"bar\" } }\n)] \/\/ error CS0182\n&#91;InlineData(\n    new SourceData { CommaSeparatedValues = \"foo,bar,baz\" },\n    new TranslatedData { Values = new&#91;] { \"foo\", \"bar\", \"baz\" } }\n)] \/\/ error CS0182\ninternal void Test_using_InlineData(SourceData source, TranslatedData expected)\n{\n    var actual = new Translator().Translate(source);\n    Assert.Equivalent(expected, actual);\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>So using&nbsp;<code>InlineData<\/code>&nbsp;is unfortunately out of the picture.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Attempt 2: MemberData<\/strong>\u200b<\/h2>\n\n\n\n<p>Another way is to use&nbsp;<code>[MemberData]<\/code>&nbsp;attribute. With this attribute, we need to specify a&nbsp;<code>public static<\/code>&nbsp;property or method returning&nbsp;<code>IEnumerable&lt;object[]&gt;<\/code>&nbsp;&#8211; a collection of arrays, with each array representing arguments for a single test case:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>&#91;Theory]\n&#91;MemberData(nameof(TestCases_MemberData))]\npublic void Test_using_MemberData(SourceData source, TranslatedData expected)\n{\n    var actual = new Translator().Translate(source);\n    Assert.Equivalent(expected, actual);\n}\n\npublic static IEnumerable&lt;object&#91;]> TheoryTestCases_MemberData()\n{\n    return new List&lt;object&#91;]>()\n    {\n        new object&#91;] {\n            new SourceData { CommaSeparatedValues = \"foo\" },\n            new TranslatedData { Values = new&#91;] {\"foo\" } }\n        },\n        new object&#91;] {\n            new SourceData { CommaSeparatedValues = \"foo,bar\" },\n            new TranslatedData { Values = new&#91;] {\"foo\", \"bar\" } }\n        },\n        new object&#91;] {\n            new SourceData { CommaSeparatedValues = \"foo,bar,baz\" },\n            new TranslatedData { Values = new&#91;] {\"foo\", \"bar\", \"baz\" } }\n        }\n    };\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>This time the compiler gives us the following error:&nbsp;<code>CS0051: Inconsistent accessibility: parameter type 'TranslatedData' is less accessible than method 'Test_using_MemberData(SourceData, TranslatedData)'<\/code>.<\/p>\n\n\n\n<p>Fortunately, the error is pretty simple to fix:&nbsp;<strong>xUnit allows the test methods to be not only&nbsp;<code>public<\/code>, but also&nbsp;<code>internal<\/code>,&nbsp;<code>protected<\/code>&nbsp;and even&nbsp;<code>private<\/code><\/strong>, so we just have modify our test method to use&nbsp;<code>internal<\/code>&nbsp;access modifier.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u200b<strong>Attempt 3: ClassData<\/strong><\/h2>\n\n\n\n<p>Finally we have the&nbsp;<code>[ClassData]<\/code>&nbsp;attribute. With this attribute, wee need to specify a class that implements&nbsp;<code>IEnumerable&lt;object[]&gt;<\/code>&nbsp;interface and returns our test cases as a list of arrays of&nbsp;<code>object<\/code>. The simplest way to achieve this is to create a&nbsp;<code>List&lt;object[]&gt;<\/code>&nbsp;containing our test cases and defer the&nbsp;<code>IEnumerable<\/code>&nbsp;implementation to that list.<\/p>\n\n\n\n<p>As we learned from the previous point, our test method needs to be&nbsp;<code>internal<\/code>. Additionally, the test data class also needs to be&nbsp;<code>internal<\/code>.<\/p>\n\n\n\n<p>Let\u2019s see it in action:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>&#91;Theory]\n&#91;ClassData(typeof(TestCases_ClassData))]\ninternal void Test_using_ClassData(SourceData source, TranslatedData expected)\n{\n    var actual = new Translator().Translate(source);\n    Assert.Equivalent(expected, actual);\n}\n\n\ninternal class TestCases_ClassData : IEnumerable&lt;object&#91;]>\n{\n    private readonly List&lt;object&#91;]> _testCases = new List&lt;object&#91;]>\n    {\n        new object&#91;]\n        {\n            new SourceData { CommaSeparatedValues = \"foo\" },\n            new TranslatedData { Values = new&#91;] { \"foo\" } }\n        },\n        new object&#91;]\n        {\n            new SourceData { CommaSeparatedValues = \"foo,bar\" },\n            new TranslatedData { Values = new&#91;] { \"foo\", \"bar\" } }\n        },\n        new object&#91;]\n        {\n            new SourceData { CommaSeparatedValues = \"foo,bar,baz\" },\n            new TranslatedData { Values = new&#91;] { \"foo\", \"bar\", \"baz\" } }\n        }\n    };\n\n    public IEnumerator&lt;object&#91;]> GetEnumerator() => _testCases.GetEnumerator();\n    IEnumerator IEnumerable.GetEnumerator() => _testCases.GetEnumerator();\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Some problems with previous attempts<\/strong><\/h2>\n\n\n\n<p>In the&nbsp;<strong>attempts #2 and #3<\/strong>&nbsp;we finally managed to write code that allows our tests to run and report meaningful results, but there are still problems with that approach.<\/p>\n\n\n\n<p>First of all, in the case of&nbsp;<code>ClassData<\/code>&nbsp;we need to write a bunch of boilerplate code &#8211; specifically, we need to manually implement&nbsp;<code>IEnumerable<\/code>&nbsp;interface to point to our list of test cases. This makes writing test cases less pleasant and intuitive than it could be, as we have to remember to use the proper boilerplate &#8211; which more often than not leads to searching through StackOverflow to remind us of how exactly to write the test data class.<\/p>\n\n\n\n<p>Our test cases, whether implemented via&nbsp;<code>MemberData<\/code>&nbsp;or&nbsp;<code>ClassData<\/code>, also lack type safety: as they are simply arrays of&nbsp;<code>object<\/code>s, the compiler doesn\u2019t check for things like whether the array length matches the number of parameters for our test method, or whether the types of objects in the array match the types of parameters of our test method.<\/p>\n\n\n\n<p>So if for some reason we had to change either to singature of our test method or the contents of our test cases, we may end up with the following code:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>&#91;Theory]\n&#91;ClassData(typeof(TestCases_ClassDataWithTypeSafetyErrors))]\ninternal void Test_using_ClassDataWithTypeSafetyErrors(SourceData source, TranslatedData expectation)\n{\n    var translated = new Translator().Translate(source);\n    Assert.Equivalent(expectation, translated);\n}\n\npublic static IEnumerable&lt;object&#91;]> TheoryTestCases_MemberData()\n{\n    return new List&lt;object&#91;]>()\n    {\n        new object&#91;] {\n            new string(\"foo\"),\n            new string(\"foo\")\n        },\n        new object&#91;] {\n            new string(\"foo,bar\"),\n            new string(\"foo\"),\n            new string(\"bar\")\n        },\n        new object&#91;] {\n            new string(\"foo,bar,baz\"),\n            new string(\"foo\"),\n            new string(\"bar\"),\n            new string(\"baz\")\n        }\n    };\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>The above code will compile without any errors or warnings, even from xUnit\u2019s own code analyzer, but it will crash when we try to run our tests:&nbsp;<code>Object of type 'System.String' cannot be converted to type 'SourceData'.<\/code>&nbsp;So, can we do better?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u200b\u200b<strong>Attempt <em>#4<\/em>: TheoryData<\/strong>\u200b<\/h2>\n\n\n\n<p>The answer is: yes, we can. xUnit gives us a generic&nbsp;<code>TheoryData&lt;&gt;<\/code>&nbsp;class to use in conjunction with either&nbsp;<code>MemberData<\/code>&nbsp;or&nbsp;<code>ClassData<\/code>&nbsp;attribute.&nbsp;<code>TheoryData&lt;&gt;<\/code>&nbsp;is just a type-safe wrapper around&nbsp;<code>IEnumerable&lt;object[]&gt;<\/code>&nbsp;with all the required boilerplate already written for us. Not only does this solution reduce boilerplate and thus automate much of our work, but is also enforces type-safety. All we need to do is to implement the parameterless constructor and declare our test cases there using the built-in&nbsp;<code>Add()<\/code>&nbsp;method.<\/p>\n\n\n\n<p>Again, let\u2019s see it in action:<\/p>\n\n\n<div class=\"language-csharp\">\n    \n\n<pre class=\"wp-block-code\"><code>&#91;Theory]\n&#91;ClassData(typeof(TestCases_TheoryData))]\ninternal void Test_using_TheoryData(SourceData source, TranslatedData expectation)\n{\n    var translated = new Translator().Translate(source);\n    Assert.Equivalent(expectation, translated);\n}\n\n\ninternal class TestCases_TheoryData : TheoryData&lt;SourceData, TranslatedData>\n{\n    public TestCases_TheoryData()\n    {\n        Add(\n            new SourceData { CommaSeparatedValues = \"foo\" },\n            new TranslatedData { Values = new&#91;] { \"foo\" } }\n        );\n        Add(\n            new SourceData { CommaSeparatedValues = \"foo,bar\" },\n            new TranslatedData { Values = new&#91;] { \"foo\", \"bar\" } }\n        );\n        Add(\n            new SourceData { CommaSeparatedValues = \"foo,bar,baz\" },\n            new TranslatedData { Values = new&#91;] { \"foo\", \"bar\", \"baz\" } }\n        );\n    }\n}<\/code><\/pre>\n\n\n<\/div>\n\n\n\n\n<p>And now we have finally checked all of our boxes: we are able to use non-public types as input for our test cases, the code compiles without errors or warnings, our tests report appropriate results, and also we\u2019ve reduced the amount of boilerplate code to a minimum and have the benefit of compiler-enforced type safety.<\/p>\n\n\n\n<p>Note that&nbsp;<code>TheoryData&lt;&gt;<\/code>&nbsp;can generally be used as a target for both&nbsp;<code>MemberData<\/code>&nbsp;and&nbsp;<code>ClassData<\/code>&nbsp;attributes; however in our case we need to use&nbsp;<code>ClassData<\/code>, as&nbsp;<code>MemberData<\/code>&nbsp;requires the test cases to be marked with&nbsp;<code>public static<\/code>&nbsp;modifiers, and our test data are non-public.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>When using xUnit\u2019s&nbsp;<em>Theories<\/em>&nbsp;to write parameterized tests that take non-public types as parameters, remember these things:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>mark your test methods as&nbsp;<code>internal<\/code>, as opposed to traditional&nbsp;<code>public<\/code>&nbsp;access modifier<\/li>\n\n\n\n<li>use either&nbsp;<code>MemberData<\/code>&nbsp;or&nbsp;<code>ClassData<\/code>&nbsp;to specify test cases\u2026<\/li>\n\n\n\n<li>\u2026 but I recommend using&nbsp;<code>ClassData<\/code>&nbsp;in conjunction with&nbsp;<code>TheoryData&lt;&gt;<\/code>&nbsp;for maximum convenience and type safety, as shown in the&nbsp;<strong>Attempt 4: TheoryData<\/strong>&nbsp;section of this article.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>One of the most useful features of&nbsp;xUnit&nbsp;test framework is the&nbsp;[Theory]&nbsp;attribute, which allows us to write parameterized tests. This way, we can inject multiple test cases into a single test method and test our&nbsp;SUT&nbsp;(system under test) with a variety of test data representing multiple use cases &#8211; both typical and edge-cases. There are plenty of tutorials [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2369,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[38656],"tags":[],"class_list":["post-2365","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-development"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>xUnit: using non-public types as test cases for parameterized tests - Speednet<\/title>\n<meta name=\"description\" content=\"One of the most useful features of [xUnit](https:\/\/xunit.net\/) test framework is the `[Theory]` attribute. Learn why.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/speednetsoftware.com\/xunit\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"xUnit: using non-public types as test cases for parameterized tests - Speednet\" \/>\n<meta property=\"og:description\" content=\"One of the most useful features of [xUnit](https:\/\/xunit.net\/) test framework is the `[Theory]` attribute. Learn why.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/speednetsoftware.com\/xunit\/\" \/>\n<meta property=\"og:site_name\" content=\"Speednet\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/speednetpl\" \/>\n<meta property=\"article:published_time\" content=\"2023-11-22T10:10:54+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-05T09:44:12+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/speednetsoftware.com\/app\/uploads\/2023\/11\/UnitTesting.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"speednet\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"speednet\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"xUnit: using non-public types as test cases for parameterized tests - Speednet","description":"One of the most useful features of [xUnit](https:\/\/xunit.net\/) test framework is the `[Theory]` attribute. Learn why.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/speednetsoftware.com\/xunit\/","og_locale":"en_US","og_type":"article","og_title":"xUnit: using non-public types as test cases for parameterized tests - Speednet","og_description":"One of the most useful features of [xUnit](https:\/\/xunit.net\/) test framework is the `[Theory]` attribute. Learn why.","og_url":"https:\/\/speednetsoftware.com\/xunit\/","og_site_name":"Speednet","article_publisher":"https:\/\/www.facebook.com\/speednetpl","article_published_time":"2023-11-22T10:10:54+00:00","article_modified_time":"2024-01-05T09:44:12+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/speednetsoftware.com\/app\/uploads\/2023\/11\/UnitTesting.png","type":"image\/png"}],"author":"speednet","twitter_card":"summary_large_image","twitter_misc":{"Written by":"speednet","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/speednetsoftware.com\/xunit\/#article","isPartOf":{"@id":"https:\/\/speednetsoftware.com\/xunit\/"},"author":{"name":"speednet","@id":"https:\/\/speednetsoftware.com\/#\/schema\/person\/6330852e072782aed899830ab00abdee"},"headline":"xUnit: using non-public types as test cases for parameterized tests","datePublished":"2023-11-22T10:10:54+00:00","dateModified":"2024-01-05T09:44:12+00:00","mainEntityOfPage":{"@id":"https:\/\/speednetsoftware.com\/xunit\/"},"wordCount":1048,"commentCount":0,"publisher":{"@id":"https:\/\/speednetsoftware.com\/#organization"},"articleSection":["Software development"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/speednetsoftware.com\/xunit\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/speednetsoftware.com\/xunit\/","url":"https:\/\/speednetsoftware.com\/xunit\/","name":"xUnit: using non-public types as test cases for parameterized tests - Speednet","isPartOf":{"@id":"https:\/\/speednetsoftware.com\/#website"},"datePublished":"2023-11-22T10:10:54+00:00","dateModified":"2024-01-05T09:44:12+00:00","description":"One of the most useful features of [xUnit](https:\/\/xunit.net\/) test framework is the `[Theory]` attribute. Learn why.","breadcrumb":{"@id":"https:\/\/speednetsoftware.com\/xunit\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/speednetsoftware.com\/xunit\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/speednetsoftware.com\/xunit\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/speednetsoftware.com\/"},{"@type":"ListItem","position":2,"name":"xUnit: using non-public types as test cases for parameterized tests"}]},{"@type":"WebSite","@id":"https:\/\/speednetsoftware.com\/#website","url":"https:\/\/speednetsoftware.com\/","name":"Speednet","description":"We build software","publisher":{"@id":"https:\/\/speednetsoftware.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/speednetsoftware.com\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/speednetsoftware.com\/#organization","name":"Speednet","alternateName":"Speednet","url":"https:\/\/speednetsoftware.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/speednetsoftware.com\/#\/schema\/logo\/image\/","url":"https:\/\/wp-dev.speednet.pl\/app\/uploads\/2015\/07\/speednetpl_logo.jpg","contentUrl":"https:\/\/wp-dev.speednet.pl\/app\/uploads\/2015\/07\/speednetpl_logo.jpg","width":200,"height":200,"caption":"Speednet"},"image":{"@id":"https:\/\/speednetsoftware.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/speednetpl","https:\/\/www.instagram.com\/speednet.pl\/"]},{"@type":"Person","@id":"https:\/\/speednetsoftware.com\/#\/schema\/person\/6330852e072782aed899830ab00abdee","name":"speednet","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/speednetsoftware.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/2b2698b89b5358336732f61e0e350b73?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2b2698b89b5358336732f61e0e350b73?s=96&d=mm&r=g","caption":"speednet"},"sameAs":["https:\/\/wp-dev.speednet.pl\/wp"]}]}},"_links":{"self":[{"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/posts\/2365"}],"collection":[{"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/comments?post=2365"}],"version-history":[{"count":21,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/posts\/2365\/revisions"}],"predecessor-version":[{"id":3821,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/posts\/2365\/revisions\/3821"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/media\/2369"}],"wp:attachment":[{"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/media?parent=2365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/categories?post=2365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/speednetsoftware.com\/wp-json\/wp\/v2\/tags?post=2365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}